From 3a9143ebefb6d1a0c1469d3b7236e811ebcddb63 Mon Sep 17 00:00:00 2001 From: cooper Date: Wed, 23 Sep 2026 05:38:07 -0700 Subject: [PATCH] chore: delete dead Nix copies and repository cruft Remove code and files that have no importers, callers or references, found in the Sep 2026 codebase review. Each item was grepped across the repo (Nix imports, string paths, docs, CI, Justfile, scripts). The public lib.mkOutputs export and the check-files-drift devshell binary were also checked against the sibling darkmatter repos; nothing uses them. Nix: - nix/internal/flake/: stale copy of nix/flake/ that nothing imports. nix/internal/devshell/scripts stays; .stack/modules imports it. - nix/stackpanel/core/options-core.nix: never imported; a drifted twin of core/options/core.nix. - core/lib/evalconfig.nix: drifted twin of the evaluator the Go CLI embeds (apps/stackpanel-go/pkg/nixeval/eval.nix). - core/lib/envvars.nix and envvars/: env-var catalog (including mkGoConstants) that nothing imports. The Go envvars package is the live registry. - core/lib/services-default.nix: never imported. - nix/flake/per-system-outputs.nix and the lib.mkOutputs export it backed: superseded by nix/flake/per-system/. The only reference was an invalid README example. - check-files-drift in files/default.nix: the Go FilesReconciler (rc_files.go) replaced it; nothing calls or documents it. - nix/flake/devshells/ (a backwards-compat stub) and nix/NOTES.md (stale notes): nothing references them. - The example-monorepo template with tests/test_monorepo.sh (which cds into a nonexistent alchemy/ dir), and the devenv template. Neither is exported by exports.nix; nix/flake/README.md already says the devenv template was removed. Repository cruft: - apps/web/.stackpanel/: state, keys and generated files under the legacy directory name. - packages/ui/img/: 26 unreferenced PNGs (about 7.6 MB). - docs/{infra,infradeploy,machines,colmena,sess-infra}.md: about 47k lines of pasted agent session transcripts (infra.md and infradeploy.md are identical). - .omo/: stop tracking it; it is already in .gitignore. - infra/sst/, sst.config.example.ts and tests/deployment/: orphaned SST configs. - .stack/config.example.nix and apps/stackpanel-go/.stack/config.nix.example: stale snapshots of the starter config. `stack config generate` and generate-config-example recreate them on demand. Update the references to deleted files: the nix/ and nix/flake/ READMEs, core/README.md and its generated core.mdx, docs/environment-variables.md (the Go package is now the only source, at its current path), and comments in profile.nix, global-outputs.nix, aws/default.nix, envvars.go and rc_files.go. Keep the docker module's images/imagesComputed options. The module README documents them with a usage example, so removing them is an option removal for users rather than a local cleanup. Refs: stackpanel-thq.12 --- .../ses_134bcae84ffef5Z1jt1PQ1tLXJ.json | 10 - .../ses_1426cb957ffeJ6H0qnnvrYuSFg.json | 10 - .stack/config.example.nix | 507 - apps/docs/content/docs/internal/core.mdx | 2 +- apps/stackpanel-go/.stack/config.nix.example | 507 - .../internal/reconcile/rc_files.go | 8 +- apps/stackpanel-go/pkg/envvars/envvars.go | 4 - .../gen/ide/vscode/devshell-loader.sh | 20 - .../gen/ide/vscode/stackpanel.code-workspace | 46 - .../schemas/secrets/app-config.schema.json | 26 - .../gen/schemas/secrets/config.schema.json | 23 - .../gen/schemas/secrets/env.schema.json | 38 - .../gen/schemas/secrets/schema.schema.json | 31 - .../gen/schemas/secrets/users.schema.json | 27 - .../.stackpanel/secrets/recipients/.sops.yaml | 11 - .../recipients/cooper-maruyama.age.pub | 1 - apps/web/.stackpanel/secrets/vars/.sops.yaml | 23 - apps/web/.stackpanel/state/keys/local.pub | 1 - apps/web/.stackpanel/state/stackpanel.json | 59 - docs/colmena.md | 8066 ---------- docs/environment-variables.md | 10 +- docs/infra.md | 9566 ------------ docs/infradeploy.md | 9566 ------------ docs/machines.md | 12490 ---------------- docs/sess-infra.md | 7702 ---------- infra/sst/apps/docs.ts | 21 - infra/sst/apps/index.ts | 4 - infra/sst/apps/web.ts | 24 - infra/sst/config.ts | 47 - infra/sst/index.ts | 17 - infra/sst/secrets.ts | 28 - infra/sst/sops-output.ts | 115 - infra/sst/sst.config.ts | 211 - nix/NOTES.md | 26 - nix/README.md | 7 +- nix/flake/README.md | 8 - nix/flake/devshells/README.md | 43 - nix/flake/devshells/default.nix | 14 - nix/flake/exports.nix | 5 - nix/flake/global-outputs.nix | 4 +- nix/flake/per-system-outputs.nix | 376 - nix/flake/templates/devenv/.envrc | 1 - .../templates/example-monorepo/.env.example | 2 - .../templates/example-monorepo/.gitignore | 4 - .../apps/analytics/alchemy.run.ts | 18 - .../apps/analytics/package.json | 17 - .../apps/analytics/src/index.ts | 11 - .../apps/analytics/tsconfig.json | 16 - .../apps/backend/alchemy.run.ts | 22 - .../apps/backend/package.json | 18 - .../apps/backend/src/index.ts | 11 - .../apps/backend/tsconfig.json | 16 - .../example-monorepo/apps/frontend/README.md | 56 - .../apps/frontend/alchemy.run.ts | 19 - .../apps/frontend/eslint.config.js | 28 - .../example-monorepo/apps/frontend/index.html | 13 - .../apps/frontend/package.json | 37 - .../apps/frontend/public/vite.svg | 1 - .../apps/frontend/src/App.css | 42 - .../apps/frontend/src/App.tsx | 35 - .../apps/frontend/src/assets/react.svg | 1 - .../apps/frontend/src/index.css | 68 - .../apps/frontend/src/main.tsx | 10 - .../apps/frontend/src/vite-env.d.ts | 1 - .../apps/frontend/src/worker.ts | 14 - .../apps/frontend/tsconfig.json | 36 - .../apps/frontend/types/env.d.ts | 16 - .../apps/frontend/vite.config.ts | 8 - nix/flake/templates/example-monorepo/bun.lock | 797 - .../templates/example-monorepo/package.json | 32 - .../templates/example-monorepo/tsconfig.json | 14 - .../templates/example-monorepo/turbo.json | 25 - nix/internal/flake/README.md | 34 - nix/internal/flake/default.nix | 463 - nix/internal/flake/devshells/README.md | 42 - nix/internal/flake/exports.nix | 153 - nix/internal/flake/packages.nix | 27 - nix/internal/flake/templates/README.md | 159 - .../flake/templates/_test-fixtures/README.md | 88 - .../_test-fixtures/basic/.stack/config.nix | 12 - .../external-module/.stack/config.nix | 15 - .../full-stack/.stack/config.nix | 57 - .../templates/_test-fixtures/update-golden.sh | 118 - .../with-oxlint/.stack/config.nix | 27 - nix/internal/flake/templates/default/.envrc | 40 - .../flake/templates/default/.gitignore | 31 - .../flake/templates/default/.stack/config.nix | 187 - .../flake/templates/default/.stack/data.nix | 10 - .../flake/templates/default/README.md | 105 - nix/internal/flake/templates/devenv/.envrc | 1 - nix/internal/flake/templates/minimal/.envrc | 2 - .../flake/templates/minimal/.gitignore | 31 - .../flake/templates/minimal/.stack/config.nix | 95 - .../flake/templates/minimal/.stack/data.nix | 10 - .../flake/templates/minimal/README.md | 108 - nix/stackpanel/core/README.md | 2 +- nix/stackpanel/core/lib/envvars.nix | 17 - nix/stackpanel/core/lib/envvars/derived.nix | 138 - nix/stackpanel/core/lib/envvars/helpers.nix | 46 - nix/stackpanel/core/lib/envvars/sections.nix | 345 - nix/stackpanel/core/lib/evalconfig.nix | 146 - nix/stackpanel/core/lib/services-default.nix | 41 - nix/stackpanel/core/options-core.nix | 441 - nix/stackpanel/devshell/profile.nix | 8 +- nix/stackpanel/files/default.nix | 77 - .../integrations/services/aws/default.nix | 3 - packages/ui/img/apps.png | Bin 118549 -> 0 bytes packages/ui/img/apps@2x.png | Bin 345098 -> 0 bytes packages/ui/img/binary-cache.png | Bin 65744 -> 0 bytes packages/ui/img/binary-cache@2x.png | Bin 189187 -> 0 bytes packages/ui/img/certs.png | Bin 91807 -> 0 bytes packages/ui/img/certs@2x.png | Bin 239163 -> 0 bytes packages/ui/img/ide-integration.png | Bin 251238 -> 0 bytes packages/ui/img/ide-integration@2x.png | Bin 696839 -> 0 bytes packages/ui/img/inspector.png | Bin 195850 -> 0 bytes packages/ui/img/inspector@2x.png | Bin 555046 -> 0 bytes packages/ui/img/modules.png | Bin 283231 -> 0 bytes packages/ui/img/modules@2x.png | Bin 756774 -> 0 bytes packages/ui/img/oxlint.png | Bin 161487 -> 0 bytes packages/ui/img/oxlint@2x.png | Bin 445048 -> 0 bytes packages/ui/img/packages.png | Bin 186052 -> 0 bytes packages/ui/img/packages@2x.png | Bin 535427 -> 0 bytes packages/ui/img/process-compose.png | Bin 145278 -> 0 bytes packages/ui/img/process-compose@2x.png | Bin 409900 -> 0 bytes packages/ui/img/secrets-sources.png | Bin 188814 -> 0 bytes packages/ui/img/secrets-sources@2x.png | Bin 532417 -> 0 bytes packages/ui/img/secrets.png | Bin 149349 -> 0 bytes packages/ui/img/secrets@2x.png | Bin 429374 -> 0 bytes packages/ui/img/step-ca.png | Bin 82281 -> 0 bytes packages/ui/img/step-ca@2x.png | Bin 239454 -> 0 bytes packages/ui/img/team.png | Bin 165279 -> 0 bytes packages/ui/img/team@2x.png | Bin 493270 -> 0 bytes sst.config.example.ts | 60 - tests/deployment/.gitignore | 3 - tests/deployment/sst.config.ts | 13 - tests/test_monorepo.sh | 16 - 136 files changed, 16 insertions(+), 54248 deletions(-) delete mode 100644 .omo/run-continuation/ses_134bcae84ffef5Z1jt1PQ1tLXJ.json delete mode 100644 .omo/run-continuation/ses_1426cb957ffeJ6H0qnnvrYuSFg.json delete mode 100644 .stack/config.example.nix delete mode 100644 apps/stackpanel-go/.stack/config.nix.example delete mode 100755 apps/web/.stackpanel/gen/ide/vscode/devshell-loader.sh delete mode 100644 apps/web/.stackpanel/gen/ide/vscode/stackpanel.code-workspace delete mode 100644 apps/web/.stackpanel/gen/schemas/secrets/app-config.schema.json delete mode 100644 apps/web/.stackpanel/gen/schemas/secrets/config.schema.json delete mode 100644 apps/web/.stackpanel/gen/schemas/secrets/env.schema.json delete mode 100644 apps/web/.stackpanel/gen/schemas/secrets/schema.schema.json delete mode 100644 apps/web/.stackpanel/gen/schemas/secrets/users.schema.json delete mode 100644 apps/web/.stackpanel/secrets/recipients/.sops.yaml delete mode 100644 apps/web/.stackpanel/secrets/recipients/cooper-maruyama.age.pub delete mode 100644 apps/web/.stackpanel/secrets/vars/.sops.yaml delete mode 100644 apps/web/.stackpanel/state/keys/local.pub delete mode 100644 apps/web/.stackpanel/state/stackpanel.json delete mode 100644 docs/colmena.md delete mode 100644 docs/infra.md delete mode 100644 docs/infradeploy.md delete mode 100644 docs/machines.md delete mode 100644 docs/sess-infra.md delete mode 100644 infra/sst/apps/docs.ts delete mode 100644 infra/sst/apps/index.ts delete mode 100644 infra/sst/apps/web.ts delete mode 100644 infra/sst/config.ts delete mode 100644 infra/sst/index.ts delete mode 100644 infra/sst/secrets.ts delete mode 100644 infra/sst/sops-output.ts delete mode 100644 infra/sst/sst.config.ts delete mode 100644 nix/NOTES.md delete mode 100644 nix/flake/devshells/README.md delete mode 100644 nix/flake/devshells/default.nix delete mode 100644 nix/flake/per-system-outputs.nix delete mode 100644 nix/flake/templates/devenv/.envrc delete mode 100644 nix/flake/templates/example-monorepo/.env.example delete mode 100644 nix/flake/templates/example-monorepo/.gitignore delete mode 100644 nix/flake/templates/example-monorepo/apps/analytics/alchemy.run.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/analytics/package.json delete mode 100644 nix/flake/templates/example-monorepo/apps/analytics/src/index.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/analytics/tsconfig.json delete mode 100644 nix/flake/templates/example-monorepo/apps/backend/alchemy.run.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/backend/package.json delete mode 100644 nix/flake/templates/example-monorepo/apps/backend/src/index.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/backend/tsconfig.json delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/README.md delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/alchemy.run.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/eslint.config.js delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/index.html delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/package.json delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/public/vite.svg delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/App.css delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/App.tsx delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/assets/react.svg delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/index.css delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/main.tsx delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/vite-env.d.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/src/worker.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/tsconfig.json delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/types/env.d.ts delete mode 100644 nix/flake/templates/example-monorepo/apps/frontend/vite.config.ts delete mode 100644 nix/flake/templates/example-monorepo/bun.lock delete mode 100644 nix/flake/templates/example-monorepo/package.json delete mode 100644 nix/flake/templates/example-monorepo/tsconfig.json delete mode 100644 nix/flake/templates/example-monorepo/turbo.json delete mode 100644 nix/internal/flake/README.md delete mode 100644 nix/internal/flake/default.nix delete mode 100644 nix/internal/flake/devshells/README.md delete mode 100644 nix/internal/flake/exports.nix delete mode 100644 nix/internal/flake/packages.nix delete mode 100644 nix/internal/flake/templates/README.md delete mode 100644 nix/internal/flake/templates/_test-fixtures/README.md delete mode 100644 nix/internal/flake/templates/_test-fixtures/basic/.stack/config.nix delete mode 100644 nix/internal/flake/templates/_test-fixtures/external-module/.stack/config.nix delete mode 100644 nix/internal/flake/templates/_test-fixtures/full-stack/.stack/config.nix delete mode 100755 nix/internal/flake/templates/_test-fixtures/update-golden.sh delete mode 100644 nix/internal/flake/templates/_test-fixtures/with-oxlint/.stack/config.nix delete mode 100644 nix/internal/flake/templates/default/.envrc delete mode 100644 nix/internal/flake/templates/default/.gitignore delete mode 100644 nix/internal/flake/templates/default/.stack/config.nix delete mode 100644 nix/internal/flake/templates/default/.stack/data.nix delete mode 100644 nix/internal/flake/templates/default/README.md delete mode 100644 nix/internal/flake/templates/devenv/.envrc delete mode 100644 nix/internal/flake/templates/minimal/.envrc delete mode 100644 nix/internal/flake/templates/minimal/.gitignore delete mode 100644 nix/internal/flake/templates/minimal/.stack/config.nix delete mode 100644 nix/internal/flake/templates/minimal/.stack/data.nix delete mode 100644 nix/internal/flake/templates/minimal/README.md delete mode 100644 nix/stackpanel/core/lib/envvars.nix delete mode 100644 nix/stackpanel/core/lib/envvars/derived.nix delete mode 100644 nix/stackpanel/core/lib/envvars/helpers.nix delete mode 100644 nix/stackpanel/core/lib/envvars/sections.nix delete mode 100644 nix/stackpanel/core/lib/evalconfig.nix delete mode 100644 nix/stackpanel/core/lib/services-default.nix delete mode 100644 nix/stackpanel/core/options-core.nix delete mode 100644 packages/ui/img/apps.png delete mode 100644 packages/ui/img/apps@2x.png delete mode 100644 packages/ui/img/binary-cache.png delete mode 100644 packages/ui/img/binary-cache@2x.png delete mode 100644 packages/ui/img/certs.png delete mode 100644 packages/ui/img/certs@2x.png delete mode 100644 packages/ui/img/ide-integration.png delete mode 100644 packages/ui/img/ide-integration@2x.png delete mode 100644 packages/ui/img/inspector.png delete mode 100644 packages/ui/img/inspector@2x.png delete mode 100644 packages/ui/img/modules.png delete mode 100644 packages/ui/img/modules@2x.png delete mode 100644 packages/ui/img/oxlint.png delete mode 100644 packages/ui/img/oxlint@2x.png delete mode 100644 packages/ui/img/packages.png delete mode 100644 packages/ui/img/packages@2x.png delete mode 100644 packages/ui/img/process-compose.png delete mode 100644 packages/ui/img/process-compose@2x.png delete mode 100644 packages/ui/img/secrets-sources.png delete mode 100644 packages/ui/img/secrets-sources@2x.png delete mode 100644 packages/ui/img/secrets.png delete mode 100644 packages/ui/img/secrets@2x.png delete mode 100644 packages/ui/img/step-ca.png delete mode 100644 packages/ui/img/step-ca@2x.png delete mode 100644 packages/ui/img/team.png delete mode 100644 packages/ui/img/team@2x.png delete mode 100644 sst.config.example.ts delete mode 100644 tests/deployment/.gitignore delete mode 100644 tests/deployment/sst.config.ts delete mode 100755 tests/test_monorepo.sh diff --git a/.omo/run-continuation/ses_134bcae84ffef5Z1jt1PQ1tLXJ.json b/.omo/run-continuation/ses_134bcae84ffef5Z1jt1PQ1tLXJ.json deleted file mode 100644 index 5b5e03aa..00000000 --- a/.omo/run-continuation/ses_134bcae84ffef5Z1jt1PQ1tLXJ.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "sessionID": "ses_134bcae84ffef5Z1jt1PQ1tLXJ", - "updatedAt": "2026-06-15T12:34:43.776Z", - "sources": { - "background-task": { - "state": "idle", - "updatedAt": "2026-06-15T12:34:43.776Z" - } - } -} \ No newline at end of file diff --git a/.omo/run-continuation/ses_1426cb957ffeJ6H0qnnvrYuSFg.json b/.omo/run-continuation/ses_1426cb957ffeJ6H0qnnvrYuSFg.json deleted file mode 100644 index 73e8a942..00000000 --- a/.omo/run-continuation/ses_1426cb957ffeJ6H0qnnvrYuSFg.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "sessionID": "ses_1426cb957ffeJ6H0qnnvrYuSFg", - "updatedAt": "2026-06-15T12:26:49.614Z", - "sources": { - "background-task": { - "state": "idle", - "updatedAt": "2026-06-15T12:26:49.614Z" - } - } -} \ No newline at end of file diff --git a/.stack/config.example.nix b/.stack/config.example.nix deleted file mode 100644 index 03ac1dba..00000000 --- a/.stack/config.example.nix +++ /dev/null @@ -1,507 +0,0 @@ -# ============================================================================== -# config.nix -# -# Stackpanel project configuration (starter). -# -# Generated from the current option schema. -# -# To see the latest options and examples (after upgrading stackpanel): -# stack config generate --output .stack/config.example.nix -# -# Review the generated .example and copy/merge sections you need. -# ============================================================================== -{ - # ---------------------------------------------------------------------------- - # Cli - # ---------------------------------------------------------------------------- - cli = { - # Whether to enable CLI-based file generation. - enable = false; - - # Suppress generation output messages - quiet = false; - }; - - # ---------------------------------------------------------------------------- - # Deploy - # ---------------------------------------------------------------------------- - deploy = { - # Base URL of the stackpanel cloud API. Override for self-hosted or staging - # environments. The alchemy adapter reads this from STACKPANEL_API_URL at - # deploy time. - apiUrl = "https://api.stackpanel.com"; - - # Which backend alchemy uses for deploy state. - `local`: filesystem at - # .alchemy/state/ (default). No network, no account, works offline. State - # lives on whichever machine ran the deploy, so CI runners orphan resources - # across runs. - `hosted`: api.stackpanel.com stores encrypted state per - # organization. Survives runner churn, enables true team deploys, audited via - # the studio's State panel. Requires an active Pro subscription. - stateBackend = "local"; - }; - - # ---------------------------------------------------------------------------- - # Devshell - # ---------------------------------------------------------------------------- - devshell = { - # This option has no description. - buildInputs = [ ]; - - clean = { - # List of shell aliases to unset when entering the devshell. Use this if you - # have aliases that conflict with stackpanel scripts (e.g., "dev"). - aliases = [ ]; - - # Whether to enable clean environment mode. - enable = false; - - # Whether to use --impure flag when entering the devshell. --impure allows Nix - # to access environment variables and system state, but prevents effective - # caching between runs. Set to false if you want better caching and your - # devshell doesn't need access to parent environment state. - impure = false; - - # Environment variables to preserve when clean.enable is true. These variables - # are passed through from the parent environment. Use `nix develop - # --ignore-environment --impure` with `--keep` flags for each variable in this - # list, or use the generated wrapper script. - keep = [ - "HOME" - "USER" - "LOGNAME" - "SHELL" - "TMPDIR" - "TERM" - "COLORTERM" - "TERM_PROGRAM" - "TERM_PROGRAM_VERSION" - "LANG" - "LC_ALL" - "LC_CTYPE" - "SSH_AUTH_SOCK" - "SSH_SOCKET_DIR" - "GPG_AGENT_INFO" - "GNUPGHOME" - "EDITOR" - "VISUAL" - "PAGER" - "__CF_USER_TEXT_ENCODING" - "COMMAND_MODE" - ]; - - # Direnv state variables. Only needed if using direnv inside the clean shell. - keepDirenv = [ - "DIRENV_DIR" - "DIRENV_FILE" - ]; - - # Environment variables for fzf configuration. Add to clean.keep if you want - # to preserve your fzf settings. - keepFzf = [ - "FZF_DEFAULT_COMMAND" - "FZF_DEFAULT_OPTS" - "FZF_CTRL_T_COMMAND" - "FZF_ALT_C_COMMAND" - ]; - - # Additional environment variables to keep for GUI applications. These are NOT - # included by default. Add them to clean.keep if needed: - # stackpanel.devshell.clean.keep = config.stackpanel.devshell.clean.keep ++ - # config.stackpanel.devshell.clean.keepGui; - keepGui = [ - "DISPLAY" - "WAYLAND_DISPLAY" - "XDG_RUNTIME_DIR" - "DBUS_SESSION_BUS_ADDRESS" - ]; - - # Environment variables for Warp terminal features. Add to clean.keep if using - # Warp terminal. - keepWarp = [ - "WARP_HONOR_PS1" - "WARP_IS_LOCAL_SHELL_SESSION" - "WARP_USE_SSH_WRAPPER" - ]; - - # XDG base directory environment variables (often set by home-manager). Add to - # clean.keep if you want to preserve these paths. - keepXdg = [ - "XDG_CACHE_HOME" - "XDG_CONFIG_HOME" - "XDG_DATA_HOME" - "XDG_STATE_HOME" - ]; - }; - - # This option has no description. - env = { }; - - hooks = { - # This option has no description. - after = [ ]; - - # This option has no description. - before = [ ]; - - # This option has no description. - main = [ ]; - }; - - # This option has no description. - nativeBuildInputs = [ ]; - - # This option has no description. - packages = [ ]; - - path = { - # This option has no description. - append = [ ]; - - # This option has no description. - prepend = [ ]; - }; - - # If true, timing information will be printed during hook execution. - timing = false; - }; - - # ---------------------------------------------------------------------------- - # Direnv - # ---------------------------------------------------------------------------- - direnv = { - # Hide the 'direnv: export +VAR...' log line (requires user's direnv.toml) - hide-env-diff = true; - }; - - # ---------------------------------------------------------------------------- - # Dirs - # ---------------------------------------------------------------------------- - # Directories used by stackpanel. - dirs = { - # Root directory for stackpanel files (relative to project root). This is the - # ONLY configurable directory option. Subdirectories are automatically - # computed: - keys/ (gitignored) - persistent credentials (AGE, AWS, step) - - # profile/ (gitignored) - ephemeral runtime/cache (safe to rm) - gen/ - # (gitignored) - generated IDE configs, schemas - data/ (checked in) - - # nix-backed configuration db Example: ".stack" → keys at ".stack/keys", - # profile at ".stack/profile" - home = ".stack"; - }; - - # ---------------------------------------------------------------------------- - # Enable - # ---------------------------------------------------------------------------- - # Whether to enable Enable Stackpanel. - enable = true; - - # ---------------------------------------------------------------------------- - # Envs - # ---------------------------------------------------------------------------- - # Environment variables grouped by scope name. Three usage patterns: 1. - # App-scoped (auto-populated). Each app's `apps..env` is exploded into - # one entry per environment ID, keyed `apps//`: - # `stackpanel.envs."apps/web/dev".DATABASE_URL = { ... };` You don't write - # these directly — they're contributed by env-codegen. 2. Cross-cutting - # scopes (you write these). A bare scope name like `deploy`, `infra`, or `ci` - # is meant for variables that are not tied to any single app: - # `stackpanel.envs.deploy.CLOUDFLARE_API_TOKEN = { sops = "..."; };` Loaded at - # runtime via `loadEnvScope("deploy")` (see - # `packages/gen/env/src/runtime/loader.ts`). 3. Module-contributed (modules - # write these). Stackpanel modules can declare envs they need by writing to - # `stackpanel.envs.` from their own `config = { ... };`. For example, a - # Cloudflare deployment module that's only enabled when an app sets - # `deployment.host = "cloudflare"` can do: config.stackpanel.envs.deploy = - # lib.mkIf hasCloudflareApp { CLOUDFLARE_ACCOUNT_ID = { sops = - # "/shared/cloudflare-account-id"; required = true; description = "..."; }; }; - # Module-contributed entries merge cleanly with user-written ones thanks to - # NixOS submodule semantics (declaring the same key from two places is a no-op - # as long as the values agree). Codegen reads from this attrset to produce - # per-env SOPS payloads under `/data/_envs/.sops.json` (one - # file per env, encrypted to all recipients eligible for that env). - envs = { }; - - # ---------------------------------------------------------------------------- - # FlakeApps - # ---------------------------------------------------------------------------- - # Flake apps to expose via `nix run .#`. Each app must have: - type: - # "app" - program: Path to executable (usually from a derivation) Example: - # stackpanel.flakeApps = { web = { type = "app"; program = - # "${myPackage}/bin/web"; }; }; - flakeApps = { }; - - # ---------------------------------------------------------------------------- - # Git-hooks - # ---------------------------------------------------------------------------- - # Git hooks configuration fragment (consumed by git-hooks.nix). - git-hooks = { }; - - # ---------------------------------------------------------------------------- - # Gitignore - # ---------------------------------------------------------------------------- - # Reserved options for managed `.gitignore` generation. Stackpanel core owns a - # block-managed section in `.gitignore` and merges entries from this option. - # This provides a stable, explicit API for common `.gitignore` presets while - # still allowing modules to contribute entries. The generated block is - # deduplicated and sorted. - gitignore = { - # Toggle built-in `.gitignore` presets managed by Stackpanel. - defaults = { - # DEPRECATED: Use `stackpanel.gitignore.defaults.projectMarker` instead. - # Backward-compatible alias for including `stackpanel.root-marker` in - # `.gitignore`. - addProjectMarker = false; - - # Include `.stack/config.local.nix`. - localConfig = true; - - # Include the root marker file (`stackpanel.root-marker`). - projectMarker = false; - - # Include `.stack/state/`. - stackpanelState = true; - - # Include `.tasks`. - tasksDir = true; - }; - - # Whether Stackpanel should manage a `.gitignore` block. - enable = true; - - # Additional `.gitignore` entries to include in the managed block. Example: [ - # "dist/" ".env.local" ] - entries = [ ]; - }; - - # ---------------------------------------------------------------------------- - # ModuleRequirements - # ---------------------------------------------------------------------------- - # Variable requirements declared by enabled modules. Modules add entries here - # to declare what environment variables they need. The agent/UI can query this - # to show what's missing. Format: { moduleName = { requires = [ ... ]; - # provides = [ ... ]; }; } - moduleRequirements = { }; - - # ---------------------------------------------------------------------------- - # Modules - # ---------------------------------------------------------------------------- - # Stackpanel modules that provide features and UI panels. Modules are the - # unified way to extend stackpanel functionality: - Add packages, scripts, and - # environment configuration - Generate files and manage secrets - Define - # health checks and background services - Provide UI panels for the web studio - # - Extend per-app configuration Each module can define: - `enable`: Whether - # the module is active - `meta`: Display metadata (name, description, icon, - # category) - `source`: Where the module comes from (builtin, local, - # flake-input, registry) - `features`: Which stackpanel systems it uses - - # `panels`: UI panels to render in the web studio - `configSchema`: JSON - # Schema for configuration form generation - `healthcheckModule`: Link to - # health checks Modules can be: - Builtin: Shipped with stackpanel - Local: - # Defined in your project - Remote: Installed via flake inputs or module - # registry - modules = { }; - - # ---------------------------------------------------------------------------- - # Name - # ---------------------------------------------------------------------------- - # Name of your project. May be used for naming things and diplay purposes. - name = "my-project"; - - # ---------------------------------------------------------------------------- - # Panels - # ---------------------------------------------------------------------------- - # UI panels for core Stackpanel modules. Panels are UI components that display - # information about a module's state, configuration, or managed resources. - # Unlike extension panels, these belong to built-in modules like Go, Caddy, - # Healthchecks, etc. Example: stackpanel.panels.go-status = { module = "go"; - # title = "Go Environment"; type = "PANEL_TYPE_STATUS"; order = 10; fields = [ - # { name = "metrics"; type = "FIELD_TYPE_STRING"; value = "..."; } ]; }; - panels = { }; - - # ---------------------------------------------------------------------------- - # Project - # ---------------------------------------------------------------------------- - # Project metadata including type, owner, and repository information. - project = { - # Project name (defaults to stackpanel.name) - name = "my-project"; - - # Project type (e.g., 'github', 'gitlab', 'local') - type = "github"; - }; - - # ---------------------------------------------------------------------------- - # Root-marker - # ---------------------------------------------------------------------------- - # Filename for the root marker file written to the project root. Contains the - # absolute path to the project root, allowing tools to find the project from - # any subdirectory. Add to .dockerignore and .gitignore so containers create - # their own marker. - root-marker = ".stackpanel-root"; - - # ---------------------------------------------------------------------------- - # Secrets - # ---------------------------------------------------------------------------- - secrets = { - # Secret storage backend. This is the single source of truth that controls how - # secrets are stored, how entrypoints inject them, how the agent reads/writes - # them, and what options are available in the UI. "sops" (default): direct - # SOPS/AGE encryption using generated `.sops.yaml` files. "vals": legacy - # AGE/SOPS encryption with vals for external references. "chamber": AWS SSM - # Parameter Store via the chamber CLI. - backend = "sops"; - - chamber = { - # Chamber service prefix. The full chamber service path is: - # {service-prefix}/{env} For example, with prefix "darkmatter/stackpanel" and - # a variable /dev/DATABASE_URL: chamber write darkmatter/stackpanel/dev - # DATABASE_URL chamber exec darkmatter/stackpanel/dev -- - # Defaults to "{owner}/{repo}" when project owner/repo are configured, - # otherwise falls back to the project name. - service-prefix = "my-project"; - }; - - # Code generation targets keyed by name (e.g., typescript, go, python). Used - # to drive language-specific env/secret helpers. - codegen = { }; - - # SOPS creation rules rendered into `.stack/secrets/.sops.yaml`. These rules - # mirror SOPS directly and can reference both direct recipients and reusable - # recipient groups. - creation-rules = [ ]; - - # Enable secrets management - enable = false; - - # Legacy environment-specific secrets configuration. - environments = { }; - - # Deprecated legacy groups option. - groups = { }; - - # Directory containing SOPS-encrypted secrets (legacy SOPS layout). Used when - # decrypting/merging YAML sources defined under environments. - input-directory = null; - - kms = { - # Optional AWS profile name to pass alongside the KMS ARN in `.sops.yaml`. - aws-profile = null; - - # Optional AWS IAM role ARN for assume-role. Added to the SOPS KMS entry as - # `role_arn` when set. - aws-role-arn = null; - - # AWS KMS key ARN to add as a SOPS recipient in `.stack/secrets/.sops.yaml`. - # When set, every creation rule will encrypt to this KMS key in addition to - # the configured AGE recipients. - key-arn = null; - }; - - # Master keys for encrypting and decrypting individual `.age` secret files. - # These are separate from the SOPS recipient list used for `vars/*.sops.yaml`. - # A default local key is always configured so local development works out of - # the box. - master-keys = { - local = { - age-pub = ""; - ref = "ref+file://.stack/keys/local.txt"; - }; - }; - - # Reusable recipient sets that can be referenced by SOPS creation rules. - recipient-groups = { }; - - # SOPS recipients declared in Nix. These entries are rendered into - # `.stack/secrets/.sops.yaml`. If left empty, Stackpanel falls back to - # recipients derived from `stackpanel.users.*.public-keys` and - # `secrets-allowed-environments`. - recipients = { }; - - # Directory where secret .age files are stored (default: .stack/secrets) - secrets-dir = null; - - sops-age-keys = { - # Optional `op://` references queried with `op read`. If configured, - # `sops-age-keys` attempts these references after configured file paths. - op-refs = [ ]; - - # Additional private key file paths searched by `sops-age-keys` after - # `user-key-path` and `repo-key-path`. Paths are tested with `[[ -f ]]` as - # provided, so they can be absolute or relative to your current working - # directory. - paths = [ ]; - - # Repo-local fallback AGE key path generated by Stackpanel for development. - # This is convenient for bootstrapping, but a user-level or external key - # source is preferred for long-term use. - repo-key-path = ".stack/keys/local.txt"; - - # Ordered key sources tried by `sops-age-keys`. This is the preferred - # configuration model for the UI. File-like sources are tried in order until - # one yields an AGE private key. - sources = [ - { - name = "User key path"; - type = "user-key-path"; - value = "$HOME/Library/Application Support/sops/age/keys.txt"; - } - { - name = "Repo key path"; - type = "repo-key-path"; - value = ".stack/keys/local.txt"; - } - ]; - - # Primary user-level AGE key path for SOPS. This should point to a - # user-managed key file outside the repo. Stackpanel checks this before any - # additional custom paths. - user-key-path = "$HOME/Library/Application Support/sops/age/keys.txt"; - }; - - # System-level AGE public keys (CI, deploy servers, etc.). These keys can - # decrypt all secrets regardless of environment restrictions. - system-keys = [ ]; - }; - - # ---------------------------------------------------------------------------- - # Serializable - # ---------------------------------------------------------------------------- - # Serializable configuration data for the agent and CLI. Modules can - # contribute their JSON-safe config here. This data is included in - # stackpanelConfig for external tools. - serializable = { }; - - # ---------------------------------------------------------------------------- - # State - # ---------------------------------------------------------------------------- - state = { - # Arbitrary state data contributed by modules. Use this for module-specific - # state that should be serialized. - custom = { }; - - # Devenv-related state for serialization. Populated by devenv integration - # modules (services, languages, pre-commit). Structure: { services = { - # available = [...]; enabled = [...]; }; languages = { available = [...]; - # enabled = [...]; }; preCommit = { available = [...]; enabled = [...]; }; } - devenv = { }; - - # Name of the state file written to the state directory. - file = "stackpanel.json"; - }; - - # ---------------------------------------------------------------------------- - # UserPackages - # ---------------------------------------------------------------------------- - userPackages = { - # Whether to enable user-installed packages from .stack/data/packages.nix. - enable = true; - }; - - # ---------------------------------------------------------------------------- - # Variables - # ---------------------------------------------------------------------------- - # Workspace variables keyed by their full variable ID. Prefixes determine - # storage: /computed/* - Nix-computed values (read-only) /* - Grouped - # SOPS-backed variables stored in `vars/.sops.yaml` Non-computed - # variable values resolve to variable-link markers; the group SOPS file is the - # source of truth. - variables = { }; -} diff --git a/apps/docs/content/docs/internal/core.mdx b/apps/docs/content/docs/internal/core.mdx index 18b6b84c..a4a70a7f 100644 --- a/apps/docs/content/docs/internal/core.mdx +++ b/apps/docs/content/docs/internal/core.mdx @@ -16,7 +16,7 @@ Feature-specific options live in their own directories (`apps/`, `services/`, `n | File | Description | |------|-------------| | `default.nix` | Entry point — imports all core modules, sets up hooks | -| `options-core.nix` | Root options: enable, name, root, dirs, direnv, git-hooks, checks | +| `options/core.nix` | Root options: enable, name, root, dirs, direnv, git-hooks, checks | | `outputs.nix` | Flake output derivations and apps | | `checks.nix` | Module check schema for `nix flake check` | | `motd.nix` | Message of the Day configuration | diff --git a/apps/stackpanel-go/.stack/config.nix.example b/apps/stackpanel-go/.stack/config.nix.example deleted file mode 100644 index 29a4a4c0..00000000 --- a/apps/stackpanel-go/.stack/config.nix.example +++ /dev/null @@ -1,507 +0,0 @@ -# ============================================================================== -# config.nix -# -# Stackpanel project configuration (starter). -# -# Generated from the current option schema. -# -# To see the latest options and examples (after upgrading stackpanel): -# stack config generate --output .stack/config.example.nix -# -# Review the generated .example and copy/merge sections you need. -# ============================================================================== -{ - # ---------------------------------------------------------------------------- - # Cli - # ---------------------------------------------------------------------------- - cli = { - # Whether to enable CLI-based file generation. - enable = false; - - # Suppress generation output messages - quiet = false; - }; - - # ---------------------------------------------------------------------------- - # Deploy - # ---------------------------------------------------------------------------- - deploy = { - # Base URL of the stackpanel cloud API. Override for self-hosted or staging - # environments. The alchemy adapter reads this from STACKPANEL_API_URL at - # deploy time. - apiUrl = "https://api.stackpanel.com"; - - # Which backend alchemy uses for deploy state. - `local`: filesystem at - # .alchemy/state/ (default). No network, no account, works offline. State - # lives on whichever machine ran the deploy, so CI runners orphan resources - # across runs. - `hosted`: api.stackpanel.com stores encrypted state per - # organization. Survives runner churn, enables true team deploys, audited via - # the studio's State panel. Requires an active Pro subscription. - stateBackend = "local"; - }; - - # ---------------------------------------------------------------------------- - # Devshell - # ---------------------------------------------------------------------------- - devshell = { - # This option has no description. - buildInputs = [ ]; - - clean = { - # List of shell aliases to unset when entering the devshell. Use this if you - # have aliases that conflict with stackpanel scripts (e.g., "dev"). - aliases = [ ]; - - # Whether to enable clean environment mode. - enable = false; - - # Whether to use --impure flag when entering the devshell. --impure allows Nix - # to access environment variables and system state, but prevents effective - # caching between runs. Set to false if you want better caching and your - # devshell doesn't need access to parent environment state. - impure = true; - - # Environment variables to preserve when clean.enable is true. These variables - # are passed through from the parent environment. Use `nix develop - # --ignore-environment --impure` with `--keep` flags for each variable in this - # list, or use the generated wrapper script. - keep = [ - "HOME" - "USER" - "LOGNAME" - "SHELL" - "TMPDIR" - "TERM" - "COLORTERM" - "TERM_PROGRAM" - "TERM_PROGRAM_VERSION" - "LANG" - "LC_ALL" - "LC_CTYPE" - "SSH_AUTH_SOCK" - "SSH_SOCKET_DIR" - "GPG_AGENT_INFO" - "GNUPGHOME" - "EDITOR" - "VISUAL" - "PAGER" - "__CF_USER_TEXT_ENCODING" - "COMMAND_MODE" - ]; - - # Direnv state variables. Only needed if using direnv inside the clean shell. - keepDirenv = [ - "DIRENV_DIR" - "DIRENV_FILE" - ]; - - # Environment variables for fzf configuration. Add to clean.keep if you want - # to preserve your fzf settings. - keepFzf = [ - "FZF_DEFAULT_COMMAND" - "FZF_DEFAULT_OPTS" - "FZF_CTRL_T_COMMAND" - "FZF_ALT_C_COMMAND" - ]; - - # Additional environment variables to keep for GUI applications. These are NOT - # included by default. Add them to clean.keep if needed: - # stackpanel.devshell.clean.keep = config.stackpanel.devshell.clean.keep ++ - # config.stackpanel.devshell.clean.keepGui; - keepGui = [ - "DISPLAY" - "WAYLAND_DISPLAY" - "XDG_RUNTIME_DIR" - "DBUS_SESSION_BUS_ADDRESS" - ]; - - # Environment variables for Warp terminal features. Add to clean.keep if using - # Warp terminal. - keepWarp = [ - "WARP_HONOR_PS1" - "WARP_IS_LOCAL_SHELL_SESSION" - "WARP_USE_SSH_WRAPPER" - ]; - - # XDG base directory environment variables (often set by home-manager). Add to - # clean.keep if you want to preserve these paths. - keepXdg = [ - "XDG_CACHE_HOME" - "XDG_CONFIG_HOME" - "XDG_DATA_HOME" - "XDG_STATE_HOME" - ]; - }; - - # This option has no description. - env = { }; - - hooks = { - # This option has no description. - after = [ ]; - - # This option has no description. - before = [ ]; - - # This option has no description. - main = [ ]; - }; - - # This option has no description. - nativeBuildInputs = [ ]; - - # This option has no description. - packages = [ ]; - - path = { - # This option has no description. - append = [ ]; - - # This option has no description. - prepend = [ ]; - }; - - # If true, timing information will be printed during hook execution. - timing = false; - }; - - # ---------------------------------------------------------------------------- - # Direnv - # ---------------------------------------------------------------------------- - direnv = { - # Hide the 'direnv: export +VAR...' log line (requires user's direnv.toml) - hide-env-diff = true; - }; - - # ---------------------------------------------------------------------------- - # Dirs - # ---------------------------------------------------------------------------- - # Directories used by stackpanel. - dirs = { - # Root directory for stackpanel files (relative to project root). This is the - # ONLY configurable directory option. Subdirectories are automatically - # computed: - keys/ (gitignored) - persistent credentials (AGE, AWS, step) - - # profile/ (gitignored) - ephemeral runtime/cache (safe to rm) - gen/ - # (gitignored) - generated IDE configs, schemas - data/ (checked in) - - # nix-backed configuration db Example: ".stack" → keys at ".stack/keys", - # profile at ".stack/profile" - home = ".stack"; - }; - - # ---------------------------------------------------------------------------- - # Enable - # ---------------------------------------------------------------------------- - # Whether to enable Enable Stackpanel. - enable = true; - - # ---------------------------------------------------------------------------- - # Envs - # ---------------------------------------------------------------------------- - # Environment variables grouped by scope name. Three usage patterns: 1. - # App-scoped (auto-populated). Each app's `apps..env` is exploded into - # one entry per environment ID, keyed `apps//`: - # `stackpanel.envs."apps/web/dev".DATABASE_URL = { ... };` You don't write - # these directly — they're contributed by env-codegen. 2. Cross-cutting - # scopes (you write these). A bare scope name like `deploy`, `infra`, or `ci` - # is meant for variables that are not tied to any single app: - # `stackpanel.envs.deploy.CLOUDFLARE_API_TOKEN = { sops = "..."; };` Loaded at - # runtime via `loadEnvScope("deploy")` (see - # `packages/gen/env/src/runtime/loader.ts`). 3. Module-contributed (modules - # write these). Stackpanel modules can declare envs they need by writing to - # `stackpanel.envs.` from their own `config = { ... };`. For example, a - # Cloudflare deployment module that's only enabled when an app sets - # `deployment.host = "cloudflare"` can do: config.stackpanel.envs.deploy = - # lib.mkIf hasCloudflareApp { CLOUDFLARE_ACCOUNT_ID = { sops = - # "/shared/cloudflare-account-id"; required = true; description = "..."; }; }; - # Module-contributed entries merge cleanly with user-written ones thanks to - # NixOS submodule semantics (declaring the same key from two places is a no-op - # as long as the values agree). Codegen reads from this attrset to produce - # per-env SOPS payloads under `/data/_envs/.sops.json` (one - # file per env, encrypted to all recipients eligible for that env). - envs = { }; - - # ---------------------------------------------------------------------------- - # FlakeApps - # ---------------------------------------------------------------------------- - # Flake apps to expose via `nix run .#`. Each app must have: - type: - # "app" - program: Path to executable (usually from a derivation) Example: - # stackpanel.flakeApps = { web = { type = "app"; program = - # "${myPackage}/bin/web"; }; }; - flakeApps = { }; - - # ---------------------------------------------------------------------------- - # Git-hooks - # ---------------------------------------------------------------------------- - # Git hooks configuration fragment (consumed by git-hooks.nix). - git-hooks = { }; - - # ---------------------------------------------------------------------------- - # Gitignore - # ---------------------------------------------------------------------------- - # Reserved options for managed `.gitignore` generation. Stackpanel core owns a - # block-managed section in `.gitignore` and merges entries from this option. - # This provides a stable, explicit API for common `.gitignore` presets while - # still allowing modules to contribute entries. The generated block is - # deduplicated and sorted. - gitignore = { - # Toggle built-in `.gitignore` presets managed by Stackpanel. - defaults = { - # DEPRECATED: Use `stackpanel.gitignore.defaults.projectMarker` instead. - # Backward-compatible alias for including `stackpanel.root-marker` in - # `.gitignore`. - addProjectMarker = false; - - # Include `.stack/config.local.nix`. - localConfig = true; - - # Include the root marker file (`stackpanel.root-marker`). - projectMarker = false; - - # Include `.stack/state/`. - stackpanelState = true; - - # Include `.tasks`. - tasksDir = true; - }; - - # Whether Stackpanel should manage a `.gitignore` block. - enable = true; - - # Additional `.gitignore` entries to include in the managed block. Example: [ - # "dist/" ".env.local" ] - entries = [ ]; - }; - - # ---------------------------------------------------------------------------- - # ModuleRequirements - # ---------------------------------------------------------------------------- - # Variable requirements declared by enabled modules. Modules add entries here - # to declare what environment variables they need. The agent/UI can query this - # to show what's missing. Format: { moduleName = { requires = [ ... ]; - # provides = [ ... ]; }; } - moduleRequirements = { }; - - # ---------------------------------------------------------------------------- - # Modules - # ---------------------------------------------------------------------------- - # Stackpanel modules that provide features and UI panels. Modules are the - # unified way to extend stackpanel functionality: - Add packages, scripts, and - # environment configuration - Generate files and manage secrets - Define - # health checks and background services - Provide UI panels for the web studio - # - Extend per-app configuration Each module can define: - `enable`: Whether - # the module is active - `meta`: Display metadata (name, description, icon, - # category) - `source`: Where the module comes from (builtin, local, - # flake-input, registry) - `features`: Which stackpanel systems it uses - - # `panels`: UI panels to render in the web studio - `configSchema`: JSON - # Schema for configuration form generation - `healthcheckModule`: Link to - # health checks Modules can be: - Builtin: Shipped with stackpanel - Local: - # Defined in your project - Remote: Installed via flake inputs or module - # registry - modules = { }; - - # ---------------------------------------------------------------------------- - # Name - # ---------------------------------------------------------------------------- - # Name of your project. May be used for naming things and diplay purposes. - name = "my-project"; - - # ---------------------------------------------------------------------------- - # Panels - # ---------------------------------------------------------------------------- - # UI panels for core Stackpanel modules. Panels are UI components that display - # information about a module's state, configuration, or managed resources. - # Unlike extension panels, these belong to built-in modules like Go, Caddy, - # Healthchecks, etc. Example: stackpanel.panels.go-status = { module = "go"; - # title = "Go Environment"; type = "PANEL_TYPE_STATUS"; order = 10; fields = [ - # { name = "metrics"; type = "FIELD_TYPE_STRING"; value = "..."; } ]; }; - panels = { }; - - # ---------------------------------------------------------------------------- - # Project - # ---------------------------------------------------------------------------- - # Project metadata including type, owner, and repository information. - project = { - # Project name (defaults to stackpanel.name) - name = "my-project"; - - # Project type (e.g., 'github', 'gitlab', 'local') - type = "github"; - }; - - # ---------------------------------------------------------------------------- - # Root-marker - # ---------------------------------------------------------------------------- - # Filename for the root marker file written to the project root. Contains the - # absolute path to the project root, allowing tools to find the project from - # any subdirectory. Add to .dockerignore and .gitignore so containers create - # their own marker. - root-marker = ".stackpanel-root"; - - # ---------------------------------------------------------------------------- - # Secrets - # ---------------------------------------------------------------------------- - secrets = { - # Secret storage backend. This is the single source of truth that controls how - # secrets are stored, how entrypoints inject them, how the agent reads/writes - # them, and what options are available in the UI. "sops" (default): direct - # SOPS/AGE encryption using generated `.sops.yaml` files. "vals": legacy - # AGE/SOPS encryption with vals for external references. "chamber": AWS SSM - # Parameter Store via the chamber CLI. - backend = "sops"; - - chamber = { - # Chamber service prefix. The full chamber service path is: - # {service-prefix}/{env} For example, with prefix "darkmatter/stackpanel" and - # a variable /dev/DATABASE_URL: chamber write darkmatter/stackpanel/dev - # DATABASE_URL chamber exec darkmatter/stackpanel/dev -- - # Defaults to "{owner}/{repo}" when project owner/repo are configured, - # otherwise falls back to the project name. - service-prefix = "my-project"; - }; - - # Code generation targets keyed by name (e.g., typescript, go, python). Used - # to drive language-specific env/secret helpers. - codegen = { }; - - # SOPS creation rules rendered into `.stack/secrets/.sops.yaml`. These rules - # mirror SOPS directly and can reference both direct recipients and reusable - # recipient groups. - creation-rules = [ ]; - - # Enable secrets management - enable = false; - - # Legacy environment-specific secrets configuration. - environments = { }; - - # Deprecated legacy groups option. - groups = { }; - - # Directory containing SOPS-encrypted secrets (legacy SOPS layout). Used when - # decrypting/merging YAML sources defined under environments. - input-directory = null; - - kms = { - # Optional AWS profile name to pass alongside the KMS ARN in `.sops.yaml`. - aws-profile = null; - - # Optional AWS IAM role ARN for assume-role. Added to the SOPS KMS entry as - # `role_arn` when set. - aws-role-arn = null; - - # AWS KMS key ARN to add as a SOPS recipient in `.stack/secrets/.sops.yaml`. - # When set, every creation rule will encrypt to this KMS key in addition to - # the configured AGE recipients. - key-arn = null; - }; - - # Master keys for encrypting and decrypting individual `.age` secret files. - # These are separate from the SOPS recipient list used for `vars/*.sops.yaml`. - # A default local key is always configured so local development works out of - # the box. - master-keys = { - local = { - age-pub = ""; - ref = "ref+file://.stack/keys/local.txt"; - }; - }; - - # Reusable recipient sets that can be referenced by SOPS creation rules. - recipient-groups = { }; - - # SOPS recipients declared in Nix. These entries are rendered into - # `.stack/secrets/.sops.yaml`. If left empty, Stackpanel falls back to - # recipients derived from `stackpanel.users.*.public-keys` and - # `secrets-allowed-environments`. - recipients = { }; - - # Directory where secret .age files are stored (default: .stack/secrets) - secrets-dir = null; - - sops-age-keys = { - # Optional `op://` references queried with `op read`. If configured, - # `sops-age-keys` attempts these references after configured file paths. - op-refs = [ ]; - - # Additional private key file paths searched by `sops-age-keys` after - # `user-key-path` and `repo-key-path`. Paths are tested with `[[ -f ]]` as - # provided, so they can be absolute or relative to your current working - # directory. - paths = [ ]; - - # Repo-local fallback AGE key path generated by Stackpanel for development. - # This is convenient for bootstrapping, but a user-level or external key - # source is preferred for long-term use. - repo-key-path = ".stack/keys/local.txt"; - - # Ordered key sources tried by `sops-age-keys`. This is the preferred - # configuration model for the UI. File-like sources are tried in order until - # one yields an AGE private key. - sources = [ - { - name = "User key path"; - type = "user-key-path"; - value = "$HOME/Library/Application Support/sops/age/keys.txt"; - } - { - name = "Repo key path"; - type = "repo-key-path"; - value = ".stack/keys/local.txt"; - } - ]; - - # Primary user-level AGE key path for SOPS. This should point to a - # user-managed key file outside the repo. Stackpanel checks this before any - # additional custom paths. - user-key-path = "$HOME/Library/Application Support/sops/age/keys.txt"; - }; - - # System-level AGE public keys (CI, deploy servers, etc.). These keys can - # decrypt all secrets regardless of environment restrictions. - system-keys = [ ]; - }; - - # ---------------------------------------------------------------------------- - # Serializable - # ---------------------------------------------------------------------------- - # Serializable configuration data for the agent and CLI. Modules can - # contribute their JSON-safe config here. This data is included in - # stackpanelConfig for external tools. - serializable = { }; - - # ---------------------------------------------------------------------------- - # State - # ---------------------------------------------------------------------------- - state = { - # Arbitrary state data contributed by modules. Use this for module-specific - # state that should be serialized. - custom = { }; - - # Devenv-related state for serialization. Populated by devenv integration - # modules (services, languages, pre-commit). Structure: { services = { - # available = [...]; enabled = [...]; }; languages = { available = [...]; - # enabled = [...]; }; preCommit = { available = [...]; enabled = [...]; }; } - devenv = { }; - - # Name of the state file written to the state directory. - file = "stackpanel.json"; - }; - - # ---------------------------------------------------------------------------- - # UserPackages - # ---------------------------------------------------------------------------- - userPackages = { - # Whether to enable user-installed packages from .stack/data/packages.nix. - enable = true; - }; - - # ---------------------------------------------------------------------------- - # Variables - # ---------------------------------------------------------------------------- - # Workspace variables keyed by their full variable ID. Prefixes determine - # storage: /computed/* - Nix-computed values (read-only) /* - Grouped - # SOPS-backed variables stored in `vars/.sops.yaml` Non-computed - # variable values resolve to variable-link markers; the group SOPS file is the - # source of truth. - variables = { }; -} diff --git a/apps/stackpanel-go/internal/reconcile/rc_files.go b/apps/stackpanel-go/internal/reconcile/rc_files.go index 21df8da3..6b90bb8a 100644 --- a/apps/stackpanel-go/internal/reconcile/rc_files.go +++ b/apps/stackpanel-go/internal/reconcile/rc_files.go @@ -11,10 +11,10 @@ import ( "strings" ) -// FilesReconciler is the Go port of check-files-drift: it compares every pure -// (whole-file, unadopted) entry of the current generation against disk and -// detects stale files left by the previous generation. Apply delegates to the -// `write-files` script so there is exactly one writer. +// FilesReconciler compares every pure (whole-file, unadopted) entry of the +// current generation against disk and detects stale files left by the previous +// generation. Apply delegates to the `write-files` script so there is exactly +// one writer. type FilesReconciler struct{} const filesID = "files" diff --git a/apps/stackpanel-go/pkg/envvars/envvars.go b/apps/stackpanel-go/pkg/envvars/envvars.go index 09f8749d..8b0d6bf3 100644 --- a/apps/stackpanel-go/pkg/envvars/envvars.go +++ b/apps/stackpanel-go/pkg/envvars/envvars.go @@ -1,11 +1,7 @@ // Package envvars provides centralized environment variable definitions for Stackpanel. // -// This package is the Go counterpart to nix/stackpanel/core/lib/envvars.nix. // It provides a single source of truth for all environment variables used by // the Stackpanel CLI and related tools. -// -// IMPORTANT: When adding new environment variables, also update envvars.nix -// to keep Nix and Go definitions in sync. package envvars import ( diff --git a/apps/web/.stackpanel/gen/ide/vscode/devshell-loader.sh b/apps/web/.stackpanel/gen/ide/vscode/devshell-loader.sh deleted file mode 100755 index bd624696..00000000 --- a/apps/web/.stackpanel/gen/ide/vscode/devshell-loader.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# Devshell loader for VS Code integrated terminal -# This script is sourced by VS Code to enter the devenv shell - -# Find the project root (where this script lives relative to) -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)" - -cd "$PROJECT_ROOT" - -# Check if devenv is available -if command -v devenv &> /dev/null; then - # Enter devenv shell - eval "$(devenv print-dev-env)" -elif [ -f .envrc ]; then - # Fallback to direnv - if command -v direnv &> /dev/null; then - eval "$(direnv export bash)" - fi -fi diff --git a/apps/web/.stackpanel/gen/ide/vscode/stackpanel.code-workspace b/apps/web/.stackpanel/gen/ide/vscode/stackpanel.code-workspace deleted file mode 100644 index a455c820..00000000 --- a/apps/web/.stackpanel/gen/ide/vscode/stackpanel.code-workspace +++ /dev/null @@ -1,46 +0,0 @@ -{ - "extensions": { - "recommendations": ["redhat.vscode-yaml"] - }, - "folders": [ - { - "path": "../../../.." - } - ], - "settings": { - "terminal.integrated.defaultProfile.linux": "devenv", - "terminal.integrated.defaultProfile.osx": "devenv", - "terminal.integrated.profiles.linux": { - "devenv": { - "args": [ - "-c", - "source ${workspaceFolder}/.stackpanel/gen/ide/vscode/devshell-loader.sh \u0026\u0026 exec bash" - ], - "path": "/bin/bash" - } - }, - "terminal.integrated.profiles.osx": { - "devenv": { - "args": [ - "-c", - "source ${workspaceFolder}/.stackpanel/gen/ide/vscode/devshell-loader.sh \u0026\u0026 exec zsh" - ], - "path": "/bin/zsh" - } - }, - "yaml.completion": true, - "yaml.hover": true, - "yaml.schemas": { - "./.stackpanel/gen/schemas/secrets/app-config.schema.json": ".stackpanel/secrets/apps/*/config.yaml", - "./.stackpanel/gen/schemas/secrets/config.schema.json": ".stackpanel/secrets/config.yaml", - "./.stackpanel/gen/schemas/secrets/env.schema.json": [ - ".stackpanel/secrets/apps/*/dev.yaml", - ".stackpanel/secrets/apps/*/staging.yaml", - ".stackpanel/secrets/apps/*/prod.yaml" - ], - "./.stackpanel/gen/schemas/secrets/schema.schema.json": ".stackpanel/secrets/apps/*/common.yaml", - "./.stackpanel/gen/schemas/secrets/users.schema.json": ".stackpanel/secrets/users.yaml" - }, - "yaml.validate": true - } -} diff --git a/apps/web/.stackpanel/gen/schemas/secrets/app-config.schema.json b/apps/web/.stackpanel/gen/schemas/secrets/app-config.schema.json deleted file mode 100644 index 9de933b2..00000000 --- a/apps/web/.stackpanel/gen/schemas/secrets/app-config.schema.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "description": "Per-app secrets and codegen config (.stackpanel/secrets/apps/*/config.yaml)", - "properties": { - "codegen": { - "additionalProperties": false, - "description": "Code generation settings for type-safe env access", - "properties": { - "language": { - "description": "Target language for generated code", - "enum": ["typescript", "python", "go"], - "type": "string" - }, - "path": { - "description": "Output path relative to project root (e.g., packages/api/src/env.ts)", - "type": "string" - } - }, - "required": ["language", "path"], - "type": "object" - } - }, - "title": "App Secrets Configuration", - "type": "object" -} diff --git a/apps/web/.stackpanel/gen/schemas/secrets/config.schema.json b/apps/web/.stackpanel/gen/schemas/secrets/config.schema.json deleted file mode 100644 index a5ca9cc2..00000000 --- a/apps/web/.stackpanel/gen/schemas/secrets/config.schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "description": "Global secrets configuration (.stackpanel/secrets/config.yaml)", - "properties": { - "backend": { - "default": "sops", - "description": "Secrets backend to use (sops or vals)", - "enum": ["sops", "vals"], - "type": "string" - }, - "default-environments": { - "default": ["dev", "staging", "prod"], - "description": "Default environments created for new apps", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "title": "Stackpanel Secrets Configuration", - "type": "object" -} diff --git a/apps/web/.stackpanel/gen/schemas/secrets/env.schema.json b/apps/web/.stackpanel/gen/schemas/secrets/env.schema.json deleted file mode 100644 index 44fbd3e5..00000000 --- a/apps/web/.stackpanel/gen/schemas/secrets/env.schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "description": "Environment-specific secrets (.stackpanel/secrets/apps/*/{dev,staging,prod}.yaml)", - "properties": { - "schema": { - "additionalProperties": { - "additionalProperties": false, - "properties": { - "default": { - "type": "string" - }, - "description": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "sensitive": { - "type": "boolean" - } - }, - "type": "object" - }, - "description": "Environment-specific schema overrides (merged with common.yaml)", - "type": "object" - }, - "users": { - "description": "Usernames (from users.yaml) who can access this environment", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "title": "Environment Secrets", - "type": "object" -} diff --git a/apps/web/.stackpanel/gen/schemas/secrets/schema.schema.json b/apps/web/.stackpanel/gen/schemas/secrets/schema.schema.json deleted file mode 100644 index d3e3d96d..00000000 --- a/apps/web/.stackpanel/gen/schemas/secrets/schema.schema.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": { - "additionalProperties": false, - "description": "Secret variable definition", - "properties": { - "default": { - "description": "Default value (only for non-sensitive secrets)", - "type": "string" - }, - "description": { - "description": "Human-readable description of this secret", - "type": "string" - }, - "required": { - "default": false, - "description": "Whether this secret must be set", - "type": "boolean" - }, - "sensitive": { - "default": true, - "description": "Whether to mask value in logs and output", - "type": "boolean" - } - }, - "type": "object" - }, - "description": "Schema for secret variables (.stackpanel/secrets/apps/*/common.yaml)", - "title": "Secret Schema", - "type": "object" -} diff --git a/apps/web/.stackpanel/gen/schemas/secrets/users.schema.json b/apps/web/.stackpanel/gen/schemas/secrets/users.schema.json deleted file mode 100644 index 7813353f..00000000 --- a/apps/web/.stackpanel/gen/schemas/secrets/users.schema.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": { - "additionalProperties": false, - "properties": { - "admin": { - "default": false, - "description": "Admins can decrypt secrets for all environments", - "type": "boolean" - }, - "github": { - "description": "GitHub username (for key lookup)", - "type": "string" - }, - "pubkey": { - "description": "AGE public key (starts with age1...)", - "pattern": "^age1[a-z0-9]{58}$", - "type": "string" - } - }, - "required": ["pubkey"], - "type": "object" - }, - "description": "Team members with access to secrets (.stackpanel/secrets/users.yaml)", - "title": "Stackpanel Users", - "type": "object" -} diff --git a/apps/web/.stackpanel/secrets/recipients/.sops.yaml b/apps/web/.stackpanel/secrets/recipients/.sops.yaml deleted file mode 100644 index a77cbe85..00000000 --- a/apps/web/.stackpanel/secrets/recipients/.sops.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# SOPS configuration for group encryption keys -# -# Auto-generated by stackpanel from .stackpanel/secrets/recipients/**/*.age.pub -# DO NOT EDIT - changes will be overwritten. To add recipients, add .age.pub files -# to the recipients directory and re-enter the devshell. - -creation_rules: - - path_regex: \.enc\.age$ - key_groups: - - age: - - age175t6gzk7pk70ltq7hm54yngkyytm36mxy0vz78296p49gcs0e5uq6emj46 diff --git a/apps/web/.stackpanel/secrets/recipients/cooper-maruyama.age.pub b/apps/web/.stackpanel/secrets/recipients/cooper-maruyama.age.pub deleted file mode 100644 index ab512041..00000000 --- a/apps/web/.stackpanel/secrets/recipients/cooper-maruyama.age.pub +++ /dev/null @@ -1 +0,0 @@ -age175t6gzk7pk70ltq7hm54yngkyytm36mxy0vz78296p49gcs0e5uq6emj46 diff --git a/apps/web/.stackpanel/secrets/vars/.sops.yaml b/apps/web/.stackpanel/secrets/vars/.sops.yaml deleted file mode 100644 index 94cf8282..00000000 --- a/apps/web/.stackpanel/secrets/vars/.sops.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Auto-generated from config.nix — DO NOT EDIT. -# Group public keys are defined in stackpanel.secrets.groups..age-pub. -# To change a group key, update config.nix and re-enter the devshell. -# -# Values preceded by a '# safe' comment are stored in plaintext. -# All other values are encrypted. -creation_rules: - - path_regex: ^dev\.sops\.yaml$ - unencrypted_comment_regex: "^# safe$" - key_groups: - - age: - - age1783kahlc7yxv2md9vtpx4wq899csvqxty03fatcs5s7lqfh5334s6p7r0l - - path_regex: ^prod\.sops\.yaml$ - unencrypted_comment_regex: "^# safe$" - key_groups: - - age: - - age1tvczw6y7g4v0ma7cn05adrnst9jnnsh9j8ge0t0flls8ucq5yg9qe37jhe - - path_regex: ^common\.sops\.yaml$ - unencrypted_comment_regex: "^# safe$" - key_groups: - - age: - - age1783kahlc7yxv2md9vtpx4wq899csvqxty03fatcs5s7lqfh5334s6p7r0l - - age1tvczw6y7g4v0ma7cn05adrnst9jnnsh9j8ge0t0flls8ucq5yg9qe37jhe diff --git a/apps/web/.stackpanel/state/keys/local.pub b/apps/web/.stackpanel/state/keys/local.pub deleted file mode 100644 index ab512041..00000000 --- a/apps/web/.stackpanel/state/keys/local.pub +++ /dev/null @@ -1 +0,0 @@ -age175t6gzk7pk70ltq7hm54yngkyytm36mxy0vz78296p49gcs0e5uq6emj46 diff --git a/apps/web/.stackpanel/state/stackpanel.json b/apps/web/.stackpanel/state/stackpanel.json deleted file mode 100644 index 5283785f..00000000 --- a/apps/web/.stackpanel/state/stackpanel.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "apps": { - "docs": { - "port": 6400, - "domain": "docs.localhost", - "url": "http://docs.localhost", - "tls": false - }, - "server": { - "port": 6401, - "tls": false - }, - "web": { - "port": 6402, - "domain": "stackpanel.localhost", - "url": "http://stackpanel.localhost", - "tls": false - } - }, - "basePort": 6400, - "network": { - "step": { - "enable": true - } - }, - "paths": { - "state": ".stackpanel/state", - "gen": ".stackpanel/gen", - "data": ".stackpanel" - }, - "projectName": "stackpanel", - "services": { - "minio": { - "key": "MINIO", - "name": "Minio", - "port": 6412, - "envVar": "STACKPANEL_MINIO_PORT" - }, - "minio_console": { - "key": "MINIO_CONSOLE", - "name": "Minio Console", - "port": 6413, - "envVar": "STACKPANEL_MINIO_CONSOLE_PORT" - }, - "postgres": { - "key": "POSTGRES", - "name": "PostgreSQL", - "port": 6410, - "envVar": "STACKPANEL_POSTGRES_PORT" - }, - "redis": { - "key": "REDIS", - "name": "Redis", - "port": 6411, - "envVar": "STACKPANEL_REDIS_PORT" - } - }, - "version": 1 -} diff --git a/docs/colmena.md b/docs/colmena.md deleted file mode 100644 index db9c9fc6..00000000 --- a/docs/colmena.md +++ /dev/null @@ -1,8066 +0,0 @@ -# Colmena: StackPanel module for options config UI - -**Session ID:** ses_357e0f475ffe5p71rGzRuGiQLc -**Created:** 2/28/2026, 10:38:52 PM -**Updated:** 2/28/2026, 10:46:32 PM - ---- - -## User - -add a stack module for configuring all colmena options. it should include the UI definitions to manage it via the UI - ---- - -## Assistant (Build · gpt-5.3-codex · 4.7s) - -_Thinking:_ - - - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "in_progress", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "pending", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "pending", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "in_progress", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "pending", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "pending", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.4s) - -_Thinking:_ - - - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "nix/stack/modules/*" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo.test.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/.app-module-template.nix -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "nix/stack/modules/**/{meta.nix,module.nix,ui.nix}" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/env-codegen/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/env-codegen/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/git-hooks/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/git-hooks/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/git-hooks/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/meta.nix -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/docs/content/**/*colmena*" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.0s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/default.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # modules/default.nix - Module Discovery and Auto-Import -00003| # -00004| # This file handles module imports in two ways: -00005| # -00006| # 1. AUTO-DISCOVERY: Directory modules (new structure) are auto-imported -00007| # - module-name/default.nix -00008| # - Excludes directories starting with _ or . -00009| # -00010| # 2. EXPLICIT IMPORTS: Legacy single-file modules are imported explicitly -00011| # - These are listed below and should be migrated to directory structure -00012| # -00013| # The `_template/` directory provides the template for new modules. -00014| # -00015| # For fast metadata access without full evaluation, use: -00016| # config.stack._moduleMetas. -00017| # ============================================================================== -00018| { -00019| lib, -00020| config, -00021| ... -00022| }: -00023| let -00024| # Read the modules directory -00025| modulesDir = ./.; -00026| dirContents = builtins.readDir modulesDir; -00027| -00028| # --------------------------------------------------------------------------- -00029| # Auto-discovery: Only import DIRECTORY modules (new structure) -00030| # --------------------------------------------------------------------------- -00031| # Exclude: -00032| # - _template, _*, .* (hidden/template directories) -00033| # - Files (legacy modules are imported explicitly below) -00034| isAutoImportable = name: type: -00035| let -00036| isHidden = lib.hasPrefix "_" name || lib.hasPrefix "." name; -00037| isDirectory = type == "directory"; -00038| in -00039| isDirectory && !isHidden; -00040| -00041| autoImportDirs = lib.filterAttrs isAutoImportable dirContents; -00042| autoImports = lib.mapAttrsToList (name: _: ./${name}) autoImportDirs; -00043| -00044| # --------------------------------------------------------------------------- -00045| # Explicit imports: Legacy single-file modules -00046| # --------------------------------------------------------------------------- -00047| # These modules should be migrated to directory structure over time. -00048| # Do NOT add new modules here - create a directory module instead. -00049| # -00050| # As of 2025-01-24, all legacy modules have been migrated to directory structure: -00051| # - git-hooks -> git-hooks/ -00052| # - ci-formatters -> ci-formatters/ -00053| # - go -> go/ -00054| # - app-commands -> app-commands/ -00055| # - entrypoints -> entrypoints/ -00056| # - process-compose -> process-compose/ -00057| # - turbo -> turbo/ -00058| # -00059| # NOTE: bun.nix is excluded - it has conflicts and was never imported -00060| # NOTE: devenv-*.nix are excluded - they require devenvSchema via wrapDevenv -00061| legacyImports = [ -00062| ]; -00063| -00064| # --------------------------------------------------------------------------- -00065| # Fast Metadata Discovery -00066| # --------------------------------------------------------------------------- -00067| # Read meta.nix from each directory module for fast access without full eval. -00068| # This is useful for the agent to list available modules quickly. -00069| -00070| # Try to load meta.nix from each directory, returning null if it doesn't exist -00071| tryLoadMeta = name: -00072| let -00073| metaPath = ./${name}/meta.nix; -00074| in -00075| if builtins.pathExists metaPath -00076| then import metaPath -00077| else null; -00078| -00079| moduleMetas = lib.filterAttrs (_: v: v != null) ( -00080| lib.mapAttrs (name: _: tryLoadMeta name) autoImportDirs -00081| ); -00082| -00083| in -00084| { -00085| imports = autoImports ++ legacyImports; -00086| -00087| # Expose module metadata for fast discovery -00088| # Access via: config.stack._moduleMetas.oxlint.name -00089| config.stack._moduleMetas = moduleMetas; -00090| } -00091| - -(End of file - total 91 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/meta.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # meta.nix - Module Metadata -00003| # -00004| # This file contains static metadata that can be read without evaluating the -00005| # full module. This enables fast module discovery for the UI. -00006| # -00007| # IMPORTANT: This file should contain ONLY pure data - no imports, no lib calls, -00008| # no function definitions. This allows it to be read with builtins.import -00009| # without any module system evaluation. -00010| # ============================================================================== -00011| { -00012| # Unique identifier (should match directory name) -00013| id = "my-module"; -00014| -00015| # Display name -00016| name = "My Module"; -00017| -00018| # Short description (shown in module list) -00019| description = "A brief description of what this module does"; -00020| -00021| # Category for grouping in the UI -00022| # Options: development | database | infrastructure | monitoring | secrets | ci-cd | deployment | language | service | integration -00023| category = "development"; -00024| -00025| # Semantic version -00026| version = "1.0.0"; -00027| -00028| # Lucide icon name (see: https://lucide.dev/icons) -00029| icon = "puzzle"; -00030| -00031| # Link to documentation -00032| homepage = null; -00033| -00034| # Author or team -00035| author = "Stack"; -00036| -00037| # Searchable tags -00038| tags = [ ]; -00039| -00040| # Module dependencies (other module IDs that must be enabled) -00041| requires = [ ]; -00042| -00043| # Conflicting modules (cannot be enabled together) -00044| conflicts = [ ]; -00045| -00046| # Feature flags - declare what this module provides -00047| # Used for UI filtering and display -00048| features = { -00049| files = false; # Generates config files -00050| scripts = false; # Provides shell commands -00051| healthchecks = false; # Has health checks -00052| packages = false; # Adds packages to devshell -00053| services = false; # Runs background services -00054| secrets = false; # Manages secrets -00055| tasks = false; # Provides turbo/build tasks -00056| appModule = false; # Adds per-app configuration options -00057| }; -00058| -00059| # Priority for ordering (lower = higher priority) -00060| priority = 100; -00061| -00062| # Configuration boilerplate to inject into user's config.nix when module is installed -00063| # This should be a commented-out example config that users can uncomment/modify -00064| # The boilerplate will be injected into the "# STACKPANEL_MODULES_BEGIN" section -00065| configBoilerplate = '' -00066| # ${meta.name} - ${meta.description} -00067| # See: ${meta.homepage or "https://stack.dev/docs/modules/${meta.id}"} -00068| # my-module = { -00069| # enable = true; -00070| # # Add your configuration here -00071| # }; -00072| ''; -00073| } -00074| - -(End of file - total 74 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # module.nix - Module Implementation -00003| # -00004| # This is a standard NixOS-style module that defines options and configuration. -00005| # For complex modules, you can split this into options.nix and config.nix. -00006| # -00007| # The module should: -00008| # 1. Define options under stack.modules..* -00009| # 2. Define per-app options via appModules if needed -00010| # 3. Implement config = lib.mkIf cfg.enable { ... } -00011| # 4. Define flake checks for CI (required for certification) -00012| # 5. Define health checks for runtime monitoring -00013| # -00014| # For large modules with many checks, consider splitting checks into checks.nix -00015| # ============================================================================== -00016| { -00017| lib, -00018| config, -00019| pkgs, -00020| ... -00021| }: -00022| let -00023| # Import metadata for reference -00024| meta = import ./meta.nix; -00025| -00026| # Shorthand for this module's config -00027| cfg = config.stack.modules.${meta.id}; -00028| -00029| # Shorthand for stack config -00030| sp = config.stack; -00031| -00032| # --------------------------------------------------------------------------- -00033| # Per-app options module (if this module adds per-app configuration) -00034| # --------------------------------------------------------------------------- -00035| # Uncomment and customize if you need per-app options: -00036| # -00037| # appModule = { lib, name, ... }: { -00038| # options.myModule = { -00039| # enable = lib.mkEnableOption "Enable ${meta.name} for this app"; -00040| # # Add more per-app options here -00041| # }; -00042| # }; -00043| # -00044| # Then add to config below: -00045| # stack.appModules = [ appModule ]; -00046| -00047| in -00048| { -00049| # =========================================================================== -00050| # Options -00051| # =========================================================================== -00052| options.stack.modules.${meta.id} = { -00053| enable = lib.mkEnableOption meta.description; -00054| -00055| # Add module-specific options here -00056| # Example: -00057| # package = lib.mkOption { -00058| # type = lib.types.package; -00059| # default = pkgs.my-package; -00060| # description = "The package to use"; -00061| # }; -00062| }; -00063| -00064| # =========================================================================== -00065| # Configuration -00066| # =========================================================================== -00067| config = lib.mkIf (sp.enable && cfg.enable) { -00068| # ------------------------------------------------------------------------- -00069| # Packages - Add packages to devshell -00070| # ------------------------------------------------------------------------- -00071| # stack.devshell.packages = [ pkgs.my-package ]; -00072| -00073| # ------------------------------------------------------------------------- -00074| # Files - Generate configuration files -00075| # ------------------------------------------------------------------------- -00076| # stack.files.entries = { -00077| # "path/to/config.json" = { -00078| # type = "text"; -00079| # text = builtins.toJSON { key = "value"; }; -00080| # description = "Configuration file for ${meta.name}"; -00081| # source = meta.id; -00082| # }; -00083| # }; -00084| -00085| # ------------------------------------------------------------------------- -00086| # Scripts - Add shell commands -00087| # ------------------------------------------------------------------------- -00088| # stack.scripts = { -00089| # my-command = { -00090| # exec = '' -00091| # echo "Hello from ${meta.name}" -00092| # ''; -00093| # description = "Run ${meta.name}"; -00094| # }; -00095| # }; -00096| -00097| # ========================================================================= -00098| # Flake Checks (CI) - Run with `nix flake check` -00099| # ========================================================================= -00100| # These checks run in CI and are required for module certification. -00101| # Categories: -00102| # - eval: Module evaluates (REQUIRED for certification) -00103| # - packages: Dependencies available (REQUIRED for certification) -00104| # - config: Config generation works (recommended) -00105| # - integration: Works with sample project (recommended) -00106| # - lint: Code passes linting (optional) -00107| # - custom.*: Module-specific checks (optional) -00108| # -00109| # For large modules, consider moving checks to a separate checks.nix file. -00110| # ------------------------------------------------------------------------- -00111| stack.moduleChecks.${meta.id} = { -00112| # REQUIRED: Verify module evaluates without errors -00113| eval = { -00114| description = "${meta.name} module evaluates correctly"; -00115| required = true; -00116| derivation = pkgs.runCommand "${meta.id}-eval-check" {} '' -00117| echo "✓ Module ${meta.name} evaluates successfully" -00118| touch $out -00119| ''; -00120| }; -00121| -00122| # REQUIRED: Verify required packages are available -00123| packages = { -00124| description = "${meta.name} packages are available"; -00125| required = true; -00126| derivation = pkgs.runCommand "${meta.id}-packages-check" { -00127| # nativeBuildInputs = [ pkgs.my-package ]; -00128| } '' -00129| # my-package --version -00130| echo "✓ All required packages available" -00131| touch $out -00132| ''; -00133| }; -00134| -00135| # RECOMMENDED: Verify config generation works -00136| # config = { -00137| # description = "${meta.name} config generation works"; -00138| # required = false; -00139| # derivation = pkgs.runCommand "${meta.id}-config-check" {} '' -00140| # echo '${builtins.toJSON { example = "config"; }}' | ${pkgs.jq}/bin/jq . -00141| # echo "✓ Config generation works" -00142| # touch $out -00143| # ''; -00144| # }; -00145| -00146| # OPTIONAL: Module-specific checks -00147| # custom = { -00148| # my-custom-check = { -00149| # description = "Custom check for ${meta.name}"; -00150| # required = false; -00151| # derivation = pkgs.runCommand "${meta.id}-custom-check" {} '' -00152| # echo "✓ Custom check passed" -00153| # touch $out -00154| # ''; -00155| # }; -00156| # }; -00157| }; -00158| -00159| # ========================================================================= -00160| # Health Checks (Runtime) - Shown in UI, run in devshell -00161| # ========================================================================= -00162| # These checks run at runtime to verify the module is working correctly. -00163| # They are displayed in the Stack UI and can be run manually. -00164| # ------------------------------------------------------------------------- -00165| stack.healthchecks.modules.${meta.id} = { -00166| enable = true; -00167| displayName = meta.name; -00168| checks = { -00169| installed = { -00170| description = "${meta.name} is installed and accessible"; -00171| script = '' -00172| # command -v my-command >/dev/null 2>&1 && my-command --version -00173| echo "Check not implemented" -00174| exit 0 -00175| ''; -00176| severity = "critical"; -00177| timeout = 5; -00178| }; -00179| # Add more runtime health checks as needed -00180| # config-valid = { -00181| # description = "Configuration is valid"; -00182| # script = '' -00183| # test -f "$STACKPANEL_ROOT/path/to/config.json" -00184| # ''; -00185| # severity = "warning"; -00186| # timeout = 5; -00187| # }; -00188| }; -00189| }; -00190| -00191| # ------------------------------------------------------------------------- -00192| # Module Registration - Required for UI discovery -00193| # ------------------------------------------------------------------------- -00194| stack.modules.${meta.id} = { -00195| enable = true; -00196| inherit meta; -00197| source.type = "builtin"; -00198| features = meta.features; -00199| tags = meta.tags; -00200| priority = meta.priority; -00201| healthcheckModule = meta.id; -00202| }; -00203| }; -00204| } -00205| - -(End of file - total 205 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - UI Panel Definitions -00003| # -00004| # This file defines the UI panels and fields that appear in the Stack UI. -00005| # It's evaluated lazily - only when the UI needs to display this module's panels. -00006| # -00007| # Panel Types: -00008| # - PANEL_TYPE_STATUS: Show status/health information -00009| # - PANEL_TYPE_FORM: Editable configuration form -00010| # - PANEL_TYPE_TABLE: Tabular data display -00011| # - PANEL_TYPE_APPS_GRID: Grid of app cards -00012| # - PANEL_TYPE_CUSTOM: Custom rendering (provide component name) -00013| # -00014| # Field Types: -00015| # - FIELD_TYPE_STRING: Text input -00016| # - FIELD_TYPE_NUMBER: Numeric input -00017| # - FIELD_TYPE_BOOLEAN: Toggle/checkbox -00018| # - FIELD_TYPE_SELECT: Single-select dropdown -00019| # - FIELD_TYPE_MULTISELECT: Multi-select dropdown -00020| # - FIELD_TYPE_APP_FILTER: App selector -00021| # - FIELD_TYPE_COLUMNS: Multi-column layout -00022| # - FIELD_TYPE_JSON: Raw JSON data -00023| # ============================================================================== -00024| { -00025| lib, -00026| config, -00027| ... -00028| }: -00029| let -00030| meta = import ./meta.nix; -00031| cfg = config.stack.modules.${meta.id}; -00032| in -00033| lib.mkIf cfg.enable { -00034| # Register panel(s) for this module -00035| stack.panels."${meta.id}-status" = { -00036| module = meta.id; -00037| title = "${meta.name} Status"; -00038| description = meta.description; -00039| type = "PANEL_TYPE_STATUS"; -00040| order = meta.priority; -00041| fields = [ -00042| # Example: Show enabled status -00043| { -00044| name = "enabled"; -00045| type = "FIELD_TYPE_BOOLEAN"; -00046| value = "true"; -00047| } -00048| # Example: Show version -00049| # { -00050| # name = "version"; -00051| # type = "FIELD_TYPE_STRING"; -00052| # value = meta.version; -00053| # } -00054| # Example: Show configured apps (if appModule is used) -00055| # { -00056| # name = "apps"; -00057| # type = "FIELD_TYPE_JSON"; -00058| # value = builtins.toJSON (lib.attrNames enabledApps); -00059| # } -00060| ]; -00061| }; -00062| -00063| # Example: Configuration form panel -00064| # stack.panels."${meta.id}-config" = { -00065| # module = meta.id; -00066| # title = "${meta.name} Configuration"; -00067| # type = "PANEL_TYPE_FORM"; -00068| # order = meta.priority + 1; -00069| # fields = [ -00070| # { -00071| # name = "option1"; -00072| # type = "FIELD_TYPE_STRING"; -00073| # label = "Option 1"; -00074| # value = cfg.option1 or ""; -00075| # } -00076| # { -00077| # name = "option2"; -00078| # type = "FIELD_TYPE_BOOLEAN"; -00079| # label = "Enable Feature"; -00080| # value = if cfg.enableFeature then "true" else "false"; -00081| # } -00082| # ]; -00083| # }; -00084| } -00085| - -(End of file - total 85 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # module.nix - Turbo Module Implementation -00003| # -00004| # Turborepo integration for stack. -00005| # -00006| # This module: -00007| # 1. Generates turbo.json from stack.tasks -00008| # 2. Compiles task scripts with `exec` to Nix derivations -00009| # 3. Creates symlinks in .tasks/bin/ for Turborepo to invoke -00010| # 4. Generates package.json script entries -00011| # 5. Handles per-app task overrides via stack.apps.*.tasks -00012| # -00013| # Architecture: -00014| # - Tasks with `exec` become writeShellApplication derivations -00015| # - Derivations are symlinked to .tasks/bin/ -00016| # - package.json scripts call ./.tasks/bin/ -00017| # - turbo.json references task names with deps/outputs/caching -00018| # -00019| # Usage: -00020| # stack.tasks = { -00021| # build = { -00022| # exec = "npm run compile"; -00023| # after = [ "deps" "^build" ]; -00024| # outputs = [ "dist/**" ]; -00025| # runtimeInputs = [ pkgs.nodejs ]; -00026| # }; -00027| # dev = { -00028| # persistent = true; -00029| # cache = false; -00030| # }; -00031| # }; -00032| # ============================================================================== -00033| { -00034| lib, -00035| config, -00036| pkgs, -00037| ... -00038| }: -00039| let -00040| meta = import ./meta.nix; -00041| cfg = config.stack; -00042| tasksCfg = cfg.tasks; -00043| # NOTE: Do NOT define appsCfg here - it causes infinite recursion when -00044| # combined with appModules. Access cfg.apps only inside mkIf blocks. -00045| -00046| # --------------------------------------------------------------------------- -00047| # Per-app task options module (added via appModules) -00048| # --------------------------------------------------------------------------- -00049| db = import ../../db { inherit lib; }; -00050| -00051| # Per-app task submodule (reuses same schema as workspace tasks) -00052| appTaskSubmodule = lib.types.submoduleWith { -00053| modules = [ -00054| # Proto-derived options (exec, description, outputs, inputs, etc.) -00055| { options = db.asOptions db.extend.task; } -00056| # Nix-only runtime options (dependsOn, before, runtimeInputs) -00057| # Note: 'package' option is omitted for per-app tasks since scope is implicit -00058| { -00059| options = { -00060| dependsOn = lib.mkOption { -00061| type = lib.types.listOf lib.types.str; -00062| default = [ ]; -00063| description = '' -00064| Tasks that must complete before this task runs. -00065| Use `^taskname` to reference the same task in dependencies. -00066| ''; -00067| }; -00068| -00069| before = lib.mkOption { -00070| type = lib.types.listOf lib.types.str; -00071| default = [ ]; -00072| description = '' -00073| Tasks that depend on this task completing first. -00074| Automatically adds this task to the target tasks' dependsOn. -00075| ''; -00076| }; -00077| -00078| runtimeInputs = lib.mkOption { -00079| type = lib.types.listOf lib.types.package; -00080| default = [ ]; -00081| description = '' -00082| Packages to include in PATH when running the task script. -00083| Used with `exec` to create hermetic writeShellApplication derivations. -00084| ''; -00085| }; -00086| }; -00087| } -00088| ]; -00089| specialArgs = { inherit lib; }; -00090| }; -00091| -00092| taskAppModule = -00093| { lib, ... }: -00094| { -00095| options.turbo = { -00096| enable = lib.mkOption { -00097| type = lib.types.bool; -00098| default = true; -00099| description = "Whether to include this app in turbo.json generation."; -00100| }; -00101| }; -00102| -00103| options.tasks = lib.mkOption { -00104| type = lib.types.attrsOf appTaskSubmodule; -00105| default = { }; -00106| description = '' -00107| Per-app task definitions for Turborepo. -00108| -00109| These generate: -00110| - Per-package turbo.json with task overrides -00111| - Task scripts in /.tasks/bin/ -00112| -00113| Options mirror workspace tasks but are scoped to this app. -00114| ''; -00115| example = lib.literalExpression '' -00116| { -00117| build = { -00118| exec = "bun run build"; -00119| dependsOn = [ "^build" ]; -00120| outputs = [ "dist/**" ]; -00121| }; -00122| dev = { -00123| exec = "bun run dev"; -00124| persistent = true; -00125| cache = false; -00126| }; -00127| } -00128| ''; -00129| }; -00130| }; -00131| -00132| # --------------------------------------------------------------------------- -00133| # Helper: Create task script derivation -00134| # --------------------------------------------------------------------------- -00135| mkTaskScript = -00136| taskName: taskCfg: -00137| let -00138| hasExec = taskCfg.exec or null != null; -00139| in -00140| if !hasExec then -00141| null -00142| else -00143| pkgs.writeShellApplication { -00144| name = taskName; -00145| runtimeInputs = taskCfg.runtimeInputs or [ ]; -00146| text = '' -00147| # Task: ${taskName} -00148| ${lib.optionalString (taskCfg.description or null != null) "# ${taskCfg.description}"} -00149| -00150| # Change to working directory if specified -00151| ${lib.optionalString (taskCfg.cwd or null != null) '' -00152| cd "''${STACKPANEL_ROOT:-$(pwd)}/${taskCfg.cwd}" -00153| ''} -00154| -00155| # Set task-specific environment variables -00156| ${lib.concatStringsSep "\n" ( -00157| lib.mapAttrsToList (k: v: "export ${k}=${lib.escapeShellArg v}") (taskCfg.env or { }) -00158| )} -00159| -00160| # Execute task -00161| ${taskCfg.exec} -00162| ''; -00163| }; -00164| -00165| # --------------------------------------------------------------------------- -00166| # Helper: Create app-level task script derivation -00167| # --------------------------------------------------------------------------- -00168| mkAppTaskScript = -00169| appName: appCfg: taskName: taskCfg: -00170| let -00171| hasExec = taskCfg.exec or null != null; -00172| appPath = appCfg.path or "apps/${appName}"; -00173| in -00174| if !hasExec then -00175| null -00176| else -00177| pkgs.writeShellApplication { -00178| name = "${appName}-${taskName}"; -00179| runtimeInputs = taskCfg.runtimeInputs or [ ]; -00180| text = '' -00181| # App: ${appName}, Task: ${taskName} -00182| ${lib.optionalString (taskCfg.description or null != null) "# ${taskCfg.description}"} -00183| -00184| # Change to app directory -00185| cd "''${STACKPANEL_ROOT:-$(pwd)}/${taskCfg.cwd or appPath}" -00186| -00187| # Set task-specific environment variables -00188| ${lib.concatStringsSep "\n" ( -00189| lib.mapAttrsToList (k: v: "export ${k}=${lib.escapeShellArg v}") (taskCfg.env or { }) -00190| )} -00191| -00192| # Execute task -00193| ${taskCfg.exec} -00194| ''; -00195| }; -00196| -00197| # --------------------------------------------------------------------------- -00198| # Compute reverse dependencies (before -> dependsOn) -00199| # --------------------------------------------------------------------------- -00200| # Build a map: taskName -> list of tasks that should run before it -00201| computeReverseDeps = -00202| tasks: -00203| let -00204| # For each task, if it has `before = [ "x" "y" ]`, add this task to x and y's deps -00205| addReverseDeps = -00206| acc: taskName: taskCfg: -00207| lib.foldl' ( -00208| innerAcc: targetTask: -00209| innerAcc -00210| // { -00211| ${targetTask} = (innerAcc.${targetTask} or [ ]) ++ [ taskName ]; -00212| } -00213| ) acc (taskCfg.before or [ ]); -00214| in -00215| lib.foldl' (acc: taskName: addReverseDeps acc taskName tasks.${taskName}) { } (lib.attrNames tasks); -00216| -00217| reverseDeps = computeReverseDeps tasksCfg; -00218| -00219| # --------------------------------------------------------------------------- -00220| # Generate turbo.json task entry -00221| # --------------------------------------------------------------------------- -00222| mkTurboTask = -00223| taskName: taskCfg: -00224| let -00225| # Combine explicit `dependsOn` with reverse deps from other tasks' `before` -00226| explicitDeps = taskCfg.dependsOn or [ ]; -00227| reverseDepsForTask = reverseDeps.${taskName} or [ ]; -00228| allDeps = explicitDeps ++ reverseDepsForTask; -00229| -00230| # Build the task config, omitting empty/default values -00231| taskConfig = -00232| { } -00233| // lib.optionalAttrs (allDeps != [ ]) { dependsOn = allDeps; } -00234| // lib.optionalAttrs ((taskCfg.outputs or [ ]) != [ ]) { outputs = taskCfg.outputs; } -00235| // lib.optionalAttrs ((taskCfg.inputs or [ ]) != [ ]) { inputs = taskCfg.inputs; } -00236| // lib.optionalAttrs (taskCfg.cache or null == false) { cache = false; } -00237| // lib.optionalAttrs (taskCfg.persistent or null == true) { persistent = true; } -00238| // lib.optionalAttrs (taskCfg.interactive or null == true) { interactive = true; }; -00239| in -00240| taskConfig; -00241| -00242| # --------------------------------------------------------------------------- -00243| # Generate turbo.json task key (with optional package scope) -00244| # If task has `package` attribute, use "package#taskName" format -00245| # --------------------------------------------------------------------------- -00246| mkTurboTaskKey = -00247| taskName: taskCfg: -00248| if taskCfg.package or null != null then "${taskCfg.package}#${taskName}" else taskName; -00249| -00250| # --------------------------------------------------------------------------- -00251| # Generate workspace-level turbo.json -00252| # Tasks with `package` attribute get scoped keys (e.g., "@pkg/name#task") -00253| # --------------------------------------------------------------------------- -00254| turboConfig = { -00255| "$schema" = "https://turbo.build/schema.json"; -00256| ui = "tui"; -00257| tasks = lib.listToAttrs ( -00258| lib.mapAttrsToList (taskName: taskCfg: { -00259| name = mkTurboTaskKey taskName taskCfg; -00260| value = mkTurboTask taskName taskCfg; -00261| }) tasksCfg -00262| ); -00263| }; -00264| -00265| turboJsonText = builtins.toJSON turboConfig; -00266| -00267| # --------------------------------------------------------------------------- -00268| # Generate task scripts and symlink file entries -00269| # --------------------------------------------------------------------------- -00270| taskScripts = lib.filterAttrs (_: v: v != null) (lib.mapAttrs mkTaskScript tasksCfg); -00271| -00272| # File entries for .tasks/bin/ symlinks -00273| taskSymlinkEntries = lib.mapAttrs' (taskName: scriptDrv: { -00274| name = ".tasks/bin/${taskName}"; -00275| value = { -00276| type = "symlink"; -00277| target = "${scriptDrv}/bin/${taskName}"; -00278| source = meta.id; -00279| description = "Task script for ${taskName}"; -00280| }; -00281| }) taskScripts; -00282| -00283| # --------------------------------------------------------------------------- -00284| # Helper functions for per-app turbo.json and task scripts -00285| # NOTE: These are functions, not values - they're called lazily inside mkIf -00286| # --------------------------------------------------------------------------- -00287| -00288| # Per-app task scripts -00289| mkAppTaskScripts = -00290| appName: appCfg: -00291| let -00292| appTasks = appCfg.tasks or { }; -00293| in -00294| lib.filterAttrs (_: v: v != null) (lib.mapAttrs (mkAppTaskScript appName appCfg) appTasks); -00295| -00296| # Per-app turbo.json content -00297| mkAppTurboConfig = -00298| appName: appCfg: -00299| let -00300| appTasks = appCfg.tasks or { }; -00301| appTaskConfigs = lib.mapAttrs ( -00302| taskName: taskCfg: -00303| let -00304| explicitDeps = taskCfg.dependsOn or [ ]; -00305| taskConfig = -00306| { } -00307| // lib.optionalAttrs (explicitDeps != [ ]) { dependsOn = explicitDeps; } -00308| // lib.optionalAttrs ((taskCfg.outputs or [ ]) != [ ]) { outputs = taskCfg.outputs; }; -00309| in -00310| taskConfig -00311| ) appTasks; -00312| in -00313| { -00314| extends = [ "//" ]; -00315| tasks = appTaskConfigs; -00316| }; -00317| -00318| # Per-app file entries generator -00319| mkAppFileEntries = -00320| appsWithTasks: appTaskScripts: appTurboConfigs: appName: appCfg: -00321| let -00322| appPath = appCfg.path or "apps/${appName}"; -00323| scripts = appTaskScripts.${appName} or { }; -00324| -00325| # turbo.json for this app -00326| turboEntry = { -00327| "${appPath}/turbo.json" = { -00328| type = "text"; -00329| text = builtins.toJSON (appTurboConfigs.${appName}); -00330| source = meta.id; -00331| description = "Per-package turbo.json for ${appName}"; -00332| }; -00333| }; -00334| -00335| # .tasks/bin/ symlinks for this app -00336| symlinkEntries = lib.mapAttrs' (taskName: scriptDrv: { -00337| name = "${appPath}/.tasks/bin/${taskName}"; -00338| value = { -00339| type = "symlink"; -00340| target = "${scriptDrv}/bin/${appName}-${taskName}"; -00341| source = meta.id; -00342| description = "Task script for ${appName}:${taskName}"; -00343| }; -00344| }) scripts; -00345| in -00346| turboEntry // symlinkEntries; -00347| -00348| # --------------------------------------------------------------------------- -00349| # Generate package.json script entries -00350| # Scripts are simple wrappers calling .tasks/bin/ -00351| # --------------------------------------------------------------------------- -00352| packageJsonScripts = lib.mapAttrs (taskName: _scriptDrv: "./.tasks/bin/${taskName}") taskScripts; -00353| -00354| # Check if we have any tasks defined -00355| hasTasks = tasksCfg != { }; -00356| -00357| in -00358| { -00359| # =========================================================================== -00360| # Options -00361| # =========================================================================== -00362| options.stack.turbo = { -00363| enable = lib.mkEnableOption "Turborepo integration" // { -00364| default = true; -00365| }; -00366| -00367| ui = lib.mkOption { -00368| type = lib.types.enum [ -00369| "tui" -00370| "stream" -00371| ]; -00372| default = "tui"; -00373| description = "Turborepo UI mode."; -00374| }; -00375| -00376| envMode = lib.mkOption { -00377| type = lib.types.nullOr ( -00378| lib.types.enum [ -00379| "strict" -00380| "loose" -00381| ] -00382| ); -00383| default = null; -00384| description = "Turborepo environment mode. If null, uses Turborepo default."; -00385| }; -00386| -00387| # Read-only computed outputs -00388| config = lib.mkOption { -00389| type = lib.types.attrs; -00390| readOnly = true; -00391| description = "Generated turbo.json configuration."; -00392| }; -00393| -00394| scripts = lib.mkOption { -00395| type = lib.types.attrsOf lib.types.package; -00396| readOnly = true; -00397| description = "Generated task script derivations."; -00398| }; -00399| -00400| packageJsonScripts = lib.mkOption { -00401| type = lib.types.attrsOf lib.types.str; -00402| readOnly = true; -00403| description = "Package.json script entries to merge."; -00404| }; -00405| }; -00406| -00407| # =========================================================================== -00408| # Config -00409| # =========================================================================== -00410| config = lib.mkMerge [ -00411| # Add per-app turbo options via appModules -00412| { -00413| stack.appModules = [ taskAppModule ]; -00414| } -00415| -00416| # When stack is enabled and has tasks, generate outputs -00417| (lib.mkIf (cfg.enable && hasTasks) { -00418| # Expose computed values -00419| stack.turbo.config = turboConfig; -00420| stack.turbo.scripts = taskScripts; -00421| stack.turbo.packageJsonScripts = packageJsonScripts; -00422| -00423| # Populate tasksComputed with generated derivations -00424| stack.tasksComputed = lib.mapAttrs (taskName: taskCfg: { -00425| script = taskScripts.${taskName} or null; -00426| turboConfig = mkTurboTask taskName taskCfg; -00427| dependsOn = (taskCfg.dependsOn or [ ]) ++ (reverseDeps.${taskName} or [ ]); -00428| }) tasksCfg; -00429| -00430| # Generate files via stack.files system (workspace-level only) -00431| stack.files.entries = lib.mkMerge [ -00432| # Root turbo.json -00433| { -00434| "turbo.json" = { -00435| type = "text"; -00436| text = turboJsonText; -00437| source = meta.id; -00438| description = "Turborepo pipeline configuration"; -00439| }; -00440| } -00441| -00442| # .tasks/bin/ symlinks for workspace-level tasks -00443| taskSymlinkEntries -00444| ]; -00445| -00446| # Add turbo to devshell packages -00447| stack.devshell.packages = [ pkgs.turbo ]; -00448| -00449| # Add .tasks/ to gitignore reminder in MOTD -00450| stack.motd.commands = lib.mkIf (taskScripts != { }) [ -00451| { -00452| name = "Tasks:"; -00453| description = lib.concatStringsSep ", " (lib.attrNames taskScripts); -00454| } -00455| ]; -00456| -00457| # Register module -00458| stack.modules.${meta.id} = { -00459| enable = true; -00460| meta = { -00461| name = meta.name; -00462| description = meta.description; -00463| icon = meta.icon; -00464| category = meta.category; -00465| author = meta.author; -00466| version = meta.version; -00467| homepage = meta.homepage; -00468| }; -00469| source.type = "builtin"; -00470| features = meta.features; -00471| tags = meta.tags; -00472| priority = meta.priority; -00473| }; -00474| }) -00475| -00476| # Per-app turbo files - computed lazily to avoid recursion with appModules -00477| ( -00478| let -00479| # Access cfg.apps here inside the config block, not at module top-level -00480| # Only include apps that have tasks defined (not null/empty) and turbo enabled -00481| # We check turbo.enable first to avoid evaluating tasks when turbo is disabled -00482| appsWithTasks = lib.filterAttrs ( -00483| _: appCfg: -00484| (appCfg.turbo.enable or true) && (appCfg ? tasks) && (appCfg.tasks != null) && (appCfg.tasks != { }) -00485| ) cfg.apps; -00486| -00487| hasAppsWithTasks = appsWithTasks != { }; -00488| -00489| # Compute app-specific values only when needed -00490| appTaskScripts = lib.mapAttrs mkAppTaskScripts appsWithTasks; -00491| appTurboConfigs = lib.mapAttrs mkAppTurboConfig appsWithTasks; -00492| appFileEntries = lib.foldl' ( -00493| acc: appName: -00494| acc -00495| // (mkAppFileEntries appsWithTasks appTaskScripts appTurboConfigs appName appsWithTasks.${appName}) -00496| ) { } (lib.attrNames appsWithTasks); -00497| in -00498| lib.mkIf (cfg.enable && hasAppsWithTasks) { -00499| stack.files.entries = appFileEntries; -00500| } -00501| ) -00502| ]; -00503| } -00504| - -(End of file - total 504 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Turbo UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| tasksCfg = cfg.tasks or { }; -00015| hasTasks = tasksCfg != { }; -00016| in -00017| lib.mkIf (cfg.enable && hasTasks) { -00018| stack.panels."${meta.id}-status" = { -00019| module = meta.id; -00020| title = "Turborepo"; -00021| description = meta.description; -00022| type = "PANEL_TYPE_STATUS"; -00023| order = meta.priority; -00024| fields = [ -00025| { -00026| name = "tasks"; -00027| type = "FIELD_TYPE_JSON"; -00028| value = builtins.toJSON (lib.attrNames tasksCfg); -00029| } -00030| { -00031| name = "ui"; -00032| type = "FIELD_TYPE_STRING"; -00033| value = cfg.turbo.ui or "tui"; -00034| } -00035| ]; -00036| }; -00037| } -00038| - -(End of file - total 38 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/meta.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # meta.nix - Turbo Module Metadata -00003| # -00004| # Static metadata for fast module discovery. Contains ONLY pure data. -00005| # ============================================================================== -00006| { -00007| # Unique identifier (matches directory name) -00008| id = "turbo"; -00009| -00010| # Display name -00011| name = "Turborepo"; -00012| -00013| # Short description -00014| description = "Turborepo task orchestration with turbo.json generation"; -00015| -00016| # Category for UI grouping -00017| category = "development"; -00018| -00019| # Semantic version -00020| version = "1.0.0"; -00021| -00022| # Lucide icon name -00023| icon = "rocket"; -00024| -00025| # Documentation link -00026| homepage = "https://turbo.build/repo"; -00027| -00028| # Author -00029| author = "Stack"; -00030| -00031| # Searchable tags -00032| tags = [ -00033| "turbo" -00034| "turborepo" -00035| "monorepo" -00036| "tasks" -00037| "pipeline" -00038| "build" -00039| ]; -00040| -00041| # Module dependencies -00042| requires = [ ]; -00043| -00044| # Conflicting modules -00045| conflicts = [ ]; -00046| -00047| # Feature flags -00048| features = { -00049| files = true; # Generates turbo.json and task scripts -00050| scripts = false; -00051| healthchecks = false; -00052| packages = true; # Creates task script derivations -00053| services = false; -00054| secrets = false; -00055| tasks = true; # Provides turbo tasks -00056| appModule = true; # Adds per-app turbo.* options -00057| }; -00058| -00059| # Priority for ordering (lower = higher priority) -00060| priority = 15; -00061| } -00062| - -(End of file - total 62 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.0s) - -_Thinking:_ - - - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "PANEL_TYPE_FORM", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/modules", - "include": "**/ui.nix" -} -``` -**Output:** -``` -Found 2 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/ui.nix: - Line 9: # - PANEL_TYPE_FORM: Editable configuration form - Line 67: # type = "PANEL_TYPE_FORM"; -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Process Compose UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| pcCfg = cfg.process-compose or { }; -00015| in -00016| lib.mkIf (cfg.enable && (pcCfg.enable or false)) { -00017| stack.panels."${meta.id}-status" = { -00018| module = meta.id; -00019| title = "Process Compose"; -00020| description = meta.description; -00021| type = "PANEL_TYPE_STATUS"; -00022| order = meta.priority; -00023| fields = [ -00024| { -00025| name = "command"; -00026| type = "FIELD_TYPE_STRING"; -00027| value = pcCfg.commandName or "dev"; -00028| } -00029| { -00030| name = "processes"; -00031| type = "FIELD_TYPE_JSON"; -00032| value = builtins.toJSON (lib.attrNames (pcCfg.processes or { })); -00033| } -00034| { -00035| name = "formatWatcher"; -00036| type = "FIELD_TYPE_BOOLEAN"; -00037| value = if (pcCfg.formatWatcher.enable or true) then "true" else "false"; -00038| } -00039| ]; -00040| }; -00041| } -00042| - -(End of file - total 42 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/go/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Go UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # -00006| # The APP_CONFIG panel is auto-generated from the SpField definitions in -00007| # go-app.proto.nix. No manual field listing needed - the schema is the -00008| # single source of truth for both Nix options and UI panels. -00009| # ============================================================================== -00010| { -00011| lib, -00012| config, -00013| pkgs, -00014| ... -00015| }: -00016| let -00017| meta = import ./meta.nix; -00018| cfg = config.stack; -00019| -00020| # Import field definitions and panel generator -00021| goSchema = import ./schema.nix { inherit lib; }; -00022| panelsLib = import ../../lib/panels.nix { inherit lib; }; -00023| -00024| # Filter apps to only Go apps -00025| goApps = lib.filterAttrs (name: app: app.go.enable or false) (cfg.apps or { }); -00026| hasGoApps = goApps != { }; -00027| in -00028| lib.mkIf (cfg.enable && hasGoApps) { -00029| # --------------------------------------------------------------------------- -00030| # Status Panel - Overview of Go environment -00031| # (Hand-crafted: uses runtime data like pkgs.go.version) -00032| # --------------------------------------------------------------------------- -00033| stack.panels."${meta.id}-status" = { -00034| module = meta.id; -00035| title = "Go Environment"; -00036| description = meta.description; -00037| icon = meta.icon; -00038| type = "PANEL_TYPE_STATUS"; -00039| order = meta.priority; -00040| fields = [ -00041| { -00042| name = "metrics"; -00043| type = "FIELD_TYPE_STRING"; -00044| value = builtins.toJSON [ -00045| { -00046| label = "Go Version"; -00047| value = pkgs.go.version; -00048| status = "ok"; -00049| } -00050| { -00051| label = "Apps"; -00052| value = toString (lib.length (lib.attrNames goApps)); -00053| status = "ok"; -00054| } -00055| ]; -00056| } -00057| ]; -00058| }; -00059| -00060| # --------------------------------------------------------------------------- -00061| # App Config Panel - Per-app Go configuration -00062| # (Auto-generated from go-app.proto.nix SpField definitions) -00063| # --------------------------------------------------------------------------- -00064| stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { -00065| module = meta.id; -00066| title = "Go Configuration"; -00067| icon = meta.icon; -00068| fields = goSchema.fields; -00069| optionPrefix = "go"; -00070| apps = goApps; -00071| exclude = [ "enable" ]; -00072| order = meta.priority + 2; -00073| }; -00074| } -00075| - -(End of file - total 75 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/ui.nix" -} -``` -**Output:** -``` - -00001| { lib, config, ... }: -00002| -00003| let -00004| meta = import ./meta.nix; -00005| cfg = config.stack.aws-vault; -00006| sp = config.stack; -00007| in -00008| { -00009| config = lib.mkIf (sp.enable && cfg.enable) { -00010| stack.panels."${meta.id}-status" = { -00011| module = meta.id; -00012| title = "AWS Vault"; -00013| description = "AWS Vault configuration and wrapper status"; -00014| type = "status"; -00015| order = 100; -00016| fields = { -00017| profile = { -00018| label = "AWS Profile"; -00019| type = "FIELD_TYPE_STRING"; -00020| value = cfg.profile; -00021| description = "The active AWS profile"; -00022| }; -00023| awscliWrapper = { -00024| label = "AWS CLI Wrapper"; -00025| type = "FIELD_TYPE_BOOLEAN"; -00026| value = cfg.awscliWrapper.enable; -00027| description = "Whether the AWS CLI is wrapped with aws-vault"; -00028| }; -00029| opentofuWrapper = { -00030| label = "OpenTofu Wrapper"; -00031| type = "FIELD_TYPE_BOOLEAN"; -00032| value = cfg.opentofuWrapper.enable; -00033| description = "Whether OpenTofu is wrapped with aws-vault"; -00034| }; -00035| terraformWrapper = { -00036| label = "Terraform Wrapper"; -00037| type = "FIELD_TYPE_BOOLEAN"; -00038| value = cfg.terraformWrapper.enable; -00039| description = "Whether Terraform is wrapped with aws-vault"; -00040| }; -00041| }; -00042| }; -00043| }; -00044| } -00045| - -(End of file - total 45 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Entrypoints UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| -00015| # Filter apps with entrypoints -00016| appsWithPaths = lib.filterAttrs ( -00017| name: app: (app.path or null) != null -00018| ) (cfg.apps or { }); -00019| -00020| appsWithEntrypoints = lib.filterAttrs ( -00021| name: app: (app.entrypoint.enable or true) -00022| ) appsWithPaths; -00023| -00024| hasApps = appsWithEntrypoints != { }; -00025| in -00026| lib.mkIf (cfg.enable && hasApps) { -00027| stack.panels."${meta.id}-status" = { -00028| module = meta.id; -00029| title = "Entrypoints"; -00030| description = meta.description; -00031| type = "PANEL_TYPE_STATUS"; -00032| order = meta.priority; -00033| fields = [ -00034| { -00035| name = "apps"; -00036| type = "FIELD_TYPE_JSON"; -00037| value = builtins.toJSON ( -00038| lib.mapAttrsToList (name: appCfg: { -00039| name = name; -00040| path = "packages/scripts/entrypoints/${name}.sh"; -00041| enabled = appCfg.entrypoint.enable or true; -00042| }) appsWithEntrypoints -00043| ); -00044| } -00045| ]; -00046| }; -00047| } -00048| - -(End of file - total 48 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.3s) - -_Thinking:_ - - - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "stack\\.panels\\.", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "**/*.nix" -} -``` -**Output:** -``` -Found 31 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/ui.nix: - Line 10: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix: - Line 47: stack.panels."fly-status" = { - Line 91: stack.panels."fly-config" = { - Line 166: stack.panels."fly-apps" = { - Line 197: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix: - Line 64: stack.panels."cloudflare-status" = { - Line 112: stack.panels."cloudflare-config" = { - Line 149: stack.panels."cloudflare-apps" = { - Line 180: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { - -/Users/cm/git/darkmatter/stack/nix/stack/services/caddy.nix: - Line 124: stack.panels.caddy-status = { - Line 160: stack.panels.caddy-apps = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/ui.nix: - Line 17: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/ui.nix: - Line 18: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/ui.nix: - Line 31: stack.panels."${meta.id}-status" = { - Line 72: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/ui.nix: - Line 33: stack.panels."${meta.id}-status" = { - Line 64: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/git-hooks/ui.nix: - Line 17: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/ui.nix: - Line 21: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/ui.nix: - Line 27: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/ui.nix: - Line 33: stack.panels."${meta.id}-status" = { - Line 64: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/ui.nix: - Line 35: stack.panels."${meta.id}-status" = { - Line 64: # stack.panels."${meta.id}-config" = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/ui.nix: - Line 27: stack.panels."${meta.id}-status" = { - -/Users/cm/git/darkmatter/stack/nix/stack/lib/panels.nix: - Line 57: # A panel definition suitable for stack.panels. - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/panels.nix: - Line 12: # stack.panels.go-status = { - Line 316: stack.panels.go-status = { - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/healthchecks.nix: - Line 495: stack.panels.healthchecks-overview = { - -/Users/cm/git/darkmatter/stack/nix/stack/containers/ui.nix: - Line 38: stack.panels."${meta.id}-status" = { - Line 101: stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/lib/panels.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # nix/stack/lib/panels.nix -00003| # -00004| # Panel generation library for auto-generating UI panels from SpField definitions. -00005| # -00006| # Instead of hand-writing panel field lists in each module's ui.nix, this library -00007| # reads the SpField metadata (from go.proto.nix, bun.proto.nix, etc.) and -00008| # automatically produces PANEL_TYPE_APP_CONFIG panel definitions. -00009| # -00010| # Usage: -00011| # let -00012| # panelsLib = import ./panels.nix { inherit lib; }; -00013| # goSchema = import ../db/schemas/go.proto.nix { inherit lib; }; -00014| # in -00015| # panelsLib.mkPanelFromSpFields { -00016| # module = "go"; -00017| # title = "Go Configuration"; -00018| # icon = "code"; -00019| # fields = goSchema.fields; -00020| # optionPrefix = "go"; -00021| # apps = goApps; # evaluated apps (filtered to Go-enabled) -00022| # exclude = [ "enable" ]; -00023| # } -00024| # ============================================================================== -00025| { lib }: -00026| let -00027| sp = import ../db/lib/field.nix { inherit lib; }; -00028| -00029| # Navigate a nested attrset by a dot-separated path string -00030| # e.g., getByPath app "linting.oxlint" → app.linting.oxlint -00031| getByPath = -00032| attrset: path: -00033| let -00034| parts = lib.splitString "." path; -00035| in -00036| lib.foldl' (acc: key: acc.${key} or null) attrset parts; -00037| in -00038| { -00039| # =========================================================================== -00040| # mkPanelFromSpFields -00041| # -00042| # Auto-generate a PANEL_TYPE_APP_CONFIG panel from SpField definitions. -00043| # -00044| # Arguments: -00045| # module - Module identifier (e.g., "go", "bun") -00046| # title - Display title for the panel -00047| # icon - Lucide icon name (optional) -00048| # fields - Attrset of SpField definitions (from *.proto.nix) -00049| # optionPrefix - Key in the app submodule (e.g., "go" for app.go.*) -00050| # apps - Evaluated app configs (pre-filtered to relevant apps) -00051| # exclude - Field names to exclude (default: ["enable"]) -00052| # include - If set, only include these field names (whitelist) -00053| # order - Panel display order (default: 100) -00054| # readme - Module documentation in markdown (optional, shown in UI) -00055| # -00056| # Returns: -00057| # A panel definition suitable for stack.panels. -00058| # -00059| # =========================================================================== -00060| mkPanelFromSpFields = -00061| { -00062| module, -00063| title, -00064| icon ? null, -00065| fields, -00066| optionPrefix, -00067| apps, -00068| exclude ? [ "enable" ], -00069| include ? null, -00070| order ? 100, -00071| readme ? null, -00072| }: -00073| let -00074| # Filter to UI-visible fields, applying include/exclude -00075| visibleFields = lib.filterAttrs ( -00076| name: field: -00077| let -00078| isSpField = field._isSpField or false; -00079| hasUi = isSpField && field.ui != null && !(field.ui.hidden or false); -00080| included = if include != null then builtins.elem name include else true; -00081| excluded = builtins.elem name exclude; -00082| in -00083| hasUi && included && !excluded -00084| ) fields; -00085| -00086| # Convert SpField definitions to panel field entries -00087| # Include _order for sorting, then strip it before output -00088| panelFields = lib.mapAttrsToList (name: field: { -00089| inherit name; -00090| type = field.ui.type; -00091| value = ""; # Per-app values go in the apps map, not here -00092| options = field.ui.options; -00093| label = field.ui.label; -00094| editable = field.ui.editable; -00095| editPath = "${optionPrefix}.${name}"; -00096| placeholder = field.ui.placeholder; -00097| # Help text from field description -00098| description = field.ui.description or field.description or null; -00099| # Example value for additional context (JSON-encoded if complex) -00100| example = -00101| let -00102| ex = field.ui.example or field.example or null; -00103| in -00104| if ex == null then -00105| null -00106| else if builtins.isString ex then -00107| ex -00108| else -00109| builtins.toJSON ex; -00110| _order = field.ui.order; -00111| }) visibleFields; -00112| -00113| # Sort fields by their UI order, then strip the internal _order key -00114| sortedFields = lib.sort (a: b: a._order < b._order) panelFields; -00115| cleanFields = map (f: removeAttrs f [ "_order" ]) sortedFields; -00116| -00117| # Serialize a Nix value to a string for the config map -00118| valueToString = -00119| val: -00120| if val == null then -00121| "" -00122| else if builtins.isBool val then -00123| builtins.toJSON val -00124| else if builtins.isList val then -00125| builtins.toJSON val -00126| else if builtins.isAttrs val then -00127| builtins.toJSON val -00128| else if builtins.isInt val || builtins.isFloat val then -00129| toString val -00130| else -00131| toString val; -00132| -00133| # Generate per-app config from evaluated values -00134| # Supports both simple ("go") and nested ("linting.oxlint") option prefixes -00135| appsData = lib.mapAttrs ( -00136| appName: app: -00137| let -00138| prefixVal = getByPath app optionPrefix; -00139| in -00140| { -00141| enabled = true; -00142| config = lib.mapAttrs ( -00143| fieldName: _field: -00144| let -00145| val = if prefixVal != null then prefixVal.${fieldName} or null else null; -00146| in -00147| valueToString val -00148| ) visibleFields; -00149| } -00150| ) apps; -00151| -00152| in -00153| { -00154| inherit -00155| module -00156| title -00157| icon -00158| order -00159| readme -00160| ; -00161| type = "PANEL_TYPE_APP_CONFIG"; -00162| enabled = true; -00163| fields = cleanFields; -00164| apps = appsData; -00165| }; -00166| } -00167| - -(End of file - total 167 lines) - -``` -``` - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "FIELD_TYPE_", - "path": "/Users/cm/git/darkmatter/stack", - "include": "**/*.{nix,ts,tsx,go,proto}" -} -``` -**Output:** -``` -Found 100 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/ui.nix: - Line 19: type = "FIELD_TYPE_STRING"; - Line 25: type = "FIELD_TYPE_BOOLEAN"; - Line 31: type = "FIELD_TYPE_BOOLEAN"; - Line 37: type = "FIELD_TYPE_BOOLEAN"; - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix: - Line 57: type = "FIELD_TYPE_STRING"; - Line 63: type = "FIELD_TYPE_STRING"; - Line 69: type = "FIELD_TYPE_STRING"; - Line 75: type = "FIELD_TYPE_NUMBER"; - Line 81: type = "FIELD_TYPE_JSON"; - Line 100: type = "FIELD_TYPE_STRING"; - Line 108: type = "FIELD_TYPE_SELECT"; - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix: - Line 74: type = "FIELD_TYPE_STRING"; - Line 80: type = "FIELD_TYPE_STRING"; - Line 90: type = "FIELD_TYPE_STRING"; - Line 96: type = "FIELD_TYPE_NUMBER"; - Line 102: type = "FIELD_TYPE_JSON"; - Line 121: type = "FIELD_TYPE_STRING"; - Line 129: type = "FIELD_TYPE_STRING"; - Line 137: type = "FIELD_TYPE_STRING"; - -/Users/cm/git/darkmatter/stack/packages/proto/proto/modules.proto: - Line 23: MODULE_FIELD_TYPE_UNSPECIFIED = 0; - Line 24: MODULE_FIELD_TYPE_STRING = 1; - Line 25: MODULE_FIELD_TYPE_NUMBER = 2; - Line 26: MODULE_FIELD_TYPE_BOOLEAN = 3; - Line 27: MODULE_FIELD_TYPE_SELECT = 4; - Line 28: MODULE_FIELD_TYPE_MULTISELECT = 5; - Line 29: MODULE_FIELD_TYPE_APP_FILTER = 6; - Line 30: MODULE_FIELD_TYPE_COLUMNS = 7; - Line 31: MODULE_FIELD_TYPE_JSON = 8; - -/Users/cm/git/darkmatter/stack/packages/proto/proto/extensions.proto: - Line 31: FIELD_TYPE_UNSPECIFIED = 0; - Line 32: FIELD_TYPE_STRING = 1; - Line 33: FIELD_TYPE_NUMBER = 2; - Line 34: FIELD_TYPE_BOOLEAN = 3; - Line 35: FIELD_TYPE_SELECT = 4; - Line 36: FIELD_TYPE_MULTISELECT = 5; - Line 37: FIELD_TYPE_APP_FILTER = 6; - Line 38: FIELD_TYPE_COLUMNS = 7; - Line 39: FIELD_TYPE_JSON = 8; - -/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/modules.ts: - Line 515: * @generated from protobuf enum value: MODULE_FIELD_TYPE_UNSPECIFIED = 0; - Line 519: * @generated from protobuf enum value: MODULE_FIELD_TYPE_STRING = 1; - Line 523: * @generated from protobuf enum value: MODULE_FIELD_TYPE_NUMBER = 2; - Line 527: * @generated from protobuf enum value: MODULE_FIELD_TYPE_BOOLEAN = 3; - Line 531: * @generated from protobuf enum value: MODULE_FIELD_TYPE_SELECT = 4; - Line 535: * @generated from protobuf enum value: MODULE_FIELD_TYPE_MULTISELECT = 5; - Line 539: * @generated from protobuf enum value: MODULE_FIELD_TYPE_APP_FILTER = 6; - Line 543: * @generated from protobuf enum value: MODULE_FIELD_TYPE_COLUMNS = 7; - Line 547: * @generated from protobuf enum value: MODULE_FIELD_TYPE_JSON = 8; - Line 1645: { no: 2, name: "type", kind: "enum", T: () => ["stack.db.ModuleFieldType", ModuleFieldType, "MODULE_FIELD_TYPE_"] }, - -/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/modules_pb.d.ts: - Line 845: * @generated from enum value: MODULE_FIELD_TYPE_UNSPECIFIED = 0; - Line 850: * @generated from enum value: MODULE_FIELD_TYPE_STRING = 1; - Line 855: * @generated from enum value: MODULE_FIELD_TYPE_NUMBER = 2; - Line 860: * @generated from enum value: MODULE_FIELD_TYPE_BOOLEAN = 3; - Line 865: * @generated from enum value: MODULE_FIELD_TYPE_SELECT = 4; - Line 870: * @generated from enum value: MODULE_FIELD_TYPE_MULTISELECT = 5; - Line 875: * @generated from enum value: MODULE_FIELD_TYPE_APP_FILTER = 6; - Line 880: * @generated from enum value: MODULE_FIELD_TYPE_COLUMNS = 7; - Line 885: * @generated from enum value: MODULE_FIELD_TYPE_JSON = 8; - -/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/extensions_pb.d.ts: - Line 518: * @generated from enum value: FIELD_TYPE_UNSPECIFIED = 0; - Line 523: * @generated from enum value: FIELD_TYPE_STRING = 1; - Line 528: * @generated from enum value: FIELD_TYPE_NUMBER = 2; - Line 533: * @generated from enum value: FIELD_TYPE_BOOLEAN = 3; - Line 538: * @generated from enum value: FIELD_TYPE_SELECT = 4; - Line 543: * @generated from enum value: FIELD_TYPE_MULTISELECT = 5; - Line 548: * @generated from enum value: FIELD_TYPE_APP_FILTER = 6; - Line 553: * @generated from enum value: FIELD_TYPE_COLUMNS = 7; - Line 558: * @generated from enum value: FIELD_TYPE_JSON = 8; - -/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/extensions.ts: - Line 325: * @generated from protobuf enum value: FIELD_TYPE_UNSPECIFIED = 0; - Line 329: * @generated from protobuf enum value: FIELD_TYPE_STRING = 1; - Line 333: * @generated from protobuf enum value: FIELD_TYPE_NUMBER = 2; - Line 337: * @generated from protobuf enum value: FIELD_TYPE_BOOLEAN = 3; - Line 341: * @generated from protobuf enum value: FIELD_TYPE_SELECT = 4; - Line 345: * @generated from protobuf enum value: FIELD_TYPE_MULTISELECT = 5; - Line 349: * @generated from protobuf enum value: FIELD_TYPE_APP_FILTER = 6; - Line 353: * @generated from protobuf enum value: FIELD_TYPE_COLUMNS = 7; - Line 357: * @generated from protobuf enum value: FIELD_TYPE_JSON = 8; - Line 995: { no: 2, name: "type", kind: "enum", T: () => ["stack.db.FieldType", FieldType, "FIELD_TYPE_"] }, - -/Users/cm/git/darkmatter/stack/packages/proto/gen/gopb/modules.pb.go: - Line 102: ModuleFieldType_MODULE_FIELD_TYPE_UNSPECIFIED ModuleFieldType = 0 - Line 103: ModuleFieldType_MODULE_FIELD_TYPE_STRING ModuleFieldType = 1 - Line 104: ModuleFieldType_MODULE_FIELD_TYPE_NUMBER ModuleFieldType = 2 - Line 105: ModuleFieldType_MODULE_FIELD_TYPE_BOOLEAN ModuleFieldType = 3 - Line 106: ModuleFieldType_MODULE_FIELD_TYPE_SELECT ModuleFieldType = 4 - Line 107: ModuleFieldType_MODULE_FIELD_TYPE_MULTISELECT ModuleFieldType = 5 - Line 108: ModuleFieldType_MODULE_FIELD_TYPE_APP_FILTER ModuleFieldType = 6 - Line 109: ModuleFieldType_MODULE_FIELD_TYPE_COLUMNS ModuleFieldType = 7 - Line 110: ModuleFieldType_MODULE_FIELD_TYPE_JSON ModuleFieldType = 8 - Line 116: 0: "MODULE_FIELD_TYPE_UNSPECIFIED", - Line 117: 1: "MODULE_FIELD_TYPE_STRING", - Line 118: 2: "MODULE_FIELD_TYPE_NUMBER", - Line 119: 3: "MODULE_FIELD_TYPE_BOOLEAN", - Line 120: 4: "MODULE_FIELD_TYPE_SELECT", - Line 121: 5: "MODULE_FIELD_TYPE_MULTISELECT", - Line 122: 6: "MODULE_FIELD_TYPE_APP_FILTER", - Line 123: 7: "MODULE_FIELD_TYPE_COLUMNS", - Line 124: 8: "MODULE_FIELD_TYPE_JSON", - Line 127: "MODULE_FIELD_TYPE_UNSPECIFIED": 0, - Line 128: "MODULE_FIELD_TYPE_STRING": 1, - Line 129: "MODULE_FIELD_TYPE_NUMBER": 2, - Line 130: "MODULE_FIELD_TYPE_BOOLEAN": 3, - Line 131: "MODULE_FIELD_TYPE_SELECT": 4, - Line 132: "MODULE_FIELD_TYPE_MULTISELECT": 5, - Line 133: "MODULE_FIELD_TYPE_APP_FILTER": 6, - -(Results are truncated. Consider using a more specific path or pattern.) - -(Some paths were inaccessible and skipped) -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 1.7s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/core/options/panels.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # panels.nix -00003| # -00004| # UI Panel configuration for core Stack modules. -00005| # -00006| # This is separate from extensions - panels here are for built-in modules like -00007| # Go, Caddy, Healthchecks, Theme, etc. Extensions (like SST) define their own -00008| # panels within their extension configuration. -00009| # -00010| # Core modules register panels like this: -00011| # -00012| # stack.panels.go-status = { -00013| # module = "go"; -00014| # title = "Go Environment"; -00015| # type = "PANEL_TYPE_STATUS"; -00016| # order = 10; -00017| # fields = [ -00018| # { name = "metrics"; type = "FIELD_TYPE_STRING"; value = "..."; } -00019| # ]; -00020| # }; -00021| # -00022| # The panels are exposed via nix eval for the agent/web UI to consume. -00023| # ============================================================================== -00024| { -00025| lib, -00026| config, -00027| ... -00028| }: -00029| let -00030| cfg = config.stack; -00031| -00032| # ============================================================================ -00033| # Type Definitions -00034| # ============================================================================ -00035| -00036| # Panel types for UI rendering -00037| panelTypeEnum = lib.types.enum [ -00038| "PANEL_TYPE_UNSPECIFIED" -00039| "PANEL_TYPE_STATUS" -00040| "PANEL_TYPE_APPS_GRID" -00041| "PANEL_TYPE_FORM" -00042| "PANEL_TYPE_TABLE" -00043| "PANEL_TYPE_CUSTOM" -00044| "PANEL_TYPE_APP_CONFIG" -00045| ]; -00046| -00047| # Field types for panel configuration -00048| fieldTypeEnum = lib.types.enum [ -00049| "FIELD_TYPE_UNSPECIFIED" -00050| "FIELD_TYPE_STRING" -00051| "FIELD_TYPE_NUMBER" -00052| "FIELD_TYPE_BOOLEAN" -00053| "FIELD_TYPE_SELECT" -00054| "FIELD_TYPE_MULTISELECT" -00055| "FIELD_TYPE_APP_FILTER" -00056| "FIELD_TYPE_COLUMNS" -00057| "FIELD_TYPE_JSON" -00058| "FIELD_TYPE_CODE" -00059| ]; -00060| -00061| # Panel field type -00062| panelFieldType = lib.types.submodule { -00063| options = { -00064| name = lib.mkOption { -00065| type = lib.types.str; -00066| description = "Field name (maps to component prop)"; -00067| }; -00068| type = lib.mkOption { -00069| type = fieldTypeEnum; -00070| default = "FIELD_TYPE_STRING"; -00071| description = "Field type"; -00072| }; -00073| value = lib.mkOption { -00074| type = lib.types.nullOr lib.types.str; -00075| default = ""; -00076| description = "Field value (JSON-encoded for complex types, null if unset)"; -00077| }; -00078| options = lib.mkOption { -00079| type = lib.types.listOf ( -00080| lib.types.either lib.types.str ( -00081| lib.types.submodule { -00082| options = { -00083| value = lib.mkOption { -00084| type = lib.types.str; -00085| description = "Option value"; -00086| }; -00087| label = lib.mkOption { -00088| type = lib.types.str; -00089| description = "Option display label"; -00090| }; -00091| }; -00092| } -00093| ) -00094| ); -00095| default = [ ]; -00096| description = "Options for select fields (strings or {value, label} objects)"; -00097| }; -00098| -00099| # Extended fields for PANEL_TYPE_APP_CONFIG -00100| label = lib.mkOption { -00101| type = lib.types.nullOr lib.types.str; -00102| default = null; -00103| description = "Human-readable label for the field (defaults to name if null)"; -00104| }; -00105| editable = lib.mkOption { -00106| type = lib.types.bool; -00107| default = false; -00108| description = "Whether the field can be modified from the UI"; -00109| }; -00110| editPath = lib.mkOption { -00111| type = lib.types.nullOr lib.types.str; -00112| default = null; -00113| description = '' -00114| Dot-separated path within the app's config for writes. -00115| E.g., "go.mainPackage" tells the agent to patch -00116| apps..go.mainPackage in the data file. -00117| ''; -00118| }; -00119| placeholder = lib.mkOption { -00120| type = lib.types.nullOr lib.types.str; -00121| default = null; -00122| description = "Placeholder text for input fields"; -00123| }; -00124| configPath = lib.mkOption { -00125| type = lib.types.nullOr lib.types.str; -00126| default = null; -00127| description = "Nix config path for saving field value (e.g., 'stack.deployment.fly.organization')"; -00128| }; -00129| description = lib.mkOption { -00130| type = lib.types.nullOr lib.types.str; -00131| default = null; -00132| description = "Help text shown below the field"; -00133| }; -00134| example = lib.mkOption { -00135| type = lib.types.nullOr lib.types.str; -00136| default = null; -00137| description = "Example value shown as help text"; -00138| }; -00139| }; -00140| }; -00141| -00142| # Module panel type - a panel belonging to a core module -00143| modulePanelType = lib.types.submodule { -00144| options = { -00145| # Which module this panel belongs to -00146| module = lib.mkOption { -00147| type = lib.types.str; -00148| description = '' -00149| The core module this panel belongs to (e.g., "go", "caddy", "healthchecks"). -00150| Used for grouping panels in the UI. -00151| ''; -00152| }; -00153| -00154| # Display settings -00155| title = lib.mkOption { -00156| type = lib.types.str; -00157| description = "Display title for the panel"; -00158| }; -00159| description = lib.mkOption { -00160| type = lib.types.nullOr lib.types.str; -00161| default = null; -00162| description = "Optional description shown below the title"; -00163| }; -00164| readme = lib.mkOption { -00165| type = lib.types.nullOr lib.types.str; -00166| default = null; -00167| description = '' -00168| Module documentation in markdown format. -00169| Rendered in the UI panel to help users understand the module's configuration. -00170| ''; -00171| }; -00172| icon = lib.mkOption { -00173| type = lib.types.nullOr lib.types.str; -00174| default = null; -00175| description = "Icon name from lucide-react (e.g., 'server', 'database')"; -00176| }; -00177| -00178| # Panel type and configuration -00179| type = lib.mkOption { -00180| type = panelTypeEnum; -00181| default = "PANEL_TYPE_STATUS"; -00182| description = "Panel type (determines which component to render)"; -00183| }; -00184| order = lib.mkOption { -00185| type = lib.types.int; -00186| default = 100; -00187| description = "Display order within the module (lower = first)"; -00188| }; -00189| fields = lib.mkOption { -00190| type = lib.types.listOf panelFieldType; -00191| default = [ ]; -00192| description = "Panel configuration fields passed to the component"; -00193| }; -00194| -00195| # Visibility -00196| enabled = lib.mkOption { -00197| type = lib.types.bool; -00198| default = true; -00199| description = "Whether this panel is enabled and should be shown"; -00200| }; -00201| -00202| # Optional: Columns for PANEL_TYPE_TABLE -00203| columns = lib.mkOption { -00204| type = lib.types.listOf ( -00205| lib.types.submodule { -00206| options = { -00207| key = lib.mkOption { -00208| type = lib.types.str; -00209| description = "Column key (matches row data)"; -00210| }; -00211| label = lib.mkOption { -00212| type = lib.types.str; -00213| description = "Column header label"; -00214| }; -00215| }; -00216| } -00217| ); -00218| default = [ ]; -00219| description = "Column definitions for table panels"; -00220| }; -00221| -00222| # Optional: Rows for PANEL_TYPE_TABLE -00223| rows = lib.mkOption { -00224| type = lib.types.listOf (lib.types.attrsOf lib.types.str); -00225| default = [ ]; -00226| description = "Row data for table panels"; -00227| }; -00228| -00229| # Optional: Apps data for PANEL_TYPE_APPS_GRID -00230| apps = lib.mkOption { -00231| type = lib.types.attrsOf ( -00232| lib.types.submodule { -00233| options = { -00234| enabled = lib.mkOption { -00235| type = lib.types.bool; -00236| default = true; -00237| }; -00238| config = lib.mkOption { -00239| type = lib.types.attrsOf lib.types.str; -00240| default = { }; -00241| }; -00242| }; -00243| } -00244| ); -00245| default = { }; -00246| description = "Per-app data for apps grid panels"; -00247| }; -00248| }; -00249| }; -00250| -00251| # ============================================================================ -00252| # Helper Functions -00253| # ============================================================================ -00254| -00255| # Serialize a panel for JSON output -00256| serializePanel = id: panel: { -00257| id = id; -00258| module = panel.module; -00259| title = panel.title; -00260| description = panel.description; -00261| readme = panel.readme; -00262| icon = panel.icon; -00263| type = panel.type; -00264| order = panel.order; -00265| enabled = panel.enabled; -00266| fields = map (f: { -00267| name = f.name; -00268| type = f.type; -00269| value = f.value; -00270| options = f.options; -00271| label = f.label; -00272| editable = f.editable; -00273| editPath = f.editPath; -00274| placeholder = f.placeholder; -00275| configPath = f.configPath; -00276| description = f.description; -00277| example = f.example; -00278| }) panel.fields; -00279| apps = lib.mapAttrs (name: appData: { -00280| enabled = appData.enabled; -00281| config = appData.config; -00282| }) panel.apps; -00283| columns = panel.columns; -00284| rows = panel.rows; -00285| }; -00286| -00287| # Group panels by module -00288| groupPanelsByModule = -00289| panels: -00290| let -00291| # Get unique module names -00292| moduleNames = lib.unique (lib.mapAttrsToList (id: p: p.module) panels); -00293| -00294| # Get panels for a specific module -00295| panelsForModule = moduleName: lib.filterAttrs (id: p: p.module == moduleName) panels; -00296| in -00297| lib.genAttrs moduleNames panelsForModule; -00298| -00299| in -00300| { -00301| # ============================================================================ -00302| # Options -00303| # ============================================================================ -00304| -00305| options.stack.panels = lib.mkOption { -00306| type = lib.types.attrsOf modulePanelType; -00307| default = { }; -00308| description = '' -00309| UI panels for core Stack modules. -00310| -00311| Panels are UI components that display information about a module's state, -00312| configuration, or managed resources. Unlike extension panels, these belong -00313| to built-in modules like Go, Caddy, Healthchecks, etc. -00314| -00315| Example: -00316| stack.panels.go-status = { -00317| module = "go"; -00318| title = "Go Environment"; -00319| type = "PANEL_TYPE_STATUS"; -00320| order = 10; -00321| fields = [ -00322| { name = "metrics"; type = "FIELD_TYPE_STRING"; value = "..."; } -00323| ]; -00324| }; -00325| ''; -00326| example = lib.literalExpression '' -00327| { -00328| go-status = { -00329| module = "go"; -00330| title = "Go Environment"; -00331| type = "PANEL_TYPE_STATUS"; -00332| order = 10; -00333| fields = [ -00334| { -00335| name = "metrics"; -00336| type = "FIELD_TYPE_STRING"; -00337| value = builtins.toJSON [ -00338| { label = "Go Version"; value = "1.22"; status = "ok"; } -00339| { label = "Apps"; value = "3"; status = "ok"; } -00340| ]; -00341| } -00342| ]; -00343| }; -00344| caddy-status = { -00345| module = "caddy"; -00346| title = "Reverse Proxy"; -00347| type = "PANEL_TYPE_STATUS"; -00348| order = 20; -00349| fields = [ ... ]; -00350| }; -00351| } -00352| ''; -00353| }; -00354| -00355| # Computed/serializable panels for nix eval -00356| options.stack.panelsComputed = lib.mkOption { -00357| type = lib.types.attrsOf lib.types.unspecified; -00358| readOnly = true; -00359| description = "Serializable panels for UI consumption"; -00360| }; -00361| -00362| # Panels grouped by module -00363| options.stack.panelsByModule = lib.mkOption { -00364| type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); -00365| readOnly = true; -00366| description = "Panels grouped by their parent module"; -00367| }; -00368| -00369| # List of panels (for iteration) -00370| options.stack.panelsList = lib.mkOption { -00371| type = lib.types.listOf lib.types.unspecified; -00372| readOnly = true; -00373| description = "List of all panels sorted by order"; -00374| }; -00375| -00376| # List of modules that have panels -00377| options.stack.panelModules = lib.mkOption { -00378| type = lib.types.listOf lib.types.str; -00379| readOnly = true; -00380| description = "List of module names that have registered panels"; -00381| }; -00382| -00383| # ============================================================================ -00384| # Config -00385| # ============================================================================ -00386| -00387| config = { -00388| # Serialize panels for UI -00389| stack.panelsComputed = lib.mapAttrs serializePanel ( -00390| lib.filterAttrs (id: p: p.enabled) cfg.panels -00391| ); -00392| -00393| # Group by module -00394| stack.panelsByModule = -00395| let -00396| enabledPanels = lib.filterAttrs (id: p: p.enabled) cfg.panels; -00397| serialized = lib.mapAttrs serializePanel enabledPanels; -00398| in -00399| groupPanelsByModule serialized; -00400| -00401| # Sorted list of panels -00402| stack.panelsList = -00403| let -00404| enabledPanels = lib.filterAttrs (id: p: p.enabled) cfg.panels; -00405| serialized = lib.mapAttrs serializePanel enabledPanels; -00406| asList = lib.mapAttrsToList (id: panel: panel) serialized; -00407| in -00408| lib.sort (a: b: a.order < b.order) asList; -00409| -00410| # List of modules with panels -00411| stack.panelModules = -00412| let -00413| enabledPanels = lib.filterAttrs (id: p: p.enabled) cfg.panels; -00414| in -00415| lib.unique (lib.mapAttrsToList (id: p: p.module) enabledPanels); -00416| }; -00417| } -00418| - -(End of file - total 418 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 1.7s) - -_Thinking:_ - - - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "colmena", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "**/*.nix" -} -``` -**Output:** -``` -No files found -``` -``` - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "colmena", - "path": "/Users/cm/git/darkmatter/stack", - "include": "**/*.{md,nix,ts,tsx,go,json,yaml,yml}" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # module.nix - Fly.io Deployment Module Implementation -00003| # -00004| # Provides Fly.io deployment support using nix2container (default) or dockerTools. -00005| # -00006| # This module: -00007| # 1. Adds deployment.* options to each app via appModules -00008| # 2. Auto-contributes to stack.containers.images for deployable apps -00009| # 3. Generates fly.toml into each app directory -00010| # 4. Adds deploy scripts to packages/infra/package.json -00011| # 5. Registers turbo tasks for build + deploy workflow -00012| # 6. Creates wrapped fly- commands for each deployable app -00013| # -00014| # Container workflow: -00015| # bun run build # Build app (in app directory) -00016| # container-build web # Build container image -00017| # container-copy web docker://... # Push to registry -00018| # fly-web deploy --image registry.fly.io/my-app:latest -00019| # -00020| # Generated layout: -00021| # packages/infra/ -00022| # ├── package.json # "deploy:web" script (merged via SST) -00023| # └── turbo.json # Per-package turbo config (extends root) -00024| # -00025| # apps// -00026| # └── fly.toml # Fly.io app configuration -00027| # -00028| # Usage: -00029| # stack.apps.web = { -00030| # path = "apps/web"; -00031| # deployment = { -00032| # enable = true; -00033| # provider = "fly"; -00034| # fly.appName = "my-app"; -00035| # container = { type = "bun"; port = 3000; }; -00036| # }; -00037| # }; -00038| # ============================================================================== -00039| { -00040| lib, -00041| config, -00042| pkgs, -00043| ... -00044| }: -00045| let -00046| meta = import ./meta.nix; -00047| cfg = config.stack; -00048| deployCfg = cfg.deployment; -00049| infraPath = "packages/infra"; -00050| -00051| # Import schema for SpField definitions -00052| flySchema = import ./schema.nix { inherit lib; }; -00053| sp = import ../../db/lib/field.nix { inherit lib; }; -00054| -00055| # --------------------------------------------------------------------------- -00056| # Per-app deployment options module (added via appModules) -00057| # Uses SpField schema for simple fields, manual definitions for complex types -00058| # --------------------------------------------------------------------------- -00059| deploymentAppModule = -00060| { lib, name, ... }: -00061| { -00062| options.deployment = { -00063| fly = { -00064| # Simple fields from schema (auto-converted via sp.asOption) -00065| memory = sp.asOption flySchema.fields.memory; -00066| cpus = sp.asOption flySchema.fields.cpus; -00067| autoStart = sp.asOption flySchema.fields.autoStart; -00068| minMachines = sp.asOption flySchema.fields.minMachines; -00069| forceHttps = sp.asOption flySchema.fields.forceHttps; -00070| env = sp.asOption flySchema.fields.env; -00071| -00072| # App name defaults to stack app name - needs special handling -00073| appName = lib.mkOption { -00074| type = lib.types.str; -00075| default = name; -00076| description = flySchema.fields.appName.description; -00077| example = flySchema.fields.appName.example or null; -00078| }; -00079| -00080| # Region defaults to global setting - needs special handling -00081| region = lib.mkOption { -00082| type = lib.types.str; -00083| default = deployCfg.fly.defaultRegion or flySchema.fields.region.default or "iad"; -00084| description = flySchema.fields.region.description; -00085| }; -00086| -00087| # Override cpuKind to use enum for strict validation in Nix -00088| cpuKind = lib.mkOption { -00089| type = lib.types.enum [ -00090| "shared" -00091| "performance" -00092| ]; -00093| default = flySchema.fields.cpuKind.default or "shared"; -00094| description = flySchema.fields.cpuKind.description; -00095| }; -00096| -00097| # Override autoStop to use enum for strict validation -00098| autoStop = lib.mkOption { -00099| type = lib.types.enum [ -00100| "off" -00101| "stop" -00102| "suspend" -00103| ]; -00104| default = flySchema.fields.autoStop.default or "suspend"; -00105| description = flySchema.fields.autoStop.description; -00106| }; -00107| }; -00108| -00109| container = { -00110| type = lib.mkOption { -00111| type = lib.types.enum [ -00112| "bun" -00113| "node" -00114| "go" -00115| "static" -00116| "custom" -00117| ]; -00118| default = "bun"; -00119| description = "App type determines base image and startup command."; -00120| }; -00121| -00122| port = lib.mkOption { -00123| type = lib.types.int; -00124| default = 3000; -00125| description = "Internal port the app listens on."; -00126| }; -00127| -00128| entrypoint = lib.mkOption { -00129| type = lib.types.nullOr lib.types.str; -00130| default = null; -00131| description = "Container startup command override."; -00132| }; -00133| }; -00134| }; -00135| }; -00136| -00137| # --------------------------------------------------------------------------- -00138| # Filter deployable apps -00139| # --------------------------------------------------------------------------- -00140| getDeployableApps = -00141| apps: -00142| lib.filterAttrs ( -00143| _: appCfg: -00144| (appCfg.deployment.enable or false) -00145| && (appCfg.deployment.host or deployCfg.defaultHost or "fly") == "fly" -00146| ) apps; -00147| -00148| # --------------------------------------------------------------------------- -00149| # Generate fly.toml content -00150| # Uses pre-built container images pushed via nix2container/dockerTools -00151| # --------------------------------------------------------------------------- -00152| mkFlyToml = -00153| appName: appCfg: -00154| let -00155| d = appCfg.deployment; -00156| f = d.fly; -00157| c = d.container; -00158| appPath = appCfg.path or "apps/${appName}"; -00159| org = deployCfg.fly.organization or null; -00160| flyAppName = f.appName or appName; -00161| -00162| allEnv = { -00163| PORT = toString c.port; -00164| } -00165| // f.env; -00166| envSection = lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k} = '${v}'") allEnv); -00167| -00168| # Organization line (optional) -00169| orgLine = lib.optionalString (org != null) '' -00170| org = "${org}" -00171| ''; -00172| in -00173| '' -00174| # Generated by stack - do not edit manually -00175| # Regenerate by entering the devshell: nix develop --impure -00176| # -00177| # Deploy workflow (uses nix2container/dockerTools): -00178| # 1. Build app: bun run build (in app directory) -00179| # 2. Build container: container-build ${appName} -00180| # 3. Push container: container-copy ${appName} docker://registry.fly.io/ -00181| # 4. Deploy: flyctl deploy --config ${appPath}/fly.toml --image registry.fly.io/${flyAppName}:latest -00182| # -00183| # Or use turbo workflow: -00184| # turbo run ship:${appName} -00185| -00186| app = "${flyAppName}" -00187| ${orgLine} -00188| # Build section removed - we use pre-built container images -00189| # Container is built with nix2container/dockerTools and pushed via skopeo -00190| -00191| [env] -00192| ${envSection} -00193| -00194| [http_service] -00195| internal_port = ${toString c.port} -00196| force_https = ${lib.boolToString f.forceHttps} -00197| auto_stop_machines = "${f.autoStop}" -00198| auto_start_machines = ${lib.boolToString f.autoStart} -00199| min_machines_running = ${toString f.minMachines} -00200| processes = ["app"] -00201| -00202| [[vm]] -00203| memory = "${f.memory}" -00204| cpu_kind = "${f.cpuKind}" -00205| cpus = ${toString f.cpus} -00206| ''; -00207| -00208| # --------------------------------------------------------------------------- -00209| # Package.json scripts for packages/infra -00210| # --------------------------------------------------------------------------- -00211| # Organization for Fly.io (if configured) -00212| flyOrg = deployCfg.fly.organization or null; -00213| orgFlag = if flyOrg != null then "--org ${flyOrg}" else ""; -00214| -00215| mkDeployPackageScripts = -00216| deployableApps: -00217| lib.foldlAttrs ( -00218| acc: appName: appCfg: -00219| let -00220| flyAppName = appCfg.deployment.fly.appName or appName; -00221| appPath = appCfg.path or "apps/${appName}"; -00222| in -00223| acc -00224| // { -00225| # Build container (nix2container) -00226| "container:build:${appName}" = -00227| "cd ../.. && nix build --impure .#packages.x86_64-linux.container-${appName}"; -00228| # Push container to Fly.io registry (dockerTools + skopeo) -00229| "container:push:${appName}" = -00230| "cd ../.. && nix run --impure .#copy-container-${appName} -- docker://registry.fly.io/ --dest-creds x:$(flyctl auth token)"; -00231| # Deploy to Fly.io (creates app if needed, uses pre-pushed image) -00232| "deploy:${appName}" = -00233| "cd ../.. && (flyctl status -a ${flyAppName} > /dev/null 2>&1 || flyctl apps create ${flyAppName} ${orgFlag}) && flyctl deploy --config ${appPath}/fly.toml --image registry.fly.io/${flyAppName}:latest"; -00234| # Full workflow -00235| "ship:${appName}" = "turbo run deploy:${appName}"; -00236| } -00237| ) { } deployableApps; -00238| -00239| # --------------------------------------------------------------------------- -00240| # Turbo tasks - Global task definitions with transit dependencies -00241| # -00242| # Workflow: build -> container:build -> container:push -> deploy -00243| # -00244| # Each app with deployment.enable has scripts generated at: -00245| # apps//.tasks/bin/{container-build,container-push,deploy} -00246| # -00247| # Apps add short wrappers in package.json that call these scripts. -00248| # Turbo runs tasks across all packages that have them. -00249| # --------------------------------------------------------------------------- -00250| -00251| # Global turbo task definitions (apply to any package with the script) -00252| globalDeployTasks = { -00253| # build:container is a separate build script that apps can customize -00254| # For Fly.io apps: runs Node/Nitro build (no ALCHEMY) -00255| # For Cloudflare apps: this task won't exist, so turbo skips it -00256| "build:container" = { -00257| description = "Build app for container deployment"; -00258| # No cache - we want fresh builds for containers -00259| cache = false; -00260| }; -00261| # Container build depends on the app's container build script -00262| "container:build" = { -00263| description = "Build container image"; -00264| cache = false; -00265| dependsOn = [ "build:container" ]; -00266| }; -00267| # Container push depends on container build -00268| "container:push" = { -00269| description = "Push container to registry"; -00270| cache = false; -00271| dependsOn = [ "container:build" ]; -00272| }; -00273| # Deploy depends on container push -00274| "deploy" = { -00275| description = "Deploy to production"; -00276| cache = false; -00277| dependsOn = [ "container:push" ]; -00278| }; -00279| }; -00280| -00281| # --------------------------------------------------------------------------- -00282| # Per-app deploy scripts (generated at apps//.tasks/bin/) -00283| # --------------------------------------------------------------------------- -00284| mkAppDeployScriptDerivations = -00285| appName: appCfg: -00286| let -00287| flyAppName = appCfg.deployment.fly.appName or appName; -00288| appPath = appCfg.path or "apps/${appName}"; -00289| flyConfigPath = "${appPath}/fly.toml"; -00290| in -00291| { -00292| # Note: container-build and container-push scripts are now generated by -00293| # the containers module for ALL apps with containers enabled (not just Fly). -00294| -00295| # Deploy to Fly -00296| "deploy" = pkgs.writeShellScriptBin "deploy" '' -00297| set -euo pipefail -00298| # Find repo root -00299| ROOT="''${STACKPANEL_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}" -00300| cd "$ROOT" -00301| -00302| # Ensure app exists (create if not) -00303| if ! flyctl status -a ${flyAppName} > /dev/null 2>&1; then -00304| echo "📱 Creating Fly.io app: ${flyAppName}..." -00305| flyctl apps create ${flyAppName} ${orgFlag} -00306| fi -00307| -00308| echo "🚀 Deploying ${appName} to Fly.io..." -00309| flyctl deploy --config ${flyConfigPath} --image "registry.fly.io/${flyAppName}:latest" -00310| echo "✅ Deployed to https://${flyAppName}.fly.dev/" -00311| ''; -00312| }; -00313| -00314| # Generate file entries for app deploy scripts -00315| mkAppDeployFileEntries = -00316| appName: appCfg: -00317| let -00318| scripts = mkAppDeployScriptDerivations appName appCfg; -00319| appPath = appCfg.path or "apps/${appName}"; -00320| in -00321| lib.mapAttrs' (scriptName: scriptDrv: { -00322| name = "${appPath}/.tasks/bin/${scriptName}"; -00323| value = { -00324| type = "symlink"; -00325| target = "${scriptDrv}/bin/${scriptName}"; -00326| source = meta.id; -00327| description = "Deploy script: ${scriptName} for ${appName}"; -00328| }; -00329| }) scripts; -00330| -00331| # Per-package turbo.json -00332| infraTurboJson = builtins.toJSON { extends = [ "//" ]; }; -00333| -00334| # --------------------------------------------------------------------------- -00335| # Generate container configs for deployable apps -00336| # These are contributed to stack.containers for nix2container builds -00337| # --------------------------------------------------------------------------- -00338| mkContainerConfigs = -00339| deployableApps: -00340| lib.mapAttrs ( -00341| appName: appCfg: -00342| let -00343| d = appCfg.deployment; -00344| c = d.container; -00345| f = d.fly; -00346| flyAppName = f.appName or appName; -00347| appPath = appCfg.path or "apps/${appName}"; -00348| in -00349| { -00350| name = flyAppName; -00351| version = "latest"; -00352| type = c.type; -00353| port = c.port; -00354| registry = "docker://registry.fly.io/"; -00355| workingDir = "/app"; -00356| # Build output path for impure builds (app built on macOS, copied to container) -00357| buildOutputPath = "${appPath}/.output"; -00358| # Fly.io auth via flyctl -00359| defaultCopyArgs = [ -00360| "--dest-creds" -00361| "x:$(flyctl auth token)" -00362| ]; -00363| # Container environment -00364| env = { -00365| PORT = toString c.port; -00366| } -00367| // f.env; -00368| } -00369| ) deployableApps; -00370| -00371| # Check if we have any deployable apps -00372| hasDeployableApps = deployableApps != { }; -00373| deployableApps = getDeployableApps cfg.apps; -00374| -00375| in -00376| { -00377| # =========================================================================== -00378| # Options -00379| # =========================================================================== -00380| options.stack.deployment = { -00381| enable = lib.mkEnableOption "deployment module" // { -00382| default = true; -00383| }; -00384| -00385| defaultHost = lib.mkOption { -00386| type = lib.types.enum [ -00387| "cloudflare" -00388| "fly" -00389| "vercel" -00390| "aws" -00391| ]; -00392| default = "cloudflare"; -00393| description = '' -00394| Default deployment host for apps that don't specify one. -00395| - cloudflare: Cloudflare Workers (edge, serverless) -00396| - fly: Fly.io (containers, VMs) -00397| - vercel: Vercel (Next.js, etc.) -00398| - aws: AWS (Lambda, ECS, etc.) -00399| ''; -00400| }; -00401| -00402| fly = { -00403| organization = lib.mkOption { -00404| type = lib.types.nullOr lib.types.str; -00405| default = null; -00406| description = "Fly.io organization slug."; -00407| }; -00408| -00409| defaultRegion = lib.mkOption { -00410| type = lib.types.str; -00411| default = "iad"; -00412| description = "Default Fly.io region for deployments."; -00413| }; -00414| }; -00415| }; -00416| -00417| # =========================================================================== -00418| # Configuration -00419| # =========================================================================== -00420| config = lib.mkMerge [ -00421| # Always add appModules (unconditionally) -00422| { -00423| stack.appModules = [ deploymentAppModule ]; -00424| } -00425| -00426| # Apply config when stack is enabled and has deployable apps -00427| (lib.mkIf (cfg.enable && deployCfg.enable && hasDeployableApps) { -00428| # NOTE: Container configs are NOT contributed here. -00429| # The containers module reads deployment.fly.* settings and applies them. -00430| # This avoids conflicts when both container.enable and deployment.enable are true. -00431| -00432| # Enable docker tooling for skopeo -00433| stack.docker.enable = true; -00434| -00435| # ------------------------------------------------------------------------- -00436| # Merge deploy scripts into SST's packages/infra/package.json -00437| # ------------------------------------------------------------------------- -00438| stack.sst.package.scripts = mkDeployPackageScripts deployableApps; -00439| -00440| # ------------------------------------------------------------------------- -00441| # Generated Files (fly.toml + per-app deploy scripts) -00442| # ------------------------------------------------------------------------- -00443| stack.files.entries = lib.mkMerge [ -00444| # packages/infra/turbo.json -00445| { -00446| "${infraPath}/turbo.json" = { -00447| type = "text"; -00448| text = infraTurboJson; -00449| source = meta.id; -00450| description = "Per-package turbo config for deploy tasks"; -00451| }; -00452| } -00453| -00454| # apps//fly.toml -00455| (lib.mapAttrs' (appName: appCfg: { -00456| name = -00457| let -00458| appPath = appCfg.path or "apps/${appName}"; -00459| in -00460| "${appPath}/fly.toml"; -00461| value = { -00462| type = "text"; -00463| text = mkFlyToml appName appCfg; -00464| source = meta.id; -00465| description = "Fly.io configuration for ${appName}"; -00466| }; -00467| }) deployableApps) -00468| -00469| # Per-app deploy scripts at apps//.tasks/bin/ -00470| (lib.mkMerge (lib.mapAttrsToList mkAppDeployFileEntries deployableApps)) -00471| ]; -00472| -00473| # ------------------------------------------------------------------------- -00474| # Turbo Tasks (global definitions) -00475| # ------------------------------------------------------------------------- -00476| stack.tasks = globalDeployTasks; -00477| -00478| # ------------------------------------------------------------------------- -00479| # Per-app package.json scripts (injected into apps via appModules) -00480| # ------------------------------------------------------------------------- -00481| # This is handled by the deploymentAppModule which adds scripts to each app -00482| -00483| # ------------------------------------------------------------------------- -00484| # Devshell Packages -00485| # ------------------------------------------------------------------------- -00486| stack.devshell.packages = [ -00487| pkgs.flyctl -00488| ]; -00489| -00490| # ------------------------------------------------------------------------- -00491| # Wrapped fly commands per app (bakes in -c and -a flags) -00492| # Usage: fly-web status, fly-web logs, fly-web ssh console, etc. -00493| # ------------------------------------------------------------------------- -00494| stack.scripts = lib.mapAttrs' ( -00495| appName: appCfg: -00496| let -00497| flyAppName = appCfg.deployment.fly.appName or appName; -00498| appPath = appCfg.path or "apps/${appName}"; -00499| configPath = "${appPath}/fly.toml"; -00500| in -00501| { -00502| name = "fly-${appName}"; -00503| value = { -00504| description = "Fly.io CLI for ${appName} (pre-configured)"; -00505| args = [ -00506| { -00507| name = "command"; -00508| description = "Flyctl command (status, logs, deploy, etc.)"; -00509| required = true; -00510| } -00511| { -00512| name = "..."; -00513| description = "Additional flyctl arguments"; -00514| } -00515| ]; -00516| exec = '' -00517| # Wrapped flyctl with pre-configured app and config -00518| # Usage: fly-${appName} [args...] -00519| # -00520| # Examples: -00521| # fly-${appName} status -00522| # fly-${appName} logs -00523| # fly-${appName} ssh console -00524| # fly-${appName} secrets list -00525| # fly-${appName} scale count 2 -00526| -00527| if [ $# -eq 0 ]; then -00528| echo "fly-${appName}: Wrapped flyctl for ${appName}" -00529| echo "" -00530| echo "Pre-configured with:" -00531| echo " --app ${flyAppName}" -00532| echo " --config ${configPath}" -00533| echo "" -00534| echo "Usage: fly-${appName} [args...]" -00535| echo "" -00536| echo "Common commands:" -00537| echo " status - Show app status" -00538| echo " logs - Stream logs" -00539| echo " ssh console - SSH into a machine" -00540| echo " secrets - Manage secrets" -00541| echo " scale - Scale machines" -00542| echo " deploy - Deploy the app" -00543| echo "" -00544| flyctl --help -00545| exit 0 -00546| fi -00547| -00548| exec flyctl --app "${flyAppName}" --config "${configPath}" "$@" -00549| ''; -00550| }; -00551| } -00552| ) deployableApps; -00553| -00554| # ------------------------------------------------------------------------- -00555| # Health Checks -00556| # ------------------------------------------------------------------------- -00557| stack.healthchecks.modules.${meta.id} = { -00558| enable = true; -00559| displayName = meta.name; -00560| checks = { -00561| flyctl-installed = { -00562| description = "Fly.io CLI is installed and accessible"; -00563| script = '' -00564| command -v flyctl >/dev/null 2>&1 && flyctl version -00565| ''; -00566| severity = "critical"; -00567| timeout = 5; -00568| }; -00569| -00570| flyctl-auth = { -00571| description = "Logged in to Fly.io"; -00572| script = '' -00573| flyctl auth whoami 2>/dev/null -00574| ''; -00575| severity = "warning"; -00576| timeout = 10; -00577| }; -00578| }; -00579| }; -00580| -00581| # ------------------------------------------------------------------------- -00582| # Module Registration -00583| # ------------------------------------------------------------------------- -00584| stack.modules.${meta.id} = { -00585| enable = true; -00586| meta = { -00587| name = meta.name; -00588| description = meta.description; -00589| icon = meta.icon; -00590| category = meta.category; -00591| author = meta.author; -00592| version = meta.version; -00593| homepage = meta.homepage; -00594| }; -00595| source.type = "builtin"; -00596| features = meta.features; -00597| tags = meta.tags; -00598| priority = meta.priority; -00599| }; -00600| }) -00601| ]; -00602| } -00603| - -(End of file - total 603 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Fly.io Deployment UI Panel Definitions -00003| # -00004| # Defines UI panels for Fly.io deployment configuration and status. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| deployCfg = cfg.deployment; -00015| -00016| # Import schema and panel generator -00017| flySchema = import ./schema.nix { inherit lib; }; -00018| panelsLib = import ../../lib/panels.nix { inherit lib; }; -00019| -00020| # Get apps configured for Fly.io deployment -00021| flyApps = lib.filterAttrs ( -00022| _name: appCfg: -00023| (appCfg.deployment.enable or false) -00024| && (appCfg.deployment.host or deployCfg.defaultHost or "fly") == "fly" -00025| ) (cfg.apps or { }); -00026| -00027| hasFlyApps = flyApps != { }; -00028| -00029| # Build app status list -00030| appStatusList = lib.mapAttrsToList ( -00031| name: appCfg: -00032| let -00033| fly = appCfg.deployment.fly or { }; -00034| in -00035| { -00036| name = name; -00037| appName = fly.appName or name; -00038| region = fly.region or deployCfg.fly.defaultRegion or "iad"; -00039| memory = fly.memory or "512mb"; -00040| } -00041| ) flyApps; -00042| in -00043| lib.mkIf hasFlyApps { -00044| # ------------------------------------------------------------------------- -00045| # Deployment Status Panel -00046| # ------------------------------------------------------------------------- -00047| stack.panels."fly-status" = { -00048| module = meta.id; -00049| title = "Fly.io Deployments"; -00050| description = "Container-based deployments on Fly.io"; -00051| type = "PANEL_TYPE_STATUS"; -00052| order = meta.priority; -00053| icon = "server"; -00054| fields = [ -00055| { -00056| name = "provider"; -00057| type = "FIELD_TYPE_STRING"; -00058| label = "Provider"; -00059| value = "Fly.io"; -00060| } -00061| { -00062| name = "organization"; -00063| type = "FIELD_TYPE_STRING"; -00064| label = "Organization"; -00065| value = deployCfg.fly.organization or "(default)"; -00066| } -00067| { -00068| name = "defaultRegion"; -00069| type = "FIELD_TYPE_STRING"; -00070| label = "Default Region"; -00071| value = deployCfg.fly.defaultRegion or "iad"; -00072| } -00073| { -00074| name = "appCount"; -00075| type = "FIELD_TYPE_NUMBER"; -00076| label = "Apps"; -00077| value = toString (lib.length (lib.attrNames flyApps)); -00078| } -00079| { -00080| name = "apps"; -00081| type = "FIELD_TYPE_JSON"; -00082| label = "Configured Apps"; -00083| value = builtins.toJSON appStatusList; -00084| } -00085| ]; -00086| }; -00087| -00088| # ------------------------------------------------------------------------- -00089| # Deployment Configuration Form -00090| # ------------------------------------------------------------------------- -00091| stack.panels."fly-config" = { -00092| module = meta.id; -00093| title = "Fly.io Settings"; -00094| description = "Configure Fly.io deployment settings"; -00095| type = "PANEL_TYPE_FORM"; -00096| order = meta.priority + 1; -00097| fields = [ -00098| { -00099| name = "organization"; -00100| type = "FIELD_TYPE_STRING"; -00101| label = "Organization"; -00102| description = "Fly.io organization name"; -00103| value = deployCfg.fly.organization or ""; -00104| configPath = "stack.deployment.fly.organization"; -00105| } -00106| { -00107| name = "defaultRegion"; -00108| type = "FIELD_TYPE_SELECT"; -00109| label = "Default Region"; -00110| description = "Default region for new deployments"; -00111| value = deployCfg.fly.defaultRegion or "iad"; -00112| configPath = "stack.deployment.fly.defaultRegion"; -00113| options = [ -00114| { -00115| value = "iad"; -00116| label = "Ashburn, Virginia (iad)"; -00117| } -00118| { -00119| value = "lax"; -00120| label = "Los Angeles (lax)"; -00121| } -00122| { -00123| value = "ord"; -00124| label = "Chicago (ord)"; -00125| } -00126| { -00127| value = "sea"; -00128| label = "Seattle (sea)"; -00129| } -00130| { -00131| value = "ewr"; -00132| label = "Secaucus, NJ (ewr)"; -00133| } -00134| { -00135| value = "lhr"; -00136| label = "London (lhr)"; -00137| } -00138| { -00139| value = "ams"; -00140| label = "Amsterdam (ams)"; -00141| } -00142| { -00143| value = "fra"; -00144| label = "Frankfurt (fra)"; -00145| } -00146| { -00147| value = "nrt"; -00148| label = "Tokyo (nrt)"; -00149| } -00150| { -00151| value = "sin"; -00152| label = "Singapore (sin)"; -00153| } -00154| { -00155| value = "syd"; -00156| label = "Sydney (syd)"; -00157| } -00158| ]; -00159| } -00160| ]; -00161| }; -00162| -00163| # ------------------------------------------------------------------------- -00164| # Per-App Deployment Table -00165| # ------------------------------------------------------------------------- -00166| stack.panels."fly-apps" = { -00167| module = meta.id; -00168| title = "Fly.io Apps"; -00169| description = "Apps deployed to Fly.io"; -00170| type = "PANEL_TYPE_TABLE"; -00171| order = meta.priority + 2; -00172| columns = [ -00173| { -00174| key = "name"; -00175| label = "App"; -00176| } -00177| { -00178| key = "appName"; -00179| label = "Fly App Name"; -00180| } -00181| { -00182| key = "region"; -00183| label = "Region"; -00184| } -00185| { -00186| key = "memory"; -00187| label = "Memory"; -00188| } -00189| ]; -00190| rows = appStatusList; -00191| }; -00192| -00193| # ------------------------------------------------------------------------- -00194| # Per-App Fly.io Configuration Panel (for Deployment tab) -00195| # Auto-generated from schema.nix SpField definitions -00196| # ------------------------------------------------------------------------- -00197| stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { -00198| module = meta.id; -00199| title = "Fly.io Configuration"; -00200| icon = meta.icon; -00201| fields = flySchema.fields; -00202| optionPrefix = "deployment.fly"; -00203| apps = flyApps; -00204| # Exclude env (hidden complex type) -00205| exclude = [ "env" ]; -00206| order = meta.priority + 3; -00207| }; -00208| } -00209| - -(End of file - total 209 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/meta.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # meta.nix - Fly.io Deployment Module Metadata -00003| # -00004| # Static metadata for fast module discovery. Contains ONLY pure data. -00005| # ============================================================================== -00006| { -00007| # Unique identifier (follows deployment- convention) -00008| id = "deployment-fly"; -00009| -00010| # Display name -00011| name = "Fly.io Deployment"; -00012| -00013| # Short description -00014| description = "Deploy containerized applications to Fly.io via devenv containers (nix2container)"; -00015| -00016| # Category for UI grouping -00017| category = "deployment"; -00018| -00019| # Semantic version -00020| version = "1.0.0"; -00021| -00022| # Lucide icon name -00023| icon = "rocket"; -00024| -00025| # Documentation link -00026| homepage = "https://fly.io/docs/"; -00027| -00028| # Author -00029| author = "Stack"; -00030| -00031| # Searchable tags -00032| tags = [ -00033| "fly" -00034| "fly.io" -00035| "deployment" -00036| "container" -00037| "nix2container" -00038| "devenv" -00039| "cloud" -00040| "hosting" -00041| ]; -00042| -00043| # Module dependencies -00044| requires = [ "containers" ]; -00045| -00046| # Conflicting modules -00047| conflicts = [ ]; -00048| -00049| # Feature flags -00050| features = { -00051| files = true; # Generates fly.toml and deploy scripts -00052| scripts = true; # Provides deploy scripts -00053| healthchecks = true; # Deployment health checks -00054| packages = true; # Creates container derivations -00055| services = false; -00056| secrets = false; -00057| tasks = true; # Generates turbo deploy tasks -00058| appModule = true; # Adds per-app deployment.* options -00059| }; -00060| -00061| # Priority for ordering (lower = higher priority) -00062| priority = 25; -00063| } -00064| - -(End of file - total 64 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/deployment/default.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # Deployment Module -00003| # -00004| # Aggregates all deployment provider modules. -00005| # -00006| # Supported hosts: -00007| # - cloudflare: Cloudflare Workers (edge, serverless) -00008| # - fly: Fly.io (containers, VMs) -00009| # - vercel: Vercel (Next.js, etc.) [planned] -00010| # - aws: AWS (Lambda, ECS, etc.) [planned] -00011| # -00012| # Each host module defines its own global options and adds per-app options -00013| # via appModules. See fly/module.nix and cloudflare/module.nix for -00014| # host-specific options. -00015| # -00016| # The actual deployment is handled by the deployment infra module at -00017| # infra/modules/deployment/, which reads each app's `framework` × `host` -00018| # config and creates the appropriate alchemy resources. -00019| # -00020| # Usage: -00021| # stack.deployment.defaultHost = "cloudflare"; -00022| # -00023| # stack.apps.web = { -00024| # framework = "tanstack-start"; -00025| # deployment = { -00026| # enable = true; -00027| # host = "cloudflare"; -00028| # bindings = [ "DATABASE_URL" "CORS_ORIGIN" ]; -00029| # secrets = [ "DATABASE_URL" ]; -00030| # }; -00031| # }; -00032| # ============================================================================== -00033| { -00034| imports = [ -00035| ./fly # Fly.io (container-based) -00036| ./cloudflare # Cloudflare Workers (edge) -00037| ]; -00038| } -00039| - -(End of file - total 39 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.5s) - -_Thinking:_ - - - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "nix/stack/modules/turbo/*" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/meta.nix -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/default.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # default.nix - Turbo Module Entry Point -00003| # -00004| # Turborepo task orchestration with turbo.json generation. -00005| # -00006| # Components: -00007| # - meta.nix: Static metadata for discovery -00008| # - module.nix: Options, task compilation, file generation -00009| # - ui.nix: UI panel definitions -00010| # -00011| # Usage: -00012| # stack.tasks = { -00013| # build = { exec = "npm run build"; outputs = [ "dist/**" ]; }; -00014| # dev = { persistent = true; cache = false; }; -00015| # }; -00016| # ============================================================================== -00017| { -00018| lib, -00019| config, -00020| pkgs, -00021| ... -00022| }: -00023| let -00024| meta = import ./meta.nix; -00025| in -00026| { -00027| imports = [ -00028| ./module.nix -00029| ./packages.nix -00030| ./ui.nix -00031| ]; -00032| } -00033| - -(End of file - total 33 lines) - -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "nix/stack/modules/*/default.nix" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/aws-vault/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/env-codegen/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/git-hooks/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/default.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.0s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # module.nix - Bun Module Implementation -00003| # -00004| # Provides Bun/TypeScript application support using bun2nix for packaging. -00005| # -00006| # Features: -00007| # - Automatic bun2nix CLI in devshell (converts bun.lock -> bun.nix) -00008| # - Generated package.json with postinstall script that runs bun2nix -00009| # - Hermetic app packaging via bun2nix.writeBunApplication -00010| # - run- and test- wrapper scripts -00011| # - Health checks for bun, bun2nix, and lockfile presence -00012| # -00013| # Prerequisites: -00014| # - bun.nix lockfile (auto-generated via postinstall or: bun2nix) -00015| # - package.json in the app or root directory -00016| # -00017| # Architecture: -00018| # Generated files (package.json) are created via stack.files system -00019| # and materialized on shell entry. package.json uses type="json" for -00020| # deep-merge support so other modules can contribute to the same file. -00021| # -00022| # App definition example: -00023| # stack.apps.my-app = { -00024| # path = "apps/web"; -00025| # bun = { -00026| # enable = true; -00027| # buildPhase = "bun run build"; -00028| # startScript = "bun run start"; -00029| # generateFiles = true; # Generates package.json with bun2nix postinstall -00030| # }; -00031| # }; -00032| # -00033| # See: https://nix-community.github.io/bun2nix/building-packages/writeBunApplication.html -00034| # ============================================================================== -00035| { -00036| lib, -00037| config, -00038| pkgs, -00039| ... -00040| }: -00041| let -00042| meta = import ./meta.nix; -00043| sp = config.stack; -00044| -00045| # Import unified field definitions (single source of truth) -00046| bunSchema = import ./schema.nix { inherit lib; }; -00047| spField = import ../../db/lib/field.nix { inherit lib; }; -00048| -00049| # Compute npm scope prefix from config (project.repo or name) -00050| # e.g., "stack" -> "@stack" -00051| prefix = sp.project.repo or sp.name; -00052| -00053| # --------------------------------------------------------------------------- -00054| # Helper Functions -00055| # --------------------------------------------------------------------------- -00056| -00057| # Filter apps to only Bun-enabled apps -00058| bunApps = lib.filterAttrs (name: app: app.bun.enable or false) sp.apps; -00059| hasBunApps = bunApps != { }; -00060| -00061| # Build a Bun app package using bun2nix.writeBunApplication -00062| # Supports both per-app bun.nix lockfile and root bun.nix layouts -00063| mkBunPackage = -00064| name: app: -00065| let -00066| bunCfg = app.bun; -00067| repoRoot = ../../../..; -00068| appPath = app.path; -00069| -00070| # Check if app has its own bun.nix lockfile (per-app layout) -00071| hasPerAppBunNix = builtins.pathExists (repoRoot + "/${appPath}/bun.nix"); -00072| -00073| # For per-app layout, use the app directory as source -00074| # For workspace layout, use repo root -00075| src = if hasPerAppBunNix then repoRoot + "/${appPath}" else repoRoot; -00076| -00077| # package.json location -00078| packageJsonPath = -00079| if hasPerAppBunNix then repoRoot + "/${appPath}/package.json" else repoRoot + "/package.json"; -00080| -00081| # bun.nix lockfile location (generated by running `bun2nix`) -00082| bunNixPath = if hasPerAppBunNix then repoRoot + "/${appPath}/bun.nix" else repoRoot + "/bun.nix"; -00083| in -00084| pkgs.bun2nix.writeBunApplication { -00085| pname = if bunCfg.binaryName != null then bunCfg.binaryName else name; -00086| version = bunCfg.version; -00087| packageJson = packageJsonPath; -00088| inherit src; -00089| -00090| buildPhase = bunCfg.buildPhase; -00091| startScript = bunCfg.startScript; -00092| runtimeInputs = bunCfg.runtimeInputs; -00093| runtimeEnv = bunCfg.runtimeEnv; -00094| inheritPath = bunCfg.inheritPath; -00095| -00096| bunDeps = pkgs.bun2nix.fetchBunDeps { bunNix = bunNixPath; }; -00097| }; -00098| -00099| # --------------------------------------------------------------------------- -00100| # File Generation (package.json with bun2nix postinstall) -00101| # --------------------------------------------------------------------------- -00102| -00103| # Generate package.json for a Bun app -00104| generatePackageJson = -00105| name: app: -00106| let -00107| bunCfg = app.bun; -00108| in -00109| { -00110| name = name; -00111| private = true; -00112| dependencies = { -00113| "@${prefix}/scripts" = "workspace:*"; -00114| }; -00115| scripts = { -00116| preinstall = "check-devshell"; -00117| postinstall = "bun2nix"; -00118| dev = "bun run --hot ${bunCfg.mainPackage}"; -00119| build = bunCfg.buildPhase; -00120| start = bunCfg.startScript; -00121| test = "bun test"; -00122| }; -00123| }; -00124| -00125| # Create file entries for materialization (uses stack.files system) -00126| # package.json uses type="json" for deep-merge support from other modules -00127| mkGeneratedFileEntries = name: app: { -00128| "${app.path}/package.json" = { -00129| type = "json"; -00130| jsonValue = generatePackageJson name app; -00131| source = "bun"; -00132| description = "Bun app package.json (scripts, dependencies, bun2nix postinstall)"; -00133| }; -00134| }; -00135| -00136| in -00137| { -00138| # =========================================================================== -00139| # Options -00140| # =========================================================================== -00141| # Expose Bun packages for nix build (separate from modules attrsOf) -00142| options.stack.bun.packages = lib.mkOption { -00143| type = lib.types.attrsOf lib.types.unspecified; -00144| default = { }; -00145| description = '' -00146| Bun packages for apps with bun.enable = true. -00147| These are exposed for `nix build` but NOT included in devshell. -00148| Access via config.stack.bun.packages.apps.. -00149| ''; -00150| }; -00151| -00152| # =========================================================================== -00153| # Configuration -00154| # =========================================================================== -00155| config = lib.mkMerge [ -00156| # Always register appModules (unconditionally) -00157| # Options are auto-generated from bun-app.proto.nix (single source of truth) -00158| # runtimeInputs is Nix-only (listOf package - no proto equivalent) -00159| { -00160| stack.appModules = [ -00161| ( -00162| { lib, ... }: -00163| { -00164| options.bun = lib.mkOption { -00165| type = lib.types.submodule { -00166| options = lib.mapAttrs (_: spField.asOption) bunSchema.fields // { -00167| # Nix-only option: package references can't be proto fields -00168| runtimeInputs = lib.mkOption { -00169| type = lib.types.listOf lib.types.package; -00170| default = [ ]; -00171| description = "Runtime nix dependencies"; -00172| }; -00173| }; -00174| }; -00175| default = { }; -00176| description = "Bun-specific configuration for this app"; -00177| }; -00178| } -00179| ) -00180| ]; -00181| } -00182| -00183| # Apply config when stack is enabled and bun apps exist -00184| (lib.mkIf (sp.enable && hasBunApps) { -00185| # ----------------------------------------------------------------------- -00186| # Packages - Build Bun applications via writeBunApplication -00187| # ----------------------------------------------------------------------- -00188| stack.bun.packages = { -00189| apps = lib.mapAttrs mkBunPackage bunApps; -00190| }; -00191| -00192| # ----------------------------------------------------------------------- -00193| # Devshell - Add bun2nix CLI to shell environment -00194| # ----------------------------------------------------------------------- -00195| stack.devshell.packages = [ -00196| pkgs.bun2nix # Native bun2nix CLI (converts bun.lock -> bun.nix) -00197| ]; -00198| -00199| # ----------------------------------------------------------------------- -00200| # File Generation - package.json with bun2nix postinstall -00201| # ----------------------------------------------------------------------- -00202| stack.files.entries = lib.mkMerge ( -00203| lib.mapAttrsToList ( -00204| name: app: lib.optionalAttrs app.bun.generateFiles (mkGeneratedFileEntries name app) -00205| ) bunApps -00206| ); -00207| -00208| # ----------------------------------------------------------------------- -00209| # Scripts - Add run- and test- commands -00210| # ----------------------------------------------------------------------- -00211| stack.scripts = lib.mkMerge ( -00212| lib.mapAttrsToList (name: app: { -00213| "run-${name}" = { -00214| exec = ''cd "$STACKPANEL_ROOT/${app.path}" && exec bun run ${app.bun.mainPackage} "$@"''; -00215| runtimeInputs = [ pkgs.bun ]; -00216| description = "Run ${name} Bun app"; -00217| args = [ -00218| { -00219| name = "..."; -00220| description = "Arguments passed to the bun script"; -00221| } -00222| ]; -00223| }; -00224| "test-${name}" = { -00225| exec = ''cd "$STACKPANEL_ROOT/${app.path}" && exec bun test "$@"''; -00226| runtimeInputs = [ pkgs.bun ]; -00227| description = "Test ${name} Bun app"; -00228| args = [ -00229| { -00230| name = "..."; -00231| description = "Arguments passed to bun test"; -00232| } -00233| ]; -00234| }; -00235| }) bunApps -00236| ); -00237| -00238| # ----------------------------------------------------------------------- -00239| # Flake Checks (CI) -00240| # ----------------------------------------------------------------------- -00241| stack.moduleChecks.${meta.id} = { -00242| eval = { -00243| description = "${meta.name} module evaluates correctly"; -00244| required = true; -00245| derivation = pkgs.runCommand "${meta.id}-eval-check" { } '' -00246| echo "Bun module evaluates successfully" -00247| touch $out -00248| ''; -00249| }; -00250| -00251| packages = { -00252| description = "${meta.name} packages are available"; -00253| required = true; -00254| derivation = pkgs.runCommand "${meta.id}-packages-check" { nativeBuildInputs = [ pkgs.bun ]; } '' -00255| bun --version -00256| echo "Bun runtime available" -00257| touch $out -00258| ''; -00259| }; -00260| }; -00261| -00262| # ----------------------------------------------------------------------- -00263| # Health Checks (Runtime) -00264| # ----------------------------------------------------------------------- -00265| stack.healthchecks.modules.${meta.id} = { -00266| enable = true; -00267| displayName = meta.name; -00268| checks = { -00269| bun-installed = { -00270| description = "Bun runtime is installed and accessible"; -00271| script = '' -00272| command -v bun >/dev/null 2>&1 && bun --version -00273| ''; -00274| severity = "critical"; -00275| timeout = 5; -00276| }; -00277| -00278| bun-version = { -00279| description = "Bun version is 1.2 or newer"; -00280| script = '' -00281| version=$(bun --version 2>/dev/null) -00282| major=$(echo "$version" | cut -d. -f1) -00283| minor=$(echo "$version" | cut -d. -f2) -00284| [ "$major" -gt 1 ] || ([ "$major" -eq 1 ] && [ "$minor" -ge 2 ]) -00285| ''; -00286| severity = "warning"; -00287| timeout = 5; -00288| }; -00289| -00290| bun2nix-installed = { -00291| description = "bun2nix CLI is installed and accessible"; -00292| script = '' -00293| command -v bun2nix >/dev/null 2>&1 && bun2nix --version -00294| ''; -00295| severity = "warning"; -00296| timeout = 5; -00297| }; -00298| -00299| lockfile-exists = { -00300| description = "bun.nix lockfile exists for Nix builds"; -00301| script = '' -00302| STACKPANEL_ROOT="''${STACKPANEL_ROOT:-$(pwd)}" -00303| test -f "$STACKPANEL_ROOT/bun.nix" || \ -00304| find "$STACKPANEL_ROOT/apps" -name "bun.nix" -type f | head -1 | grep -q . -00305| ''; -00306| severity = "warning"; -00307| timeout = 5; -00308| }; -00309| }; -00310| }; -00311| -00312| # ----------------------------------------------------------------------- -00313| # Module Registration -00314| # ----------------------------------------------------------------------- -00315| stack.modules.${meta.id} = { -00316| enable = true; -00317| meta = { -00318| name = meta.name; -00319| description = meta.description; -00320| icon = meta.icon; -00321| category = meta.category; -00322| author = meta.author; -00323| version = meta.version; -00324| homepage = meta.homepage; -00325| }; -00326| source.type = "builtin"; -00327| features = meta.features; -00328| tags = meta.tags; -00329| priority = meta.priority; -00330| healthcheckModule = meta.id; -00331| }; -00332| }) -00333| ]; -00334| } -00335| - -(End of file - total 335 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Bun UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # -00006| # The APP_CONFIG panel is auto-generated from the SpField definitions in -00007| # bun-app.proto.nix. No manual field listing needed - the schema is the -00008| # single source of truth for both Nix options and UI panels. -00009| # ============================================================================== -00010| { -00011| lib, -00012| config, -00013| pkgs, -00014| ... -00015| }: -00016| let -00017| meta = import ./meta.nix; -00018| cfg = config.stack; -00019| -00020| # Import field definitions and panel generator -00021| bunSchema = import ./schema.nix { inherit lib; }; -00022| panelsLib = import ../../lib/panels.nix { inherit lib; }; -00023| -00024| # Filter apps to only Bun-enabled apps -00025| bunApps = lib.filterAttrs (name: app: app.bun.enable or false) (cfg.apps or {}); -00026| hasBunApps = bunApps != {}; -00027| in -00028| lib.mkIf (cfg.enable && hasBunApps) { -00029| # --------------------------------------------------------------------------- -00030| # Status Panel - Overview of Bun environment -00031| # (Hand-crafted: uses runtime data like pkgs.bun.version) -00032| # --------------------------------------------------------------------------- -00033| stack.panels."${meta.id}-status" = { -00034| module = meta.id; -00035| title = "Bun Environment"; -00036| description = meta.description; -00037| icon = meta.icon; -00038| type = "PANEL_TYPE_STATUS"; -00039| order = meta.priority; -00040| fields = [ -00041| { -00042| name = "metrics"; -00043| type = "FIELD_TYPE_STRING"; -00044| value = builtins.toJSON [ -00045| { -00046| label = "Bun Version"; -00047| value = pkgs.bun.version; -00048| status = "ok"; -00049| } -00050| { -00051| label = "Apps"; -00052| value = toString (lib.length (lib.attrNames bunApps)); -00053| status = if bunApps != { } then "ok" else "warning"; -00054| } -00055| ]; -00056| } -00057| ]; -00058| }; -00059| -00060| # --------------------------------------------------------------------------- -00061| # App Config Panel - Per-app Bun configuration -00062| # (Auto-generated from bun-app.proto.nix SpField definitions) -00063| # --------------------------------------------------------------------------- -00064| stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { -00065| module = meta.id; -00066| title = "Bun Configuration"; -00067| icon = meta.icon; -00068| fields = bunSchema.fields; -00069| optionPrefix = "bun"; -00070| apps = bunApps; -00071| exclude = [ "enable" "generateFiles" ]; -00072| order = meta.priority + 2; -00073| }; -00074| } -00075| - -(End of file - total 75 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/meta.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # meta.nix - Bun Module Metadata -00003| # -00004| # Static metadata for fast module discovery. Contains ONLY pure data. -00005| # ============================================================================== -00006| { -00007| # Unique identifier (matches directory name) -00008| id = "bun"; -00009| -00010| # Display name -00011| name = "Bun"; -00012| -00013| # Short description -00014| description = "Bun/TypeScript application support with bun2nix packaging"; -00015| -00016| # Category for UI grouping -00017| category = "language"; -00018| -00019| # Semantic version -00020| version = "1.0.0"; -00021| -00022| # Lucide icon name -00023| icon = "zap"; -00024| -00025| # Documentation link -00026| homepage = "https://nix-community.github.io/bun2nix/building-packages/writeBunApplication.html"; -00027| -00028| # Author -00029| author = "Stack"; -00030| -00031| # Searchable tags -00032| tags = [ -00033| "bun" -00034| "bun2nix" -00035| "typescript" -00036| "javascript" -00037| "nodejs" -00038| "runtime" -00039| "package-manager" -00040| ]; -00041| -00042| # Module dependencies -00043| requires = [ ]; -00044| -00045| # Conflicting modules -00046| conflicts = [ ]; -00047| -00048| # Feature flags -00049| features = { -00050| files = true; # Generates package.json with bun2nix postinstall -00051| scripts = true; # Provides run- and test- scripts -00052| healthchecks = true; -00053| packages = true; # Builds Bun applications via bun2nix -00054| services = false; -00055| secrets = false; -00056| tasks = false; -00057| appModule = true; # Adds per-app bun.* options -00058| }; -00059| -00060| # Priority for ordering (lower = higher priority) -00061| priority = 20; -00062| } -00063| - -(End of file - total 63 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/default.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # default.nix - Bun Module Entry Point -00003| # -00004| # Bun/TypeScript application support using bun2nix for hermetic packaging. -00005| # -00006| # Components: -00007| # - meta.nix: Static metadata for discovery -00008| # - module.nix: Options, packages, scripts, health checks -00009| # - ui.nix: UI panel definitions -00010| # -00011| # Usage: -00012| # stack.modules.bun.enable = true; -00013| # stack.apps.my-app = { -00014| # path = "apps/web"; -00015| # bun.enable = true; -00016| # }; -00017| # ============================================================================== -00018| { -00019| lib, -00020| config, -00021| pkgs, -00022| ... -00023| }: -00024| let -00025| meta = import ./meta.nix; -00026| in -00027| { -00028| imports = [ -00029| ./module.nix -00030| ./ui.nix -00031| ]; -00032| } -00033| - -(End of file - total 33 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.9s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # module.nix - CI Formatters Module Implementation -00003| # -00004| # Flake checks for formatter tooling. -00005| # Runs wrapped formatters in a writable copy of the repo for CI usage. -00006| # ============================================================================== -00007| { -00008| lib, -00009| config, -00010| pkgs, -00011| ... -00012| }: -00013| let -00014| meta = import ./meta.nix; -00015| cfg = config.stack; -00016| appsComputed = cfg.appsComputed or { }; -00017| repoRoot = ../../../..; -00018| -00019| formatters = lib.flatten ( -00020| lib.mapAttrsToList (_: app: app.wrappedTooling.formatters or [ ]) appsComputed -00021| ); -00022| -00023| formatterCheck = -00024| pkgs.runCommand "stack-formatters" -00025| { -00026| nativeBuildInputs = formatters; -00027| } -00028| '' -00029| if [ "${toString (formatters == [ ])}" = "1" ]; then -00030| touch "$out" -00031| exit 0 -00032| fi -00033| -00034| export STACKPANEL_ROOT="$PWD/src" -00035| cp -R ${repoRoot} "$STACKPANEL_ROOT" -00036| chmod -R u+w "$STACKPANEL_ROOT" -00037| cd "$STACKPANEL_ROOT" -00038| -00039| ${lib.concatMapStringsSep "\n" (tool: "${lib.getExe tool}") formatters} -00040| -00041| touch "$out" -00042| ''; -00043| in -00044| { -00045| # =========================================================================== -00046| # Configuration -00047| # =========================================================================== -00048| config = lib.mkIf cfg.enable { -00049| stack.checks = lib.optionalAttrs (formatters != [ ]) { -00050| formatters = formatterCheck; -00051| }; -00052| -00053| # Register module -00054| stack.modules.${meta.id} = { -00055| enable = true; -00056| meta = { -00057| name = meta.name; -00058| description = meta.description; -00059| icon = meta.icon; -00060| category = meta.category; -00061| author = meta.author; -00062| version = meta.version; -00063| homepage = meta.homepage; -00064| }; -00065| source.type = "builtin"; -00066| features = meta.features; -00067| tags = meta.tags; -00068| priority = meta.priority; -00069| }; -00070| }; -00071| } -00072| - -(End of file - total 72 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - CI Formatters UI Panel Definitions -00003| # -00004| # Defines the panels that appear in the Stack UI for this module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| appsComputed = cfg.appsComputed or { }; -00015| -00016| formatters = lib.flatten ( -00017| lib.mapAttrsToList (_: app: app.wrappedTooling.formatters or [ ]) appsComputed -00018| ); -00019| in -00020| lib.mkIf (cfg.enable && formatters != [ ]) { -00021| stack.panels."${meta.id}-status" = { -00022| module = meta.id; -00023| title = "CI Formatters"; -00024| description = meta.description; -00025| type = "PANEL_TYPE_STATUS"; -00026| order = meta.priority; -00027| fields = [ -00028| { -00029| name = "formatters"; -00030| type = "FIELD_TYPE_JSON"; -00031| value = builtins.toJSON (map (f: f.name or "unknown") formatters); -00032| } -00033| ]; -00034| }; -00035| } -00036| - -(End of file - total 36 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/ci-formatters/meta.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # meta.nix - CI Formatters Module Metadata -00003| # -00004| # Static metadata for fast module discovery. Contains ONLY pure data. -00005| # ============================================================================== -00006| { -00007| # Unique identifier (matches directory name) -00008| id = "ci-formatters"; -00009| -00010| # Display name -00011| name = "CI Formatters"; -00012| -00013| # Short description -00014| description = "Flake checks for formatter tooling in CI"; -00015| -00016| # Category for UI grouping -00017| category = "ci-cd"; -00018| -00019| # Semantic version -00020| version = "1.0.0"; -00021| -00022| # Lucide icon name -00023| icon = "check-circle"; -00024| -00025| # Documentation link -00026| homepage = null; -00027| -00028| # Author -00029| author = "Stack"; -00030| -00031| # Searchable tags -00032| tags = [ -00033| "ci" -00034| "formatters" -00035| "checks" -00036| "flake" -00037| ]; -00038| -00039| # Module dependencies -00040| requires = [ ]; -00041| -00042| # Conflicting modules -00043| conflicts = [ ]; -00044| -00045| # Feature flags -00046| features = { -00047| files = false; -00048| scripts = false; -00049| healthchecks = false; -00050| packages = false; -00051| services = false; -00052| secrets = false; -00053| tasks = false; -00054| appModule = false; -00055| }; -00056| -00057| # Priority for ordering (lower = higher priority) -00058| priority = 90; -00059| } -00060| - -(End of file - total 60 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.2s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # ui.nix - Cloudflare Deployment UI Panel Definitions -00003| # -00004| # Defines UI panels for Cloudflare Workers deployment configuration and status. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| meta = import ./meta.nix; -00013| cfg = config.stack; -00014| deployCfg = cfg.deployment; -00015| -00016| # Import schema and panel generator -00017| cloudflareSchema = import ./schema.nix { inherit lib; }; -00018| panelsLib = import ../../lib/panels.nix { inherit lib; }; -00019| -00020| # Get apps configured for Cloudflare deployment -00021| cloudflareApps = lib.filterAttrs ( -00022| _name: appCfg: -00023| (appCfg.deployment.enable or false) -00024| && (appCfg.deployment.host or deployCfg.defaultHost or "cloudflare") == "cloudflare" -00025| ) (cfg.apps or { }); -00026| -00027| hasCloudflareApps = cloudflareApps != { }; -00028| -00029| # Derive active framework name from an app config -00030| frameworkNames = [ -00031| "tanstack-start" -00032| "nextjs" -00033| "vite" -00034| "hono" -00035| "astro" -00036| "remix" -00037| "nuxt" -00038| ]; -00039| getFramework = -00040| appCfg: -00041| let -00042| enabled = lib.filter (fw: appCfg.framework.${fw}.enable or false) frameworkNames; -00043| in -00044| if enabled == [ ] then "-" else lib.head enabled; -00045| -00046| # Build app status list -00047| appStatusList = lib.mapAttrsToList ( -00048| name: appCfg: -00049| let -00050| cf = appCfg.deployment.cloudflare or { }; -00051| in -00052| { -00053| name = name; -00054| workerName = cf.workerName or name; -00055| type = getFramework appCfg; -00056| route = if cf.route or null != null then cf.route else "-"; -00057| } -00058| ) cloudflareApps; -00059| in -00060| lib.mkIf hasCloudflareApps { -00061| # ------------------------------------------------------------------------- -00062| # Deployment Status Panel -00063| # ------------------------------------------------------------------------- -00064| stack.panels."cloudflare-status" = { -00065| module = meta.id; -00066| title = "Cloudflare Workers"; -00067| description = "Edge deployments on Cloudflare Workers"; -00068| type = "PANEL_TYPE_STATUS"; -00069| order = meta.priority; -00070| icon = "cloud"; -00071| fields = [ -00072| { -00073| name = "provider"; -00074| type = "FIELD_TYPE_STRING"; -00075| label = "Provider"; -00076| value = "Cloudflare Workers"; -00077| } -00078| { -00079| name = "accountId"; -00080| type = "FIELD_TYPE_STRING"; -00081| label = "Account ID"; -00082| value = -00083| if deployCfg.cloudflare.accountId != null then -00084| lib.substring 0 8 deployCfg.cloudflare.accountId + "..." -00085| else -00086| "(from env)"; -00087| } -00088| { -00089| name = "compatibilityDate"; -00090| type = "FIELD_TYPE_STRING"; -00091| label = "Compatibility Date"; -00092| value = deployCfg.cloudflare.compatibilityDate or "2024-01-01"; -00093| } -00094| { -00095| name = "appCount"; -00096| type = "FIELD_TYPE_NUMBER"; -00097| label = "Workers"; -00098| value = toString (lib.length (lib.attrNames cloudflareApps)); -00099| } -00100| { -00101| name = "apps"; -00102| type = "FIELD_TYPE_JSON"; -00103| label = "Configured Apps"; -00104| value = builtins.toJSON appStatusList; -00105| } -00106| ]; -00107| }; -00108| -00109| # ------------------------------------------------------------------------- -00110| # Deployment Configuration Form -00111| # ------------------------------------------------------------------------- -00112| stack.panels."cloudflare-config" = { -00113| module = meta.id; -00114| title = "Cloudflare Settings"; -00115| description = "Configure Cloudflare Workers deployment settings"; -00116| type = "PANEL_TYPE_FORM"; -00117| order = meta.priority + 1; -00118| fields = [ -00119| { -00120| name = "accountId"; -00121| type = "FIELD_TYPE_STRING"; -00122| label = "Account ID"; -00123| description = "Cloudflare account ID (from dashboard)"; -00124| value = deployCfg.cloudflare.accountId or ""; -00125| configPath = "stack.deployment.cloudflare.accountId"; -00126| } -00127| { -00128| name = "compatibilityDate"; -00129| type = "FIELD_TYPE_STRING"; -00130| label = "Compatibility Date"; -00131| description = "Workers API compatibility date"; -00132| value = deployCfg.cloudflare.compatibilityDate or "2024-01-01"; -00133| configPath = "stack.deployment.cloudflare.compatibilityDate"; -00134| } -00135| { -00136| name = "defaultRoute"; -00137| type = "FIELD_TYPE_STRING"; -00138| label = "Default Route"; -00139| description = "Default custom domain pattern (e.g., *.example.com/*)"; -00140| value = deployCfg.cloudflare.defaultRoute or ""; -00141| configPath = "stack.deployment.cloudflare.defaultRoute"; -00142| } -00143| ]; -00144| }; -00145| -00146| # ------------------------------------------------------------------------- -00147| # Per-App Workers Table -00148| # ------------------------------------------------------------------------- -00149| stack.panels."cloudflare-apps" = { -00150| module = meta.id; -00151| title = "Cloudflare Workers"; -00152| description = "Apps deployed as Cloudflare Workers"; -00153| type = "PANEL_TYPE_TABLE"; -00154| order = meta.priority + 2; -00155| columns = [ -00156| { -00157| key = "name"; -00158| label = "App"; -00159| } -00160| { -00161| key = "workerName"; -00162| label = "Worker Name"; -00163| } -00164| { -00165| key = "type"; -00166| label = "Type"; -00167| } -00168| { -00169| key = "route"; -00170| label = "Route"; -00171| } -00172| ]; -00173| rows = appStatusList; -00174| }; -00175| -00176| # ------------------------------------------------------------------------- -00177| # Per-App Cloudflare Configuration Panel (for Deployment tab) -00178| # Auto-generated from schema.nix SpField definitions -00179| # ------------------------------------------------------------------------- -00180| stack.panels."${meta.id}-app-config" = panelsLib.mkPanelFromSpFields { -00181| module = meta.id; -00182| title = "Cloudflare Configuration"; -00183| icon = meta.ui.icon or "cloud"; -00184| fields = cloudflareSchema.fields; -00185| optionPrefix = "deployment.cloudflare"; -00186| apps = cloudflareApps; -00187| # Exclude complex types -00188| exclude = [ -00189| "bindings" -00190| "secrets" -00191| "kvNamespaces" -00192| "d1Databases" -00193| "r2Buckets" -00194| ]; -00195| order = meta.priority + 3; -00196| }; -00197| } -00198| - -(End of file - total 198 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/core/options/modules.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # modules.nix -00003| # -00004| # Stack Module System - the unified system for extending stack. -00005| # -00006| # Modules are the primary way to extend stack functionality. They can: -00007| # - Add devshell packages, hooks, and environment variables -00008| # - Generate files via stack.files.entries -00009| # - Provide shell scripts/commands via stack.scripts -00010| # - Define health checks via stack.healthchecks -00011| # - Register UI panels for the web studio -00012| # - Extend per-app configuration via stack.appModules -00013| # -00014| # Modules can be: -00015| # - Builtin: Shipped with stack (e.g., postgres, redis, step-ca) -00016| # - Local: Defined in the project repository -00017| # - Remote: Installed from flake inputs or a module registry -00018| # -00019| # Example module definition: -00020| # -00021| # stack.modules.myModule = { -00022| # enable = true; -00023| # meta = { -00024| # name = "My Module"; -00025| # description = "Does something useful"; -00026| # icon = "box"; -00027| # category = "development"; -00028| # }; -00029| # features.scripts = true; -00030| # panels = [{ -00031| # id = "myModule-status"; -00032| # title = "My Module Status"; -00033| # type = "PANEL_TYPE_STATUS"; -00034| # fields = [{ name = "metrics"; type = "FIELD_TYPE_JSON"; value = "..."; }]; -00035| # }]; -00036| # }; -00037| # -00038| # This module replaces the older stack.extensions system. -00039| # For backward compatibility, stack.extensions is aliased to stack.modules. -00040| # ============================================================================== -00041| { -00042| lib, -00043| config, -00044| ... -00045| }: -00046| let -00047| cfg = config.stack; -00048| -00049| # ============================================================================ -00050| # Type Definitions -00051| # ============================================================================ -00052| -00053| # Module source types -00054| sourceTypeEnum = lib.types.enum [ -00055| "builtin" # Shipped with stack -00056| "local" # Defined in project -00057| "flake-input" # Installed via flake input -00058| "registry" # Installed from module registry -00059| ]; -00060| -00061| # Module categories for UI grouping -00062| categoryEnum = lib.types.enum [ -00063| "unspecified" -00064| "infrastructure" -00065| "ci-cd" -00066| "database" -00067| "secrets" -00068| "deployment" -00069| "development" -00070| "monitoring" -00071| "integration" -00072| "language" # Language-specific tooling (go, bun, python, etc.) -00073| "service" # Background services (postgres, redis, etc.) -00074| ]; -00075| -00076| # Panel types for UI rendering (matches extension-panels) -00077| panelTypeEnum = lib.types.enum [ -00078| "PANEL_TYPE_UNSPECIFIED" -00079| "PANEL_TYPE_STATUS" -00080| "PANEL_TYPE_APPS_GRID" -00081| "PANEL_TYPE_FORM" -00082| "PANEL_TYPE_TABLE" -00083| "PANEL_TYPE_CUSTOM" -00084| ]; -00085| -00086| # Field types for panel configuration -00087| fieldTypeEnum = lib.types.enum [ -00088| "FIELD_TYPE_UNSPECIFIED" -00089| "FIELD_TYPE_STRING" -00090| "FIELD_TYPE_NUMBER" -00091| "FIELD_TYPE_BOOLEAN" -00092| "FIELD_TYPE_SELECT" -00093| "FIELD_TYPE_MULTISELECT" -00094| "FIELD_TYPE_APP_FILTER" -00095| "FIELD_TYPE_COLUMNS" -00096| "FIELD_TYPE_JSON" -00097| ]; -00098| -00099| # ============================================================================ -00100| # Submodule Types -00101| # ============================================================================ -00102| -00103| # Module metadata -00104| moduleMetaType = lib.types.submodule { -00105| options = { -00106| name = lib.mkOption { -00107| type = lib.types.str; -00108| description = "Display name of the module"; -00109| }; -00110| description = lib.mkOption { -00111| type = lib.types.nullOr lib.types.str; -00112| default = null; -00113| description = "Human-readable description of what the module does"; -00114| }; -00115| icon = lib.mkOption { -00116| type = lib.types.nullOr lib.types.str; -00117| default = null; -00118| description = "Lucide icon name for the module (e.g., 'database', 'box', 'cloud')"; -00119| }; -00120| category = lib.mkOption { -00121| type = categoryEnum; -00122| default = "unspecified"; -00123| description = "Category for grouping in the UI"; -00124| }; -00125| author = lib.mkOption { -00126| type = lib.types.nullOr lib.types.str; -00127| default = null; -00128| description = "Author or maintainer of the module"; -00129| }; -00130| version = lib.mkOption { -00131| type = lib.types.nullOr lib.types.str; -00132| default = null; -00133| description = "Module version"; -00134| }; -00135| homepage = lib.mkOption { -00136| type = lib.types.nullOr lib.types.str; -00137| default = null; -00138| description = "URL to module documentation or repository"; -00139| }; -00140| }; -00141| }; -00142| -00143| # Module source configuration -00144| moduleSourceType = lib.types.submodule { -00145| options = { -00146| type = lib.mkOption { -00147| type = sourceTypeEnum; -00148| default = "builtin"; -00149| description = "Source type for the module"; -00150| }; -00151| flakeInput = lib.mkOption { -00152| type = lib.types.nullOr lib.types.str; -00153| default = null; -00154| description = "Name of the flake input (for flake-input source type)"; -00155| }; -00156| path = lib.mkOption { -00157| type = lib.types.nullOr lib.types.str; -00158| default = null; -00159| description = "Local path to the module (for local source type)"; -00160| }; -00161| registryId = lib.mkOption { -00162| type = lib.types.nullOr lib.types.str; -00163| default = null; -00164| description = "Registry identifier (for registry source type, e.g., 'stack/docker')"; -00165| }; -00166| ref = lib.mkOption { -00167| type = lib.types.nullOr lib.types.str; -00168| default = null; -00169| description = "Git ref (branch, tag, commit) for remote modules"; -00170| }; -00171| }; -00172| }; -00173| -00174| # Module feature flags - which stack features this module uses -00175| moduleFeaturesType = lib.types.submodule { -00176| options = { -00177| files = lib.mkOption { -00178| type = lib.types.bool; -00179| default = false; -00180| description = "Module generates files via stack.files"; -00181| }; -00182| scripts = lib.mkOption { -00183| type = lib.types.bool; -00184| default = false; -00185| description = "Module provides shell scripts/commands"; -00186| }; -00187| tasks = lib.mkOption { -00188| type = lib.types.bool; -00189| default = false; -00190| description = "Module defines turborepo tasks"; -00191| }; -00192| healthchecks = lib.mkOption { -00193| type = lib.types.bool; -00194| default = false; -00195| description = "Module defines health checks"; -00196| }; -00197| services = lib.mkOption { -00198| type = lib.types.bool; -00199| default = false; -00200| description = "Module configures background services/processes"; -00201| }; -00202| secrets = lib.mkOption { -00203| type = lib.types.bool; -00204| default = false; -00205| description = "Module manages secrets/variables"; -00206| }; -00207| packages = lib.mkOption { -00208| type = lib.types.bool; -00209| default = false; -00210| description = "Module adds devshell packages"; -00211| }; -00212| appModule = lib.mkOption { -00213| type = lib.types.bool; -00214| default = false; -00215| description = "Module extends per-app configuration via appModules"; -00216| }; -00217| }; -00218| }; -00219| -00220| # Panel field type -00221| panelFieldType = lib.types.submodule { -00222| options = { -00223| name = lib.mkOption { -00224| type = lib.types.str; -00225| description = "Field name (maps to component prop)"; -00226| }; -00227| type = lib.mkOption { -00228| type = fieldTypeEnum; -00229| default = "FIELD_TYPE_STRING"; -00230| description = "Field type"; -00231| }; -00232| value = lib.mkOption { -00233| type = lib.types.str; -00234| default = ""; -00235| description = "Field value (JSON-encoded for complex types)"; -00236| }; -00237| options = lib.mkOption { -00238| type = lib.types.listOf lib.types.str; -00239| default = [ ]; -00240| description = "Options for select fields"; -00241| }; -00242| }; -00243| }; -00244| -00245| # Module panel type (for UI rendering) -00246| modulePanelType = lib.types.submodule { -00247| options = { -00248| id = lib.mkOption { -00249| type = lib.types.str; -00250| description = "Unique panel identifier"; -00251| }; -00252| title = lib.mkOption { -00253| type = lib.types.str; -00254| description = "Display title"; -00255| }; -00256| description = lib.mkOption { -00257| type = lib.types.nullOr lib.types.str; -00258| default = null; -00259| description = "Panel description"; -00260| }; -00261| type = lib.mkOption { -00262| type = panelTypeEnum; -00263| default = "PANEL_TYPE_STATUS"; -00264| description = "Panel type (determines which component to render)"; -00265| }; -00266| order = lib.mkOption { -00267| type = lib.types.int; -00268| default = 100; -00269| description = "Display order (lower = first)"; -00270| }; -00271| fields = lib.mkOption { -00272| type = lib.types.listOf panelFieldType; -00273| default = [ ]; -00274| description = "Panel configuration fields"; -00275| }; -00276| }; -00277| }; -00278| -00279| # Per-app module data type -00280| moduleAppDataType = lib.types.submodule { -00281| options = { -00282| enabled = lib.mkOption { -00283| type = lib.types.bool; -00284| default = true; -00285| description = "Whether module is enabled for this app"; -00286| }; -00287| config = lib.mkOption { -00288| type = lib.types.attrsOf lib.types.str; -00289| default = { }; -00290| description = "Module config for this app (string key-value pairs)"; -00291| }; -00292| }; -00293| }; -00294| -00295| # ============================================================================ -00296| # Main Module Type -00297| # ============================================================================ -00298| -00299| moduleType = lib.types.submodule ( -00300| { name, ... }: -00301| { -00302| options = { -00303| # Standard enable flag -00304| enable = lib.mkEnableOption "the ${name} module" // { -00305| default = false; -00306| }; -00307| -00308| # Module metadata -00309| meta = lib.mkOption { -00310| type = moduleMetaType; -00311| default = { -00312| name = name; -00313| }; -00314| description = "Module metadata for display in the UI"; -00315| }; -00316| -00317| # Source information -00318| source = lib.mkOption { -00319| type = moduleSourceType; -00320| default = { }; -00321| description = "Module source configuration"; -00322| }; -00323| -00324| # Feature flags -00325| features = lib.mkOption { -00326| type = moduleFeaturesType; -00327| default = { }; -00328| description = "Which stack features this module uses"; -00329| }; -00330| -00331| # Dependencies -00332| requires = lib.mkOption { -00333| type = lib.types.listOf lib.types.str; -00334| default = [ ]; -00335| description = "Other modules that must be enabled for this module to work"; -00336| }; -00337| -00338| conflicts = lib.mkOption { -00339| type = lib.types.listOf lib.types.str; -00340| default = [ ]; -00341| description = "Other modules that conflict with this module"; -00342| }; -00343| -00344| # Load order -00345| priority = lib.mkOption { -00346| type = lib.types.int; -00347| default = 100; -00348| description = "Load order priority (lower = earlier)"; -00349| }; -00350| -00351| # Tags for filtering -00352| tags = lib.mkOption { -00353| type = lib.types.listOf lib.types.str; -00354| default = [ ]; -00355| description = "Tags for categorizing/filtering modules"; -00356| }; -00357| -00358| # Configuration schema for UI form generation -00359| configSchema = lib.mkOption { -00360| type = lib.types.nullOr lib.types.str; -00361| default = null; -00362| description = '' -00363| JSON Schema string for generating configuration forms in the UI. -00364| The schema should describe the module's configurable options. -00365| ''; -00366| example = '' -00367| { -00368| "type": "object", -00369| "properties": { -00370| "port": { "type": "integer", "default": 5432 }, -00371| "version": { "type": "string", "enum": ["15", "16"], "default": "16" } -00372| } -00373| } -00374| ''; -00375| }; -00376| -00377| # UI panels -00378| panels = lib.mkOption { -00379| type = lib.types.listOf modulePanelType; -00380| default = [ ]; -00381| description = "UI panels provided by this module"; -00382| }; -00383| -00384| # Per-app data -00385| apps = lib.mkOption { -00386| type = lib.types.attrsOf moduleAppDataType; -00387| default = { }; -00388| description = "Per-app module data (app name -> module data)"; -00389| }; -00390| -00391| # Link to healthcheck module -00392| healthcheckModule = lib.mkOption { -00393| type = lib.types.nullOr lib.types.str; -00394| default = null; -00395| description = '' -00396| Name of the healthcheck module that provides health checks for this module. -00397| This links to stack.healthchecks.modules.. -00398| ''; -00399| }; -00400| }; -00401| } -00402| ); -00403| -00404| # ============================================================================ -00405| # Computed Values -00406| # ============================================================================ -00407| -00408| # Filter to only enabled modules -00409| enabledModules = lib.filterAttrs (_: mod: mod.enable) cfg.modules; -00410| -00411| # Get builtin modules -00412| builtinModules = lib.filterAttrs (_: mod: mod.source.type == "builtin") enabledModules; -00413| -00414| # Get external modules (local, flake-input, registry) -00415| externalModules = lib.filterAttrs (_: mod: mod.source.type != "builtin") enabledModules; -00416| -00417| # Compute serializable module data (for API/UI consumption) -00418| computeSerializableModule = name: mod: { -00419| id = name; -00420| enabled = mod.enable; -00421| meta = { -00422| name = mod.meta.name; -00423| description = mod.meta.description; -00424| icon = mod.meta.icon; -00425| category = mod.meta.category; -00426| author = mod.meta.author; -00427| version = mod.meta.version; -00428| homepage = mod.meta.homepage; -00429| }; -00430| source = { -00431| type = mod.source.type; -00432| flakeInput = mod.source.flakeInput; -00433| path = mod.source.path; -00434| registryId = mod.source.registryId; -00435| ref = mod.source.ref; -00436| }; -00437| features = { -00438| files = mod.features.files; -00439| scripts = mod.features.scripts; -00440| tasks = mod.features.tasks; -00441| healthchecks = mod.features.healthchecks; -00442| services = mod.features.services; -00443| secrets = mod.features.secrets; -00444| packages = mod.features.packages; -00445| appModule = mod.features.appModule; -00446| }; -00447| requires = mod.requires; -00448| conflicts = mod.conflicts; -00449| priority = mod.priority; -00450| tags = mod.tags; -00451| configSchema = mod.configSchema; -00452| panels = map (panel: { -00453| id = panel.id; -00454| title = panel.title; -00455| description = panel.description; -00456| type = panel.type; -00457| order = panel.order; -00458| fields = map (field: { -00459| name = field.name; -00460| type = field.type; -00461| value = field.value; -00462| options = field.options; -00463| }) panel.fields; -00464| }) mod.panels; -00465| apps = lib.mapAttrs (_: appData: { -00466| enabled = appData.enabled; -00467| config = appData.config; -00468| }) mod.apps; -00469| healthcheckModule = mod.healthcheckModule; -00470| }; -00471| -00472| # All modules as serializable attrset -00473| modulesComputed = lib.mapAttrs computeSerializableModule cfg.modules; -00474| -00475| # Enabled modules only -00476| modulesComputedEnabled = lib.mapAttrs computeSerializableModule enabledModules; -00477| -00478| # Flat list for API consumption -00479| modulesList = lib.mapAttrsToList computeSerializableModule cfg.modules; -00480| modulesListEnabled = lib.mapAttrsToList computeSerializableModule enabledModules; -00481| -00482| in -00483| { -00484| # ============================================================================ -00485| # Options -00486| # ============================================================================ -00487| -00488| options.stack.modules = lib.mkOption { -00489| type = lib.types.attrsOf moduleType; -00490| default = { }; -00491| description = '' -00492| Stack modules that provide features and UI panels. -00493| -00494| Modules are the unified way to extend stack functionality: -00495| - Add packages, scripts, and environment configuration -00496| - Generate files and manage secrets -00497| - Define health checks and background services -00498| - Provide UI panels for the web studio -00499| - Extend per-app configuration -00500| -00501| Each module can define: -00502| - `enable`: Whether the module is active -00503| - `meta`: Display metadata (name, description, icon, category) -00504| - `source`: Where the module comes from (builtin, local, flake-input, registry) -00505| - `features`: Which stack systems it uses -00506| - `panels`: UI panels to render in the web studio -00507| - `configSchema`: JSON Schema for configuration form generation -00508| - `healthcheckModule`: Link to health checks -00509| -00510| Modules can be: -00511| - Builtin: Shipped with stack -00512| - Local: Defined in your project -00513| - Remote: Installed via flake inputs or module registry -00514| ''; -00515| example = lib.literalExpression '' -00516| { -00517| postgres = { -00518| enable = true; -00519| meta = { -00520| name = "PostgreSQL"; -00521| description = "PostgreSQL database server"; -00522| icon = "database"; -00523| category = "database"; -00524| }; -00525| source.type = "builtin"; -00526| features = { -00527| services = true; -00528| healthchecks = true; -00529| packages = true; -00530| }; -00531| healthcheckModule = "postgres"; -00532| panels = [{ -00533| id = "postgres-status"; -00534| title = "PostgreSQL Status"; -00535| type = "PANEL_TYPE_STATUS"; -00536| fields = [{ -00537| name = "metrics"; -00538| type = "FIELD_TYPE_JSON"; -00539| value = "[{\"label\":\"Status\",\"value\":\"Running\",\"status\":\"ok\"}]"; -00540| }]; -00541| }]; -00542| }; -00543| -00544| my-custom-module = { -00545| enable = true; -00546| meta = { -00547| name = "My Custom Module"; -00548| description = "Does something useful"; -00549| category = "development"; -00550| }; -00551| source = { -00552| type = "flake-input"; -00553| flakeInput = "my-module"; -00554| }; -00555| }; -00556| } -00557| ''; -00558| }; -00559| -00560| # ============================================================================ -00561| # Computed Read-Only Options -00562| # ============================================================================ -00563| -00564| options.stack.modulesComputed = lib.mkOption { -00565| type = lib.types.attrsOf lib.types.unspecified; -00566| readOnly = true; -00567| default = modulesComputedEnabled; -00568| description = "Computed module configurations (only enabled modules, serializable)"; -00569| }; -00570| -00571| options.stack.modulesComputedAll = lib.mkOption { -00572| type = lib.types.attrsOf lib.types.unspecified; -00573| readOnly = true; -00574| default = modulesComputed; -00575| description = "Computed module configurations (all modules including disabled, serializable)"; -00576| }; -00577| -00578| options.stack.modulesList = lib.mkOption { -00579| type = lib.types.listOf lib.types.unspecified; -00580| readOnly = true; -00581| default = modulesListEnabled; -00582| description = "Flat list of enabled modules (for API consumption)"; -00583| }; -00584| -00585| options.stack.modulesListAll = lib.mkOption { -00586| type = lib.types.listOf lib.types.unspecified; -00587| readOnly = true; -00588| default = modulesList; -00589| description = "Flat list of all modules including disabled (for API consumption)"; -00590| }; -00591| -00592| options.stack.modulesBuiltin = lib.mkOption { -00593| type = lib.types.attrsOf lib.types.unspecified; -00594| readOnly = true; -00595| default = builtinModules; -00596| description = "Builtin modules shipped with stack"; -00597| }; -00598| -00599| options.stack.modulesExternal = lib.mkOption { -00600| type = lib.types.attrsOf lib.types.unspecified; -00601| readOnly = true; -00602| default = externalModules; -00603| description = "External modules (local, flake-input, or registry)"; -00604| }; -00605| -00606| # Fast metadata discovery - allows reading module metadata without full evaluation -00607| # Set by modules/default.nix from each module's meta.nix file -00608| options.stack._moduleMetas = lib.mkOption { -00609| type = lib.types.attrsOf lib.types.unspecified; -00610| default = { }; -00611| description = '' -00612| Fast module metadata for discovery without full module evaluation. -00613| Each key is a module ID, value is the contents of that module's meta.nix. -00614| This is set automatically by the module auto-discovery in modules/default.nix. -00615| ''; -00616| }; -00617| -00618| # ============================================================================ -00619| # Config: Validation -00620| # ============================================================================ -00621| -00622| # NOTE: Assertions are not available in devenv's module system. -00623| # Module dependency validation would need to be implemented differently, -00624| # perhaps using devenv's warning system or runtime checks. -00625| # For now, dependency checks are skipped. -00626| # -00627| # TODO: Implement module dependency validation using devenv-compatible mechanism -00628| # The validation logic should check: -00629| # - Required modules are enabled when a module that requires them is enabled -00630| # - Conflicting modules are not both enabled simultaneously -00631| } -00632| - -(End of file - total 632 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 27.5s) - -_Thinking:_ - - - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "in_progress", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "pending", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "in_progress", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "pending", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 26.5s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Add File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/default.nix\n+{\n+ lib,\n+ config,\n+ pkgs,\n+ ...\n+}:\n+{\n+ imports = [\n+ ./module.nix\n+ ./ui.nix\n+ ];\n+}\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -A nix/stack/modules/colmena/default.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.7s) - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Add File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/meta.nix\n+{\n+ id = \"colmena\";\n+ name = \"Colmena\";\n+ description = \"NixOS fleet deployment orchestration with configurable colmena flags\";\n+ category = \"deployment\";\n+ version = \"1.0.0\";\n+ icon = \"ship\";\n+ homepage = \"https://colmena.cli.rs/\";\n+ author = \"Stack\";\n+ tags = [\n+ \"colmena\"\n+ \"nixos\"\n+ \"deployment\"\n+ \"fleet\"\n+ \"orchestration\"\n+ ];\n+ requires = [ ];\n+ conflicts = [ ];\n+ features = {\n+ files = false;\n+ scripts = true;\n+ healthchecks = true;\n+ packages = true;\n+ services = false;\n+ secrets = false;\n+ tasks = false;\n+ appModule = false;\n+ };\n+ priority = 35;\n+}\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -A nix/stack/modules/colmena/meta.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 26.8s) - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Add File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/module.nix\n+{\n+ lib,\n+ config,\n+ pkgs,\n+ ...\n+}:\n+let\n+ meta = import ./meta.nix;\n+ sp = config.stack;\n+ cfg = sp.colmena;\n+\n+ mkFlag = enabled: flag: lib.optionals enabled [ flag ];\n+ mkValueFlag = flag: value: lib.optionals (value != null) [ flag (toString value) ];\n+ mkListFlag = flag: values: lib.optionals (values != [ ]) [ flag (lib.concatStringsSep \",\" values) ];\n+\n+ commonFlags =\n+ [ \"--config\" cfg.config ]\n+ ++ mkValueFlag \"--flake\" cfg.flake\n+ ++ mkListFlag \"--on\" cfg.on\n+ ++ mkListFlag \"--exclude\" cfg.exclude\n+ ++ mkFlag cfg.keepResult \"--keep-result\"\n+ ++ mkFlag cfg.verbose \"--verbose\"\n+ ++ mkFlag cfg.showTrace \"--show-trace\"\n+ ++ mkFlag cfg.impure \"--impure\"\n+ ++ mkValueFlag \"--eval-node-limit\" cfg.evalNodeLimit\n+ ++ mkValueFlag \"--parallel\" cfg.parallel\n+ ++ cfg.extraFlags;\n+\n+ applyFlags =\n+ commonFlags\n+ ++ mkFlag cfg.buildOnTarget \"--build-on-target\"\n+ ++ mkFlag cfg.uploadKeys \"--upload-keys\"\n+ ++ mkFlag cfg.noSubstitute \"--no-substitute\"\n+ ++ mkFlag cfg.substituteOnDestination \"--substitute-on-destination\"\n+ ++ mkFlag (!cfg.gzip) \"--no-gzip\"\n+ ++ mkFlag cfg.reboot \"--reboot\"\n+ ++ cfg.applyExtraFlags;\n+\n+ buildFlags =\n+ commonFlags\n+ ++ mkFlag cfg.buildOnTarget \"--build-on-target\"\n+ ++ mkFlag cfg.noSubstitute \"--no-substitute\"\n+ ++ mkFlag cfg.substituteOnDestination \"--substitute-on-destination\"\n+ ++ mkFlag (!cfg.gzip) \"--no-gzip\"\n+ ++ cfg.buildExtraFlags;\n+\n+ evalFlags = commonFlags ++ cfg.evalExtraFlags;\n+\n+ renderFlags = flags: lib.concatStringsSep \" \" (map lib.escapeShellArg flags);\n+\n+ mkColmenaScript =\n+ {\n+ subcommand,\n+ flags,\n+ description,\n+ }:\n+ {\n+ inherit description;\n+ args = [\n+ {\n+ name = \"...\";\n+ description = \"Additional arguments passed to colmena ${subcommand}\";\n+ }\n+ ];\n+ exec = ''\n+ set -euo pipefail\n+ exec ${lib.getExe cfg.package} ${subcommand} ${renderFlags flags} \"$@\"\n+ '';\n+ };\n+in\n+{\n+ options.stack.colmena = {\n+ enable = lib.mkEnableOption \"Colmena deployment tooling\";\n+\n+ package = lib.mkOption {\n+ type = lib.types.package;\n+ default = pkgs.colmena;\n+ description = \"Colmena package to use for generated commands.\";\n+ };\n+\n+ config = lib.mkOption {\n+ type = lib.types.str;\n+ default = \"colmena.nix\";\n+ description = \"Path to the Colmena hive config passed via --config.\";\n+ };\n+\n+ flake = lib.mkOption {\n+ type = lib.types.nullOr lib.types.str;\n+ default = null;\n+ description = \"Optional flake reference passed via --flake.\";\n+ };\n+\n+ on = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Node names, tags, or patterns to include with --on.\";\n+ };\n+\n+ exclude = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Node names, tags, or patterns to exclude with --exclude.\";\n+ };\n+\n+ keepResult = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Keep build results in the GC roots (--keep-result).\";\n+ };\n+\n+ verbose = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Enable verbose Colmena output (--verbose).\";\n+ };\n+\n+ showTrace = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Show detailed Nix traces on evaluation failures (--show-trace).\";\n+ };\n+\n+ impure = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Evaluate with impure mode enabled (--impure).\";\n+ };\n+\n+ evalNodeLimit = lib.mkOption {\n+ type = lib.types.nullOr lib.types.int;\n+ default = null;\n+ description = \"Maximum number of nodes evaluated concurrently (--eval-node-limit).\";\n+ };\n+\n+ parallel = lib.mkOption {\n+ type = lib.types.nullOr lib.types.int;\n+ default = null;\n+ description = \"Maximum number of deployment jobs run concurrently (--parallel).\";\n+ };\n+\n+ buildOnTarget = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Build closures on target nodes (--build-on-target).\";\n+ };\n+\n+ uploadKeys = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Upload deployment keys before activation (--upload-keys).\";\n+ };\n+\n+ noSubstitute = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Disable binary cache substitution (--no-substitute).\";\n+ };\n+\n+ substituteOnDestination = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Allow substitution on destination nodes (--substitute-on-destination).\";\n+ };\n+\n+ gzip = lib.mkOption {\n+ type = lib.types.bool;\n+ default = true;\n+ description = \"Enable gzip compression for build closure transfer (use --no-gzip when false).\";\n+ };\n+\n+ reboot = lib.mkOption {\n+ type = lib.types.bool;\n+ default = false;\n+ description = \"Allow Colmena to reboot machines if needed (--reboot).\";\n+ };\n+\n+ extraFlags = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Extra flags appended to all generated Colmena commands.\";\n+ };\n+\n+ applyExtraFlags = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Extra flags appended to the generated colmena-apply command.\";\n+ };\n+\n+ buildExtraFlags = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Extra flags appended to the generated colmena-build command.\";\n+ };\n+\n+ evalExtraFlags = lib.mkOption {\n+ type = lib.types.listOf lib.types.str;\n+ default = [ ];\n+ description = \"Extra flags appended to the generated colmena-eval command.\";\n+ };\n+\n+ computed = lib.mkOption {\n+ type = lib.types.attrsOf (lib.types.listOf lib.types.str);\n+ readOnly = true;\n+ description = \"Computed Colmena flag sets for each generated command.\";\n+ };\n+ };\n+\n+ config = lib.mkIf (sp.enable && cfg.enable) {\n+ stack.colmena.computed = {\n+ common = commonFlags;\n+ apply = applyFlags;\n+ build = buildFlags;\n+ eval = evalFlags;\n+ };\n+\n+ stack.devshell.packages = [\n+ cfg.package\n+ ];\n+\n+ stack.scripts = {\n+ colmena-apply = mkColmenaScript {\n+ subcommand = \"apply\";\n+ flags = applyFlags;\n+ description = \"Run colmena apply with stack defaults\";\n+ };\n+\n+ colmena-build = mkColmenaScript {\n+ subcommand = \"build\";\n+ flags = buildFlags;\n+ description = \"Run colmena build with stack defaults\";\n+ };\n+\n+ colmena-eval = mkColmenaScript {\n+ subcommand = \"eval\";\n+ flags = evalFlags;\n+ description = \"Run colmena eval with stack defaults\";\n+ };\n+ };\n+\n+ stack.healthchecks.modules.${meta.id} = {\n+ enable = true;\n+ displayName = meta.name;\n+ checks = {\n+ colmena-installed = {\n+ description = \"Colmena CLI is installed and accessible\";\n+ script = ''\n+ command -v colmena >/dev/null 2>&1 && colmena --help >/dev/null\n+ '';\n+ severity = \"critical\";\n+ timeout = 5;\n+ };\n+\n+ hive-config = {\n+ description = \"Configured Colmena hive file exists\";\n+ script = ''\n+ if [ -n \"${cfg.flake or \"\"}\" ]; then\n+ exit 0\n+ fi\n+\n+ ROOT=\"''${STACKPANEL_ROOT:-$(pwd)}\"\n+ test -f \"$ROOT/${cfg.config}\"\n+ '';\n+ severity = \"warning\";\n+ timeout = 5;\n+ };\n+ };\n+ };\n+\n+ stack.modules.${meta.id} = {\n+ enable = true;\n+ meta = {\n+ name = meta.name;\n+ description = meta.description;\n+ icon = meta.icon;\n+ category = meta.category;\n+ author = meta.author;\n+ version = meta.version;\n+ homepage = meta.homepage;\n+ };\n+ source.type = \"builtin\";\n+ features = meta.features;\n+ tags = meta.tags;\n+ priority = meta.priority;\n+ healthcheckModule = meta.id;\n+ };\n+ };\n+}\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -A nix/stack/modules/colmena/module.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 23.4s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Add File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/ui.nix\n+{\n+ lib,\n+ config,\n+ ...\n+}:\n+let\n+ meta = import ./meta.nix;\n+ sp = config.stack;\n+ cfg = sp.colmena;\n+\n+ toBool = value: if value then \"true\" else \"false\";\n+ toJson = value: builtins.toJSON value;\n+ toNumOrEmpty = value: if value == null then \"\" else toString value;\n+in\n+lib.mkIf sp.enable {\n+ stack.panels.\"${meta.id}-status\" = {\n+ module = meta.id;\n+ title = \"Colmena\";\n+ description = meta.description;\n+ icon = meta.icon;\n+ type = \"PANEL_TYPE_STATUS\";\n+ order = meta.priority;\n+ fields = [\n+ {\n+ name = \"metrics\";\n+ type = \"FIELD_TYPE_STRING\";\n+ value = toJson [\n+ {\n+ label = \"Enabled\";\n+ value = if cfg.enable then \"Yes\" else \"No\";\n+ status = if cfg.enable then \"ok\" else \"warning\";\n+ }\n+ {\n+ label = \"Hive Config\";\n+ value = cfg.config;\n+ status = \"ok\";\n+ }\n+ {\n+ label = \"Flake\";\n+ value = cfg.flake or \"(not set)\";\n+ status = \"ok\";\n+ }\n+ ];\n+ }\n+ ];\n+ };\n+\n+ stack.panels.\"${meta.id}-config\" = {\n+ module = meta.id;\n+ title = \"Colmena Settings\";\n+ description = \"Configure Colmena defaults used by stack scripts\";\n+ icon = meta.icon;\n+ type = \"PANEL_TYPE_FORM\";\n+ order = meta.priority + 1;\n+ fields = [\n+ {\n+ name = \"enable\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Enable Colmena\";\n+ description = \"Enable Colmena tooling, scripts, and health checks\";\n+ value = toBool cfg.enable;\n+ configPath = \"stack.colmena.enable\";\n+ }\n+ {\n+ name = \"config\";\n+ type = \"FIELD_TYPE_STRING\";\n+ label = \"Hive Config Path\";\n+ description = \"Path to the Colmena hive config file\";\n+ value = cfg.config;\n+ configPath = \"stack.colmena.config\";\n+ }\n+ {\n+ name = \"flake\";\n+ type = \"FIELD_TYPE_STRING\";\n+ label = \"Flake Reference\";\n+ description = \"Optional flake target to use instead of plain hive file\";\n+ value = cfg.flake or \"\";\n+ placeholder = \".#colmena\";\n+ configPath = \"stack.colmena.flake\";\n+ }\n+ {\n+ name = \"on\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Target Filter\";\n+ description = \"List of node names, tags, or patterns passed to --on\";\n+ value = toJson cfg.on;\n+ configPath = \"stack.colmena.on\";\n+ }\n+ {\n+ name = \"exclude\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Exclude Filter\";\n+ description = \"List of node names, tags, or patterns passed to --exclude\";\n+ value = toJson cfg.exclude;\n+ configPath = \"stack.colmena.exclude\";\n+ }\n+ {\n+ name = \"keepResult\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Keep Result\";\n+ description = \"Keep build results in GC roots\";\n+ value = toBool cfg.keepResult;\n+ configPath = \"stack.colmena.keepResult\";\n+ }\n+ {\n+ name = \"verbose\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Verbose\";\n+ description = \"Enable verbose Colmena output\";\n+ value = toBool cfg.verbose;\n+ configPath = \"stack.colmena.verbose\";\n+ }\n+ {\n+ name = \"showTrace\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Show Trace\";\n+ description = \"Show detailed Nix traces on errors\";\n+ value = toBool cfg.showTrace;\n+ configPath = \"stack.colmena.showTrace\";\n+ }\n+ {\n+ name = \"impure\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Impure Eval\";\n+ description = \"Allow impure Nix evaluation\";\n+ value = toBool cfg.impure;\n+ configPath = \"stack.colmena.impure\";\n+ }\n+ {\n+ name = \"evalNodeLimit\";\n+ type = \"FIELD_TYPE_NUMBER\";\n+ label = \"Eval Node Limit\";\n+ description = \"Maximum nodes evaluated concurrently\";\n+ value = toNumOrEmpty cfg.evalNodeLimit;\n+ configPath = \"stack.colmena.evalNodeLimit\";\n+ }\n+ {\n+ name = \"parallel\";\n+ type = \"FIELD_TYPE_NUMBER\";\n+ label = \"Parallel Jobs\";\n+ description = \"Maximum concurrent deployment jobs\";\n+ value = toNumOrEmpty cfg.parallel;\n+ configPath = \"stack.colmena.parallel\";\n+ }\n+ {\n+ name = \"buildOnTarget\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Build On Target\";\n+ description = \"Build closures on target nodes\";\n+ value = toBool cfg.buildOnTarget;\n+ configPath = \"stack.colmena.buildOnTarget\";\n+ }\n+ {\n+ name = \"uploadKeys\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Upload Keys\";\n+ description = \"Upload deployment keys before activation\";\n+ value = toBool cfg.uploadKeys;\n+ configPath = \"stack.colmena.uploadKeys\";\n+ }\n+ {\n+ name = \"noSubstitute\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"No Substitute\";\n+ description = \"Disable binary cache substitution\";\n+ value = toBool cfg.noSubstitute;\n+ configPath = \"stack.colmena.noSubstitute\";\n+ }\n+ {\n+ name = \"substituteOnDestination\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Substitute On Destination\";\n+ description = \"Allow substitution on destination nodes\";\n+ value = toBool cfg.substituteOnDestination;\n+ configPath = \"stack.colmena.substituteOnDestination\";\n+ }\n+ {\n+ name = \"gzip\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Gzip Transfers\";\n+ description = \"Compress closure transfer streams\";\n+ value = toBool cfg.gzip;\n+ configPath = \"stack.colmena.gzip\";\n+ }\n+ {\n+ name = \"reboot\";\n+ type = \"FIELD_TYPE_BOOLEAN\";\n+ label = \"Allow Reboot\";\n+ description = \"Allow reboots when required by activation\";\n+ value = toBool cfg.reboot;\n+ configPath = \"stack.colmena.reboot\";\n+ }\n+ {\n+ name = \"extraFlags\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Extra Flags\";\n+ description = \"Extra flags appended to every colmena command\";\n+ value = toJson cfg.extraFlags;\n+ configPath = \"stack.colmena.extraFlags\";\n+ }\n+ {\n+ name = \"applyExtraFlags\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Apply Extra Flags\";\n+ description = \"Extra flags for colmena-apply only\";\n+ value = toJson cfg.applyExtraFlags;\n+ configPath = \"stack.colmena.applyExtraFlags\";\n+ }\n+ {\n+ name = \"buildExtraFlags\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Build Extra Flags\";\n+ description = \"Extra flags for colmena-build only\";\n+ value = toJson cfg.buildExtraFlags;\n+ configPath = \"stack.colmena.buildExtraFlags\";\n+ }\n+ {\n+ name = \"evalExtraFlags\";\n+ type = \"FIELD_TYPE_JSON\";\n+ label = \"Eval Extra Flags\";\n+ description = \"Extra flags for colmena-eval only\";\n+ value = toJson cfg.evalExtraFlags;\n+ configPath = \"stack.colmena.evalExtraFlags\";\n+ }\n+ ];\n+ };\n+}\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -A nix/stack/modules/colmena/ui.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.9s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/module.nix\n@@\n computed = lib.mkOption {\n type = lib.types.attrsOf (lib.types.listOf lib.types.str);\n+ default = { };\n readOnly = true;\n description = \"Computed Colmena flag sets for each generated command.\";\n };\n@@\n hive-config = {\n description = \"Configured Colmena hive file exists\";\n script = ''\n- if [ -n \"${cfg.flake or \"\"}\" ]; then\n+ if [ -n \"${if cfg.flake != null then cfg.flake else \"\"}\" ]; then\n exit 0\n fi\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -M nix/stack/modules/colmena/module.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/ui.nix\n@@\n toBool = value: if value then \"true\" else \"false\";\n toJson = value: builtins.toJSON value;\n toNumOrEmpty = value: if value == null then \"\" else toString value;\n+ flakeDisplay = if cfg.flake != null then cfg.flake else \"(not set)\";\n+ flakeInputValue = if cfg.flake != null then cfg.flake else \"\";\n@@\n {\n label = \"Flake\";\n- value = cfg.flake or \"(not set)\";\n+ value = flakeDisplay;\n status = \"ok\";\n }\n@@\n label = \"Flake Reference\";\n description = \"Optional flake target to use instead of plain hive file\";\n- value = cfg.flake or \"\";\n+ value = flakeInputValue;\n placeholder = \".#colmena\";\n configPath = \"stack.colmena.flake\";\n }\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -M nix/stack/modules/colmena/ui.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.7s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/module.nix" -} -``` -**Output:** -``` - -00001| { -00002| lib, -00003| config, -00004| pkgs, -00005| ... -00006| }: -00007| let -00008| meta = import ./meta.nix; -00009| sp = config.stack; -00010| cfg = sp.colmena; -00011| -00012| mkFlag = enabled: flag: lib.optionals enabled [ flag ]; -00013| mkValueFlag = -00014| flag: value: -00015| lib.optionals (value != null) [ -00016| flag -00017| (toString value) -00018| ]; -00019| mkListFlag = -00020| flag: values: -00021| lib.optionals (values != [ ]) [ -00022| flag -00023| (lib.concatStringsSep "," values) -00024| ]; -00025| -00026| commonFlags = [ -00027| "--config" -00028| cfg.config -00029| ] -00030| ++ mkValueFlag "--flake" cfg.flake -00031| ++ mkListFlag "--on" cfg.on -00032| ++ mkListFlag "--exclude" cfg.exclude -00033| ++ mkFlag cfg.keepResult "--keep-result" -00034| ++ mkFlag cfg.verbose "--verbose" -00035| ++ mkFlag cfg.showTrace "--show-trace" -00036| ++ mkFlag cfg.impure "--impure" -00037| ++ mkValueFlag "--eval-node-limit" cfg.evalNodeLimit -00038| ++ mkValueFlag "--parallel" cfg.parallel -00039| ++ cfg.extraFlags; -00040| -00041| applyFlags = -00042| commonFlags -00043| ++ mkFlag cfg.buildOnTarget "--build-on-target" -00044| ++ mkFlag cfg.uploadKeys "--upload-keys" -00045| ++ mkFlag cfg.noSubstitute "--no-substitute" -00046| ++ mkFlag cfg.substituteOnDestination "--substitute-on-destination" -00047| ++ mkFlag (!cfg.gzip) "--no-gzip" -00048| ++ mkFlag cfg.reboot "--reboot" -00049| ++ cfg.applyExtraFlags; -00050| -00051| buildFlags = -00052| commonFlags -00053| ++ mkFlag cfg.buildOnTarget "--build-on-target" -00054| ++ mkFlag cfg.noSubstitute "--no-substitute" -00055| ++ mkFlag cfg.substituteOnDestination "--substitute-on-destination" -00056| ++ mkFlag (!cfg.gzip) "--no-gzip" -00057| ++ cfg.buildExtraFlags; -00058| -00059| evalFlags = commonFlags ++ cfg.evalExtraFlags; -00060| -00061| renderFlags = flags: lib.concatStringsSep " " (map lib.escapeShellArg flags); -00062| -00063| mkColmenaScript = -00064| { -00065| subcommand, -00066| flags, -00067| description, -00068| }: -00069| { -00070| inherit description; -00071| args = [ -00072| { -00073| name = "..."; -00074| description = "Additional arguments passed to colmena ${subcommand}"; -00075| } -00076| ]; -00077| exec = '' -00078| set -euo pipefail -00079| exec ${lib.getExe cfg.package} ${subcommand} ${renderFlags flags} "$@" -00080| ''; -00081| }; -00082| in -00083| { -00084| options.stack.colmena = { -00085| enable = lib.mkEnableOption "Colmena deployment tooling"; -00086| -00087| package = lib.mkOption { -00088| type = lib.types.package; -00089| default = pkgs.colmena; -00090| description = "Colmena package to use for generated commands."; -00091| }; -00092| -00093| config = lib.mkOption { -00094| type = lib.types.str; -00095| default = "colmena.nix"; -00096| description = "Path to the Colmena hive config passed via --config."; -00097| }; -00098| -00099| flake = lib.mkOption { -00100| type = lib.types.nullOr lib.types.str; -00101| default = null; -00102| description = "Optional flake reference passed via --flake."; -00103| }; -00104| -00105| on = lib.mkOption { -00106| type = lib.types.listOf lib.types.str; -00107| default = [ ]; -00108| description = "Node names, tags, or patterns to include with --on."; -00109| }; -00110| -00111| exclude = lib.mkOption { -00112| type = lib.types.listOf lib.types.str; -00113| default = [ ]; -00114| description = "Node names, tags, or patterns to exclude with --exclude."; -00115| }; -00116| -00117| keepResult = lib.mkOption { -00118| type = lib.types.bool; -00119| default = false; -00120| description = "Keep build results in the GC roots (--keep-result)."; -00121| }; -00122| -00123| verbose = lib.mkOption { -00124| type = lib.types.bool; -00125| default = false; -00126| description = "Enable verbose Colmena output (--verbose)."; -00127| }; -00128| -00129| showTrace = lib.mkOption { -00130| type = lib.types.bool; -00131| default = false; -00132| description = "Show detailed Nix traces on evaluation failures (--show-trace)."; -00133| }; -00134| -00135| impure = lib.mkOption { -00136| type = lib.types.bool; -00137| default = false; -00138| description = "Evaluate with impure mode enabled (--impure)."; -00139| }; -00140| -00141| evalNodeLimit = lib.mkOption { -00142| type = lib.types.nullOr lib.types.int; -00143| default = null; -00144| description = "Maximum number of nodes evaluated concurrently (--eval-node-limit)."; -00145| }; -00146| -00147| parallel = lib.mkOption { -00148| type = lib.types.nullOr lib.types.int; -00149| default = null; -00150| description = "Maximum number of deployment jobs run concurrently (--parallel)."; -00151| }; -00152| -00153| buildOnTarget = lib.mkOption { -00154| type = lib.types.bool; -00155| default = false; -00156| description = "Build closures on target nodes (--build-on-target)."; -00157| }; -00158| -00159| uploadKeys = lib.mkOption { -00160| type = lib.types.bool; -00161| default = false; -00162| description = "Upload deployment keys before activation (--upload-keys)."; -00163| }; -00164| -00165| noSubstitute = lib.mkOption { -00166| type = lib.types.bool; -00167| default = false; -00168| description = "Disable binary cache substitution (--no-substitute)."; -00169| }; -00170| -00171| substituteOnDestination = lib.mkOption { -00172| type = lib.types.bool; -00173| default = false; -00174| description = "Allow substitution on destination nodes (--substitute-on-destination)."; -00175| }; -00176| -00177| gzip = lib.mkOption { -00178| type = lib.types.bool; -00179| default = true; -00180| description = "Enable gzip compression for build closure transfer (use --no-gzip when false)."; -00181| }; -00182| -00183| reboot = lib.mkOption { -00184| type = lib.types.bool; -00185| default = false; -00186| description = "Allow Colmena to reboot machines if needed (--reboot)."; -00187| }; -00188| -00189| extraFlags = lib.mkOption { -00190| type = lib.types.listOf lib.types.str; -00191| default = [ ]; -00192| description = "Extra flags appended to all generated Colmena commands."; -00193| }; -00194| -00195| applyExtraFlags = lib.mkOption { -00196| type = lib.types.listOf lib.types.str; -00197| default = [ ]; -00198| description = "Extra flags appended to the generated colmena-apply command."; -00199| }; -00200| -00201| buildExtraFlags = lib.mkOption { -00202| type = lib.types.listOf lib.types.str; -00203| default = [ ]; -00204| description = "Extra flags appended to the generated colmena-build command."; -00205| }; -00206| -00207| evalExtraFlags = lib.mkOption { -00208| type = lib.types.listOf lib.types.str; -00209| default = [ ]; -00210| description = "Extra flags appended to the generated colmena-eval command."; -00211| }; -00212| -00213| computed = lib.mkOption { -00214| type = lib.types.attrsOf (lib.types.listOf lib.types.str); -00215| default = { }; -00216| readOnly = true; -00217| description = "Computed Colmena flag sets for each generated command."; -00218| }; -00219| }; -00220| -00221| config = lib.mkIf (sp.enable && cfg.enable) { -00222| stack.colmena.computed = { -00223| common = commonFlags; -00224| apply = applyFlags; -00225| build = buildFlags; -00226| eval = evalFlags; -00227| }; -00228| -00229| stack.devshell.packages = [ -00230| cfg.package -00231| ]; -00232| -00233| stack.scripts = { -00234| colmena-apply = mkColmenaScript { -00235| subcommand = "apply"; -00236| flags = applyFlags; -00237| description = "Run colmena apply with stack defaults"; -00238| }; -00239| -00240| colmena-build = mkColmenaScript { -00241| subcommand = "build"; -00242| flags = buildFlags; -00243| description = "Run colmena build with stack defaults"; -00244| }; -00245| -00246| colmena-eval = mkColmenaScript { -00247| subcommand = "eval"; -00248| flags = evalFlags; -00249| description = "Run colmena eval with stack defaults"; -00250| }; -00251| }; -00252| -00253| stack.healthchecks.modules.${meta.id} = { -00254| enable = true; -00255| displayName = meta.name; -00256| checks = { -00257| colmena-installed = { -00258| description = "Colmena CLI is installed and accessible"; -00259| script = '' -00260| command -v colmena >/dev/null 2>&1 && colmena --help >/dev/null -00261| ''; -00262| severity = "critical"; -00263| timeout = 5; -00264| }; -00265| -00266| hive-config = { -00267| description = "Configured Colmena hive file exists"; -00268| script = '' -00269| if [ -n "${if cfg.flake != null then cfg.flake else ""}" ]; then -00270| exit 0 -00271| fi -00272| -00273| ROOT="''${STACKPANEL_ROOT:-$(pwd)}" -00274| test -f "$ROOT/${cfg.config}" -00275| ''; -00276| severity = "warning"; -00277| timeout = 5; -00278| }; -00279| }; -00280| }; -00281| -00282| stack.modules.${meta.id} = { -00283| enable = true; -00284| meta = { -00285| name = meta.name; -00286| description = meta.description; -00287| icon = meta.icon; -00288| category = meta.category; -00289| author = meta.author; -00290| version = meta.version; -00291| homepage = meta.homepage; -00292| }; -00293| source.type = "builtin"; -00294| features = meta.features; -00295| tags = meta.tags; -00296| priority = meta.priority; -00297| healthcheckModule = meta.id; -00298| }; -00299| }; -00300| } -00301| - -(End of file - total 301 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/ui.nix" -} -``` -**Output:** -``` - -00001| { -00002| lib, -00003| config, -00004| ... -00005| }: -00006| let -00007| meta = import ./meta.nix; -00008| sp = config.stack; -00009| cfg = sp.colmena; -00010| -00011| toBool = value: if value then "true" else "false"; -00012| toJson = value: builtins.toJSON value; -00013| toNumOrEmpty = value: if value == null then "" else toString value; -00014| flakeDisplay = if cfg.flake != null then cfg.flake else "(not set)"; -00015| flakeInputValue = if cfg.flake != null then cfg.flake else ""; -00016| in -00017| lib.mkIf sp.enable { -00018| stack.panels."${meta.id}-status" = { -00019| module = meta.id; -00020| title = "Colmena"; -00021| description = meta.description; -00022| icon = meta.icon; -00023| type = "PANEL_TYPE_STATUS"; -00024| order = meta.priority; -00025| fields = [ -00026| { -00027| name = "metrics"; -00028| type = "FIELD_TYPE_STRING"; -00029| value = toJson [ -00030| { -00031| label = "Enabled"; -00032| value = if cfg.enable then "Yes" else "No"; -00033| status = if cfg.enable then "ok" else "warning"; -00034| } -00035| { -00036| label = "Hive Config"; -00037| value = cfg.config; -00038| status = "ok"; -00039| } -00040| { -00041| label = "Flake"; -00042| value = flakeDisplay; -00043| status = "ok"; -00044| } -00045| ]; -00046| } -00047| ]; -00048| }; -00049| -00050| stack.panels."${meta.id}-config" = { -00051| module = meta.id; -00052| title = "Colmena Settings"; -00053| description = "Configure Colmena defaults used by stack scripts"; -00054| icon = meta.icon; -00055| type = "PANEL_TYPE_FORM"; -00056| order = meta.priority + 1; -00057| fields = [ -00058| { -00059| name = "enable"; -00060| type = "FIELD_TYPE_BOOLEAN"; -00061| label = "Enable Colmena"; -00062| description = "Enable Colmena tooling, scripts, and health checks"; -00063| value = toBool cfg.enable; -00064| configPath = "stack.colmena.enable"; -00065| } -00066| { -00067| name = "config"; -00068| type = "FIELD_TYPE_STRING"; -00069| label = "Hive Config Path"; -00070| description = "Path to the Colmena hive config file"; -00071| value = cfg.config; -00072| configPath = "stack.colmena.config"; -00073| } -00074| { -00075| name = "flake"; -00076| type = "FIELD_TYPE_STRING"; -00077| label = "Flake Reference"; -00078| description = "Optional flake target to use instead of plain hive file"; -00079| value = flakeInputValue; -00080| placeholder = ".#colmena"; -00081| configPath = "stack.colmena.flake"; -00082| } -00083| { -00084| name = "on"; -00085| type = "FIELD_TYPE_JSON"; -00086| label = "Target Filter"; -00087| description = "List of node names, tags, or patterns passed to --on"; -00088| value = toJson cfg.on; -00089| configPath = "stack.colmena.on"; -00090| } -00091| { -00092| name = "exclude"; -00093| type = "FIELD_TYPE_JSON"; -00094| label = "Exclude Filter"; -00095| description = "List of node names, tags, or patterns passed to --exclude"; -00096| value = toJson cfg.exclude; -00097| configPath = "stack.colmena.exclude"; -00098| } -00099| { -00100| name = "keepResult"; -00101| type = "FIELD_TYPE_BOOLEAN"; -00102| label = "Keep Result"; -00103| description = "Keep build results in GC roots"; -00104| value = toBool cfg.keepResult; -00105| configPath = "stack.colmena.keepResult"; -00106| } -00107| { -00108| name = "verbose"; -00109| type = "FIELD_TYPE_BOOLEAN"; -00110| label = "Verbose"; -00111| description = "Enable verbose Colmena output"; -00112| value = toBool cfg.verbose; -00113| configPath = "stack.colmena.verbose"; -00114| } -00115| { -00116| name = "showTrace"; -00117| type = "FIELD_TYPE_BOOLEAN"; -00118| label = "Show Trace"; -00119| description = "Show detailed Nix traces on errors"; -00120| value = toBool cfg.showTrace; -00121| configPath = "stack.colmena.showTrace"; -00122| } -00123| { -00124| name = "impure"; -00125| type = "FIELD_TYPE_BOOLEAN"; -00126| label = "Impure Eval"; -00127| description = "Allow impure Nix evaluation"; -00128| value = toBool cfg.impure; -00129| configPath = "stack.colmena.impure"; -00130| } -00131| { -00132| name = "evalNodeLimit"; -00133| type = "FIELD_TYPE_NUMBER"; -00134| label = "Eval Node Limit"; -00135| description = "Maximum nodes evaluated concurrently"; -00136| value = toNumOrEmpty cfg.evalNodeLimit; -00137| configPath = "stack.colmena.evalNodeLimit"; -00138| } -00139| { -00140| name = "parallel"; -00141| type = "FIELD_TYPE_NUMBER"; -00142| label = "Parallel Jobs"; -00143| description = "Maximum concurrent deployment jobs"; -00144| value = toNumOrEmpty cfg.parallel; -00145| configPath = "stack.colmena.parallel"; -00146| } -00147| { -00148| name = "buildOnTarget"; -00149| type = "FIELD_TYPE_BOOLEAN"; -00150| label = "Build On Target"; -00151| description = "Build closures on target nodes"; -00152| value = toBool cfg.buildOnTarget; -00153| configPath = "stack.colmena.buildOnTarget"; -00154| } -00155| { -00156| name = "uploadKeys"; -00157| type = "FIELD_TYPE_BOOLEAN"; -00158| label = "Upload Keys"; -00159| description = "Upload deployment keys before activation"; -00160| value = toBool cfg.uploadKeys; -00161| configPath = "stack.colmena.uploadKeys"; -00162| } -00163| { -00164| name = "noSubstitute"; -00165| type = "FIELD_TYPE_BOOLEAN"; -00166| label = "No Substitute"; -00167| description = "Disable binary cache substitution"; -00168| value = toBool cfg.noSubstitute; -00169| configPath = "stack.colmena.noSubstitute"; -00170| } -00171| { -00172| name = "substituteOnDestination"; -00173| type = "FIELD_TYPE_BOOLEAN"; -00174| label = "Substitute On Destination"; -00175| description = "Allow substitution on destination nodes"; -00176| value = toBool cfg.substituteOnDestination; -00177| configPath = "stack.colmena.substituteOnDestination"; -00178| } -00179| { -00180| name = "gzip"; -00181| type = "FIELD_TYPE_BOOLEAN"; -00182| label = "Gzip Transfers"; -00183| description = "Compress closure transfer streams"; -00184| value = toBool cfg.gzip; -00185| configPath = "stack.colmena.gzip"; -00186| } -00187| { -00188| name = "reboot"; -00189| type = "FIELD_TYPE_BOOLEAN"; -00190| label = "Allow Reboot"; -00191| description = "Allow reboots when required by activation"; -00192| value = toBool cfg.reboot; -00193| configPath = "stack.colmena.reboot"; -00194| } -00195| { -00196| name = "extraFlags"; -00197| type = "FIELD_TYPE_JSON"; -00198| label = "Extra Flags"; -00199| description = "Extra flags appended to every colmena command"; -00200| value = toJson cfg.extraFlags; -00201| configPath = "stack.colmena.extraFlags"; -00202| } -00203| { -00204| name = "applyExtraFlags"; -00205| type = "FIELD_TYPE_JSON"; -00206| label = "Apply Extra Flags"; -00207| description = "Extra flags for colmena-apply only"; -00208| value = toJson cfg.applyExtraFlags; -00209| configPath = "stack.colmena.applyExtraFlags"; -00210| } -00211| { -00212| name = "buildExtraFlags"; -00213| type = "FIELD_TYPE_JSON"; -00214| label = "Build Extra Flags"; -00215| description = "Extra flags for colmena-build only"; -00216| value = toJson cfg.buildExtraFlags; -00217| configPath = "stack.colmena.buildExtraFlags"; -00218| } -00219| { -00220| name = "evalExtraFlags"; -00221| type = "FIELD_TYPE_JSON"; -00222| label = "Eval Extra Flags"; -00223| description = "Extra flags for colmena-eval only"; -00224| value = toJson cfg.evalExtraFlags; -00225| configPath = "stack.colmena.evalExtraFlags"; -00226| } -00227| ]; -00228| }; -00229| } -00230| - -(End of file - total 230 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/meta.nix" -} -``` -**Output:** -``` - -00001| { -00002| id = "colmena"; -00003| name = "Colmena"; -00004| description = "NixOS fleet deployment orchestration with configurable colmena flags"; -00005| category = "deployment"; -00006| version = "1.0.0"; -00007| icon = "ship"; -00008| homepage = "https://colmena.cli.rs/"; -00009| author = "Stack"; -00010| tags = [ -00011| "colmena" -00012| "nixos" -00013| "deployment" -00014| "fleet" -00015| "orchestration" -00016| ]; -00017| requires = [ ]; -00018| conflicts = [ ]; -00019| features = { -00020| files = false; -00021| scripts = true; -00022| healthchecks = true; -00023| packages = true; -00024| services = false; -00025| secrets = false; -00026| tasks = false; -00027| appModule = false; -00028| }; -00029| priority = 35; -00030| } -00031| - -(End of file - total 31 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/colmena/default.nix" -} -``` -**Output:** -``` - -00001| { -00002| lib, -00003| config, -00004| pkgs, -00005| ... -00006| }: -00007| { -00008| imports = [ -00009| ./module.nix -00010| ./ui.nix -00011| ]; -00012| } -00013| - -(End of file - total 13 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.9s) - -_Thinking:_ - - - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "completed", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "completed", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "in_progress", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "completed", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "completed", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "in_progress", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 44.5s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true && git status --short", - "description": "Enter devshell and show changed files" -} -``` -**Output:** -``` -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -copying path '/nix/store/ccxdi58zdh3qy7qsk68419gzldk8mqi3-css_v1.0.1' from 'https://darkmatter.cachix.org'... -copying path '/nix/store/bhyr8aqc5v67p3hmrjdk0g6l310xiizf-dupword_v0.1.3' from 'https://darkmatter.cachix.org'... -building '/nix/store/jf1rnkbcdgv0j572i8g86lwanw0s8zfl-vendor-env.drv'... -building '/nix/store/hq9cwvqg8q1cbjzl53g7p58k6cswbaph-go-cache-env.drv'... -building '/nix/store/aha1pkg9m6a779wa54b3rzinsb7ypxa8-stack-0.1.0.drv'... -building '/nix/store/wi0gr5y2g2gl8rxravxz5nb0xichs9aw-stack-profile.drv'... -building '/nix/store/mf1hlxznkl1319f8gv0ln3rjzy7ficbr-stack-generate-bin.drv'... -building '/nix/store/hdl0vkcmpysk6wxx37rfas8wjnvljw3a-stack-gc-update.drv'... -building '/nix/store/cw6mb0d87xxlqigin44awm9k6qmkrpkw-stack-shellhook.drv'... -building '/nix/store/nj8whgqqi3xcpxa465i6hhna3swpa2sc-stack-stack-env.drv'... -Config validated for stack -✓ Stack initialized -warning: unknown experimental feature 'build-time-fetch-tree' -warning: unknown experimental feature 'parallel-eval' -warning: unknown experimental feature 'external-builders' -warning: unknown setting 'eval-cores' -warning: unknown setting 'external-builders' -warning: unknown setting 'lazy-trees' -Checking if CA is reachable... OK -Checking if root cert is installed... OK -Checking if fingerprint matches... OK -Checking if device certificate exists... OK -Checking if device certificate is valid... OK - -All checks passed! -📜 stack scripts loaded - write .gitignore (block, updated) - write process-compose.yaml -> /nix/store/hf4j1580xwlcsiabb7ncqmyilc0bg2lv-process-compose.yaml -files: 2 written, 63 unchanged -Stack development environment - -📦 Stack Ports (project: stack) - Stable port: 6400 - - Apps: - docs: 5738 -> https://docs.stack.lan - stack-go: 5797 - web: 5775 -> https://stack.stack.lan - - Services: - Minio: 6498 - Minio Console: 6436 - PostgreSQL: 6404 - Redis: 6494 - - Tip: Set STACKPANEL_QUIET=1 to hide this message - - → .stack/bin: 219 binaries -Caddy config directories created at /Users/cm/.config/caddy -Caddy config directories created at /Users/cm/.config/caddy -✅ Stack Nix module system initialized -stack core initialized - | | | - __| __| _' | __| | / __ \ _' | __ \ _ \ | - \__ \ | ( | ( < | | ( | | | __/ | - ____/ \__| \__,_| \___| _|\_\ .__/ \__,_| _| _| \___| _| - _| - -╭────────────────────────────────────────────────────────────────────────╮ -│ │ -│ stackpanel Shell │ -│ Your reproducible development environment is ready │ -│ │ -│ ───��──────────────────────────────────────────────────────────────── │ -│ Status │ -│ Agent ● localhost:9876 │ -│ Services ○ minio ○ minio_console ○ postgres ○ redis │ -│ Shell ⚠ stale (config changed, reload shell) │ -│ Studio http://localhost:3000/studio?project=stackpanel │ -│ │ -│ Environment │ -│ Node 22.22.0 • Bun 1.3.3 • Go 1.25.6 • Python 3.12.12 │ -│ PostgreSQL 17.7 • Redis 8.2.3 • Docker 28.5.2 │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Quick Start │ -│ dev  Start all development services │ -│ dev stop  Stop all services │ -│ sp status  Open interactive dashboard │ -│ sp commands  List all available commands │ -│ │ -│ Shortcuts │ -│ sp  = stackpanel │ -│ spx = run devshell commands │ -│ x  = same as spx │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ ⚠ Action Required │ -│ ! Shell is stale (config changed) → exit && direnv allow │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Resources │ -│ Docs https://stackpanel.dev/docs │ -│ │ -╰────────────────────────────────────────────────────────────────────────╯ - -═══════════════════════════════════════════════════════════════ -Shell hook completed at 2026-02-28 22:42:41 -Log saved to: /Users/cm/git/darkmatter/stack/.stack/state/shell.log -═══════════════════════════════════════════════════════════════ - M process-compose.yaml -?? nix/stack/modules/colmena/ - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.8s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git diff -- process-compose.yaml", - "description": "Show diff for generated process-compose file" -} -``` -**Output:** -``` -diff --git a/process-compose.yaml b/process-compose.yaml -index dc91e35..8e897c9 120000 ---- a/process-compose.yaml -+++ b/process-compose.yaml -@@ -1 +1 @@ --/nix/store/prqh3bk4vf2gshgglq7ypy7pncdv0ifr-process-compose.yaml -\ No newline at end of file -+/nix/store/hf4j1580xwlcsiabb7ncqmyilc0bg2lv-process-compose.yaml -\ No newline at end of file - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.4s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/process-compose.yaml\n@@\n-/nix/store/hf4j1580xwlcsiabb7ncqmyilc0bg2lv-process-compose.yaml\n+/nix/store/prqh3bk4vf2gshgglq7ypy7pncdv0ifr-process-compose.yaml\n*** End Patch" -} -``` -**Error:** -``` -Error: apply_patch verification failed: Error: Failed to find expected lines in /Users/cm/git/darkmatter/stack/process-compose.yaml: -/nix/store/hf4j1580xwlcsiabb7ncqmyilc0bg2lv-process-compose.yaml -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.7s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/process-compose.yaml" -} -``` -**Output:** -``` - -00001| environment: -00002| - AWS_CONFIG_FILE=/nix/store/j9az9mk6sq9gbnicca16wdrdgr4r47cl-aws-config -00003| - AWS_REGION=us-west-2 -00004| - AWS_SHARED_CREDENTIALS_FILE=/dev/null -00005| - CHAMBER_KMS_KEY_ALIAS=alias/stack-secrets -00006| - DATABASE_URL=postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres -00007| - GOROOT=/nix/store/rwzizip34lp556kmagcfw0fszch22x0k-go-1.25.6/share/go/ -00008| - GOTOOLCHAIN=local -00009| - MINIO_ACCESS_KEY=minioadmin -00010| - MINIO_CONSOLE_ADDRESS=:6436 -00011| - MINIO_ENDPOINT=http://localhost:6498 -00012| - MINIO_ROOT_PASSWORD=minioadmin -00013| - MINIO_ROOT_USER=minioadmin -00014| - MINIO_SECRET_KEY=minioadmin -00015| - PC_PORT_NUM=6490 -00016| - PGDATA=$HOME/.local/share/devservices/postgres/data -00017| - PGDATABASE=postgres -00018| - PGHOST=$HOME/.local/share/devservices/postgres -00019| - PGPORT=6404 -00020| - PGUSER=postgres -00021| - PORT_DOCS=6400 -00022| - PORT_STACKPANEL_GO=6401 -00023| - PORT_WEB=6402 -00024| - POSTGRES_URL=postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres -00025| - REDIS_HOST=localhost -00026| - REDIS_PORT=6494 -00027| - REDIS_SOCKET=$HOME/.local/share/devservices/redis/redis.sock -00028| - REDIS_URL=redis://localhost:6494 -00029| - S3_ENDPOINT=http://localhost:6498 -00030| - "STACKPANEL_ALIAS_FUNC=# Internal helper shared by shortcuts\n_stackpanel_commands_runner()\ -00031| \ {\n if [ $# -eq 0 ]; then\n stack commands list\nelse\n stack commands\ -00032| \ \"$@\"\nfi\n\n}\n\n# Stack command shortcut: x\nx() {\n _stackpanel_commands_runner\ -00033| \ \"$@\"\n}\n\n# Short alias for the stack CLI\nalias sp=\"stack\"\n\n\ -00034| # Explicit commands runner shortcut\nspx() {\n _stackpanel_commands_runner \"$@\"\ -00035| \n}\n" -00036| - STACKPANEL_CLEAN_IMPURE=0 -00037| - STACKPANEL_CLEAN_MODE=0 -00038| - STACKPANEL_CONFIG_JSON=/nix/store/bg37mkfd1g6awq7cyx4s07qbqzjkwpgf-stack-config.json -00039| - STACKPANEL_INFRA_INPUTS=.stack/state/infra-inputs.json -00040| - STACKPANEL_NIX_CONFIG=$STACKPANEL_ROOT/.stack/config.nix -00041| - STACKPANEL_PORTS={"base-port":6400,"enable":true,"project-name":"stack","service":{"MINIO":{"displayName":"Minio","key":"MINIO","name":"Minio","port":6498},"MINIO_CONSOLE":{"displayName":"Minio -00042| Console","key":"MINIO_CONSOLE","name":"Minio Console","port":6436},"POSTGRES":{"displayName":"PostgreSQL","key":"POSTGRES","name":"PostgreSQL","port":6404},"REDIS":{"displayName":"Redis","key":"REDIS","name":"Redis","port":6494}},"services":{"MINIO":{"name":"Minio"},"MINIO_CONSOLE":{"name":"Minio -00043| Console"},"POSTGRES":{"name":"PostgreSQL"},"REDIS":{"name":"Redis"}}} -00044| - STACKPANEL_REMOTE_BUILDER_HOST=100.102.113.26 -00045| - STACKPANEL_ROOT_DIR_NAME=.stack -00046| - STACKPANEL_ROOT_MARKER=.stack-root -00047| - STACKPANEL_SERVICES_CONFIG=[{"key":"MINIO","name":"Minio","port":6498},{"key":"MINIO_CONSOLE","name":"Minio -00048| Console","port":6436},{"key":"POSTGRES","name":"PostgreSQL","port":6404},{"key":"REDIS","name":"Redis","port":6494}] -00049| - STACKPANEL_STABLE_PORT=6400 -00050| - URL_DOCS=https://docs.stack.lan -00051| - URL_WEB=https://stack.stack.lan -00052| - __STACKPANEL_CLEAN_ENV= -00053| processes: -00054| docs: -00055| command: if [[ -f packages/scripts/entrypoints/docs.sh ]]; then source packages/scripts/entrypoints/docs.sh -00056| --dev && exec /nix/store/1sf27aybrlvs4dja4rp4c8nsp47hsm05-turbo-2.7.3/bin/turbo -00057| run -F $(/nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin/jq -r -00058| .name apps/docs/package.json 2>/dev/null || echo 'docs') dev; else exec /nix/store/1sf27aybrlvs4dja4rp4c8nsp47hsm05-turbo-2.7.3/bin/turbo -00059| run -F $(/nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin/jq -r -00060| .name apps/docs/package.json 2>/dev/null || echo 'docs') dev; fi -00061| working_dir: apps/docs -00062| format-watch: -00063| availability: -00064| backoff_seconds: 2 -00065| restart: always -00066| command: /nix/store/jj13g1knnfj2hvj06m7i6q992gql5ks4-watchexec-2.3.2/bin/watchexec -00067| --exts ts,tsx,js,jsx,json,md,css,scss,html,nix,go,rs,py -- /nix/store/1sf27aybrlvs4dja4rp4c8nsp47hsm05-turbo-2.7.3/bin/turbo -00068| run format --continue -00069| namespace: infra -00070| working_dir: null -00071| minio: -00072| availability: -00073| backoff_seconds: 5 -00074| restart: on_failure -00075| command: /nix/store/ji7l5s8y6rjpnb8isjjbarn8c197d6qk-minio-start/bin/minio-start -00076| namespace: services -00077| readiness_probe: -00078| exec: -00079| command: /nix/store/0bz5d30q8f28yz8yhf65aya4jbcxn33n-curl-8.18.0-bin/bin/curl -00080| -sf http://localhost:6498/minio/health/live -00081| initial_delay_seconds: 2 -00082| period_seconds: 5 -00083| postgres: -00084| availability: -00085| backoff_seconds: 5 -00086| restart: on_failure -00087| command: /nix/store/dsqakvx7r6pq8q8hzpwgk43ypsy82qs3-postgres-start/bin/postgres-start -00088| namespace: services -00089| readiness_probe: -00090| exec: -00091| command: /nix/store/2ypmadhl399j3g9wjvfxbbswdsxi3svr-postgresql-17.7/bin/pg_isready -00092| -h $HOME/.local/share/devservices/postgres -p 6404 -00093| initial_delay_seconds: 2 -00094| period_seconds: 5 -00095| redis: -00096| availability: -00097| backoff_seconds: 5 -00098| restart: on_failure -00099| command: /nix/store/lxsm39z29v67aasj1vzw7lk1rkbavgd6-redis-start/bin/redis-start -00100| namespace: services -00101| readiness_probe: -00102| exec: -00103| command: /nix/store/wk93aw2y3d9axv2hrii3yixzvv2sf8ha-redis-8.2.3/bin/redis-cli -00104| -p 6494 ping -00105| initial_delay_seconds: 1 -00106| period_seconds: 3 -00107| stack-go: -00108| command: if [[ -f packages/scripts/entrypoints/stack-go.sh ]]; then source -00109| packages/scripts/entrypoints/stack-go.sh --dev && exec /nix/store/1sf27aybrlvs4dja4rp4c8nsp47hsm05-turbo-2.7.3/bin/turbo -00110| run -F $(/nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin/jq -r -00111| .name apps/stack-go/package.json 2>/dev/null || echo 'stack-go') dev; -00112| else exec /nix/store/1sf27aybrlvs4dja4rp4c8nsp47hsm05-turbo-2.7.3/bin/turbo -00113| run -F $(/nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin/jq -r -00114| .name apps/stack-go/package.json 2>/dev/null || echo 'stack-go') dev; -00115| fi -00116| working_dir: apps/stack-go -00117| web: -00118| command: bun run -F web dev -00119| working_dir: apps/web -00120| shell: -00121| shell_argument: -c -00122| shell_command: /nix/store/3cq07cjs5rbf2grkmp2xip0ns7qmm0nw-bash-interactive-5.3p3/bin/bash -00123| version: '0.5' -00124| - -(End of file - total 124 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.8s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git status --short", - "description": "Check working tree after edits" -} -``` -**Output:** -``` - M process-compose.yaml -?? nix/stack/modules/colmena/ - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.3s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ls && rm \"process-compose.yaml\" && ln -s \"/nix/store/prqh3bk4vf2gshgglq7ypy7pncdv0ifr-process-compose.yaml\" \"process-compose.yaml\" && git status --short", - "description": "Restore process-compose symlink and recheck status" -} -``` -**Output:** -``` -AGENTS.md -CLAUDE.md -Justfile -README.md -README.tmpl.md -WARP.md -alchemy.run.ts -apps -bun.lock -bunfig.toml -devshell -docs -flake.lock -flake.nix -infra -nix -node_modules -om.yaml -oxlint.json -package.json -packages -process-compose.yaml -scripts -sst.config.example.ts -tests -turbo.json -?? nix/stack/modules/colmena/ - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 10.8s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c nix eval --impure --expr '(import ./nix/stack/modules/colmena/meta.nix).id'", - "description": "Evaluate new colmena module metadata" -} -``` -**Output:** -``` -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -Config validated for stack -✓ Stack initialized -warning: unknown experimental feature 'build-time-fetch-tree' -warning: unknown experimental feature 'parallel-eval' -warning: unknown experimental feature 'external-builders' -warning: unknown setting 'eval-cores' -warning: unknown setting 'external-builders' -warning: unknown setting 'lazy-trees' -Checking if CA is reachable... OK -Checking if root cert is installed... OK -Checking if fingerprint matches... OK -Checking if device certificate exists... OK -Checking if device certificate is valid... OK - -All checks passed! -📜 stack scripts loaded -Stack development environment - -📦 Stack Ports (project: stack) - Stable port: 6400 - - Apps: - docs: 5738 -> https://docs.stack.lan - stack-go: 5797 - web: 5775 -> https://stack.stack.lan - - Services: - Minio: 6498 - Minio Console: 6436 - PostgreSQL: 6404 - Redis: 6494 - - Tip: Set STACKPANEL_QUIET=1 to hide this message - - → .stack/bin: 219 binaries -Caddy config directories created at /Users/cm/.config/caddy -Caddy config directories created at /Users/cm/.config/caddy -✅ Stack Nix module system initialized -stack core initialized - | | | - __| __| _' | __| | / __ \ _' | __ \ _ \ | - \__ \ | ( | ( < | | ( | | | __/ | - ____/ \__| \__,_| \___| _|\_\ .__/ \__,_| _| _| \___| _| - _| - -╭────────────────────────────────────────────────────────────────────────╮ -│ │ -│ stackpanel Shell │ -│ Your reproducible development environment is ready │ -│ │ -│ ───��──────────────────────────────────────────────────────────────── │ -│ Status │ -│ Agent ● localhost:9876 │ -│ Services ○ postgres ○ redis ○ minio ○ minio_console │ -│ Shell ⚠ stale (config changed, reload shell) │ -│ Studio http://localhost:3000/studio?project=stackpanel │ -│ │ -│ Environment │ -│ Node 22.22.0 • Bun 1.3.3 • Go 1.25.6 • Python 3.12.12 │ -│ PostgreSQL 17.7 • Redis 8.2.3 • Docker 28.5.2 │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Quick Start │ -│ dev  Start all development services │ -│ dev stop  Stop all services │ -│ sp status  Open interactive dashboard │ -│ sp commands  List all available commands │ -│ │ -│ Shortcuts │ -│ sp  = stackpanel │ -│ spx = run devshell commands │ -│ x  = same as spx │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ ⚠ Action Required │ -│ ! Shell is stale (config changed) → exit && direnv allow │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Resources │ -│ Docs https://stackpanel.dev/docs │ -│ │ -╰────────────────────────────────────────────────────────────────────────╯ - -═══════════════════════════════════════════════════════════════ -Shell hook completed at 2026-02-28 22:43:17 -Log saved to: /Users/cm/git/darkmatter/stack/.stack/state/shell.log -═══════════════════════════════════════════════════════════════ -"colmena" - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git status --short", - "description": "Verify final changed files" -} -``` -**Output:** -``` -?? nix/stack/modules/colmena/ - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.6s) - -_Thinking:_ - - - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "completed", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "completed", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "t1", - "content": "Inspect existing directory-module pattern for module.nix/meta.nix/ui.nix and UI schema conventions", - "status": "completed", - "priority": "high" - }, - { - "id": "t2", - "content": "Implement new colmena module under nix/stack/modules with full option set and computed config wiring", - "status": "completed", - "priority": "high" - }, - { - "id": "t3", - "content": "Add UI definitions for colmena options and ensure module auto-discovery integration", - "status": "completed", - "priority": "high" - }, - { - "id": "t4", - "content": "Validate with targeted checks (nix eval or formatting/lint if available) and summarize changes", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 11.6s) - -_Thinking:_ - - - -Implemented a new built-in `colmena` stack module with UI-editable settings and command wrappers, following the directory-module pattern used across `nix/stack/modules`. - -- Added module entry + metadata in `nix/stack/modules/colmena/default.nix` and `nix/stack/modules/colmena/meta.nix`. -- Added full Colmena configuration surface in `nix/stack/modules/colmena/module.nix` under `stack.colmena.*` (enable flag, hive/flake targeting, node filters, eval/deploy limits, transfer/build toggles, and per-command extra flags). -- Implemented computed flag sets in `stack.colmena.computed` so the resolved args are visible for `common`, `apply`, `build`, and `eval`. -- Added generated scripts in `nix/stack/modules/colmena/module.nix`: - - `colmena-apply` - - `colmena-build` - - `colmena-eval` - each using your configured defaults and accepting extra passthrough args. -- Added health checks in `nix/stack/modules/colmena/module.nix` for Colmena availability and hive config presence. -- Added UI panel definitions in `nix/stack/modules/colmena/ui.nix`: - - Status panel (`PANEL_TYPE_STATUS`) - - Config form panel (`PANEL_TYPE_FORM`) with `configPath` mappings so fields can be edited from the UI (including `enable`, `config`, `flake`, filters, booleans, limits, and extra flag lists). - -Validation I ran: -- Entered devshell successfully with `nix develop --impure -c true`. -- Evaluated new metadata with `nix develop --impure -c nix eval --impure --expr '(import ./nix/stack/modules/colmena/meta.nix).id'` (returned `"colmena"`). -- Final git status is clean except the new module directory. - -Natural next steps: -1. Enable and tune it in your config via `stack.colmena` (or directly in Studio once panels load). -2. Reload shell (`exit && direnv allow` or re-enter `nix develop --impure`) and try `colmena-apply -- --help` to verify wrapper behavior with your defaults. - ---- - -## User - -create a plan to autogenerate colmena configs for apps. we also need a plan for a first-class-citizen UI for colmena as the primary deployment tool in the studio. This means we need a way to configure machines. These will use sst or alchemy, which will be defined as nix modules via the studio - ---- - -## Assistant (Plan · gpt-5.3-codex · 11.1s) - -_Thinking:_ - - - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "nix/stack/deployment/**/*.{nix,md}" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/reference.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/module.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/schema.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/schema.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/meta.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/default.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/lib/fly-oidc.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/container.nix -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/meta.nix -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/web/src/routes/studio/**/*.{ts,tsx}" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/secrets.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/setup.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/tasks.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/terminal.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/services.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/variables.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/team.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/network.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/processes.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/extensions.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/packages.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/modules.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/local-config.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/panels.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/files.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/inspector.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/infra.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/roadmap.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/index.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/checks.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/databases.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/devshells.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/deploy.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/configuration.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/dashboard.tsx -/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/apps.tsx -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/stack-go/**/*.go" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/docgen_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/convert.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/discovery.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/config_generate_example.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/agenix.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/secrets_groups.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/config.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/config_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixeval/eval.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/store.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/secrets_recipients.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/root.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/secrets.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/caddy.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/exec/exec.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/server.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/security_status.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/vars.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/healthchecks.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/commands_tui.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/nixify.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/devmode.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/nixdata_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/json_transform.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/nix_data.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/paths.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/transform.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/entities.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/cli.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/users.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/modules.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/docgen.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/helpers.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/sops.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/ws.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nix/serialize.go -/Users/cm/git/darkmatter/stack/apps/stack-go/tools.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/registry_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/registry.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/process_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/userconfig/userconfig.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/base.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/process.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/services/base_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixeval/presets_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixeval/presets.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixeval/nixeval.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixeval/nixeval_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nix/serialize_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/exec/exec_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/envvars/envvars.go -/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/common/logger.go -/Users/cm/git/darkmatter/stack/apps/stack-go/main.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/services/minio.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/services/redis.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/services/postgres.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/render.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/types.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/options.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/github/github.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/docgen/frontmatter.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/sst.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/server_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/shell_manager.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/sse.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/pair.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/project_context.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/nixpkgs_search.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/registry.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/process_compose.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/project_handlers.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/modules.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/jwt.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/nix_files.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/nix_ui.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/connect_service.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/flake_watcher.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/cors_auth.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/connect_handlers.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/connect_modules.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/connect_entities_gen.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/project/project.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/chamber.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/agenix_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/internal/agent/server/api_handlers.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/services.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/root_test.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/ports.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/status.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/project.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/preflight.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/templates.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/env.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/hook.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/gendocs.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/init.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/motd.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/logs.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/agent.go -/Users/cm/git/darkmatter/stack/apps/stack-go/cmd/cli/commands.go - -(Results are truncated. Consider using a more specific path or pattern.) -``` -``` - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "colmena|deployment|machine|alchemy|sst", - "path": "/Users/cm/git/darkmatter/stack/apps/web/src", - "include": "**/*.{ts,tsx}" -} -``` -**Output:** -``` -Found 100 matches -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps-panel-alt.tsx: - Line 88: const deploymentPanels = useMemo(() => { - Line 90: (p) => p.module === "deployment-fly" || p.module === "deployment-cloudflare", - Line 98: p.module !== "deployment-fly" && - Line 99: p.module !== "deployment-cloudflare", - Line 520: deploymentPanels={deploymentPanels.filter( - -/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-setup-progress.ts: - Line 70: const { data: sstData } = useNixData("sst", { - Line 180: status: sstData?.enable - Line 226: "Configure your local machine to use an existing AWS KMS key", - Line 268: }, [token, isConnected, sstData, agentClient, secretsBackend, isChamber]); - -/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent.ts: - Line 274: /** SST deployment status */ - Line 1152: const res = await fetch(`${this.baseUrl}/api/sst/config`, { - Line 1161: * Get the current SST deployment status. - Line 1164: const res = await fetch(`${this.baseUrl}/api/sst/status`, { - Line 1176: const res = await fetch(`${this.baseUrl}/api/sst/outputs`, { - Line 1188: const res = await fetch(`${this.baseUrl}/api/sst/resources`, { - Line 1200: const res = await fetch(`${this.baseUrl}/api/sst/deploy`, { - Line 1216: const res = await fetch(`${this.baseUrl}/api/sst/remove`, { - -/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts: - Line 17: * const { data: sst } = useSSTStatus(); - Line 24: *

SST Deployed: {sst?.deployed ? 'Yes' : 'No'}

- Line 176: sst: () => [...agentQueryKeys.all, "sst"] as const, - Line 177: sstStatus: () => [...agentQueryKeys.sst(), "status"] as const, - Line 178: sstConfig: () => [...agentQueryKeys.sst(), "config"] as const, - Line 179: sstOutputs: () => [...agentQueryKeys.sst(), "outputs"] as const, - Line 180: sstResources: () => [...agentQueryKeys.sst(), "resources"] as const, - Line 628: * Query hook for getting the SST deployment status. - Line 634: queryKey: agentQueryKeys.sstStatus(), - Line 650: queryKey: agentQueryKeys.sstConfig(), - Line 666: queryKey: agentQueryKeys.sstOutputs(), - Line 682: queryKey: agentQueryKeys.sstResources(), - Line 704: queryClient.invalidateQueries({ queryKey: agentQueryKeys.sst() }); - Line 722: queryClient.invalidateQueries({ queryKey: agentQueryKeys.sst() }); - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/types.ts: - Line 114: sstData: SSTData | null; - Line 115: sstFormData: SSTFormData; - Line 118: sstSaving: boolean; - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/setup-wizard.tsx: - Line 88: const { data: sstData, mutate: setSstData } = useNixData("sst", { - Line 91: const [sstFormData, setSstFormData] = useState({}); - Line 92: const [sstSaving, setSstSaving] = useState(false); - Line 132: const hasAwsKms = sstData?.kms?.enable ?? false; - Line 196: if (sstData) { - Line 198: enable: sstData.enable ?? false, - Line 199: "project-name": sstData["project-name"] || projectName, - Line 200: region: sstData.region || inheritedRegion, - Line 201: "account-id": sstData["account-id"] || inheritedAccountId, - Line 202: "config-path": sstData["config-path"] || "packages/infra/sst.config.ts", - Line 204: enable: sstData.kms?.enable ?? true, - Line 205: alias: sstData.kms?.alias || `${projectName}-secrets`, - Line 206: "deletion-window-days": sstData.kms?.["deletion-window-days"] ?? 30, - Line 209: provider: sstData.oidc?.provider || "github-actions", - Line 211: org: sstData.oidc?.["github-actions"]?.org || githubOrg, - Line 213: sstData.oidc?.["github-actions"]?.repo || githubRepoName || "*", - Line 214: branch: sstData.oidc?.["github-actions"]?.branch || "*", - Line 217: "org-id": sstData.oidc?.flyio?.["org-id"] || "", - Line 218: "app-name": sstData.oidc?.flyio?.["app-name"] || "*", - Line 222: sstData.oidc?.["roles-anywhere"]?.["trust-anchor-arn"] || "", - Line 227: sstData.iam?.["role-name"] || `${projectName}-secrets-role`, - Line 232: sstData, - Line 247: await setSstData(sstFormData); - Line 311: status: sstData?.enable ? "complete" : isChamber ? "incomplete" : "optional", - Line 402: sstData: sstData ?? null, - Line 403: sstFormData, - Line 406: sstSaving, - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/inspector-panel.tsx: - Line 915: passthru: { label: "Passthru", variant: "default" }, - -/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-inspector-data.test.ts: - Line 375: passthru: "Passthru", - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/shared/panel-types.ts: - Line 44: /** Nix config path for saving field value (e.g., 'stack.deployment.fly.organization') */ - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/kms-config-step.tsx: - Line 15: description: "Configure your local machine to use an existing AWS KMS key", - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/project-info-step.tsx: - Line 89: "Runtime state like current configuration snapshot and your machine-specific configuration. Rebuilt each shell entry. Not tracked in git.", - -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/infrastructure-step.tsx: - Line 25: sstData, - Line 26: sstFormData, - Line 29: sstSaving, - Line 40: status: sstData?.enable ? "complete" : "optional", - Line 71: code: `arn:aws:iam::${sstFormData["account-id"]}:role/${sstFormData.iam?.["role-name"]}`, - Line 82: code: `packages/infra/sst.config.ts`, - Line 113: checked={sstFormData.enable ?? false} - Line 115: setSstFormData({ ...sstFormData, enable: checked }) - Line 120: {sstFormData.enable && ( - Line 132: value={sstFormData.region || "us-west-2"} - Line 134: setSstFormData({ ...sstFormData, region: value }) - Line 153: value={sstFormData["account-id"] || ""} - Line 156: ...sstFormData, - Line 177: value={sstFormData.oidc?.provider || "github-actions"} - Line 180: ...sstFormData, - Line 181: oidc: { ...sstFormData.oidc, provider: value }, - Line 202: (p) => p.value === sstFormData.oidc?.provider, - Line 209: {sstFormData.oidc?.provider === "github-actions" && ( - Line 216: sstFormData.oidc?.["github-actions"]?.org || - Line 221: ...sstFormData, - Line 223: ...sstFormData.oidc, - Line 225: ...sstFormData.oidc?.["github-actions"], - Line 238: sstFormData.oidc?.["github-actions"]?.repo || "*" - Line 242: ...sstFormData, - Line 244: ...sstFormData.oidc, - Line 246: ...sstFormData.oidc?.["github-actions"], - Line 276: checked={sstFormData.kms?.enable ?? true} - Line 279: ...sstFormData, - Line 280: kms: { ...sstFormData.kms, enable: checked }, - Line 286: {sstFormData.kms?.enable && ( - Line 290: value={sstFormData.kms?.alias || ""} - Line 293: ...sstFormData, - Line 294: kms: { ...sstFormData.kms, alias: e.target.value }, - Line 307: -00444| -00445|
-00446| -00453| -00454| {app.name} -00455| -00456| {app.type && ( -00457| -00458| {app.type} -00459| -00460| )} -00461|
-00462| -00463|
-00464|
-00465| {app.tasks.length} -00466| tasks -00467|
-00468|
-00469| -00470| {app.variables.length + app.secrets.length} -00471| -00472| vars -00473|
-00474| -00475| -00476| -00477| :{app.port} -00478| -00479| -00480| -00481| Stable port for this app -00482| -00483| -00484|
-00485| -00486|
-00487| -00503|
-00504| -00505| -00506| {/* Expanded Content with subnav */} -00507| {isExpanded && ( -00508| p.apps[app.id] != null, -00516| )} -00517| containerPanels={containerPanels.filter( -00518| (p) => p.apps[app.id] != null, -00519| )} -00520| deploymentPanels={deploymentPanels.filter( -00521| (p) => p.apps[app.id] != null, -00522| )} -00523| editingTask={editingTask} -00524| taskCommandOverride={taskCommandOverride} -00525| onTaskEdit={handleTaskEdit} -00526| onTaskSave={handleTaskSave} -00527| onTaskCancel={handleTaskCancel} -00528| onTaskCommandChange={setTaskCommandOverride} -00529| onAddVariable={(envKey, value, environments) => -00530| handleAddVariableToApp( -00531| app.id, -00532| envKey, -00533| value, -00534| environments, -00535| ) -00536| } -00537| onUpdateVariable={( -00538| oldEnvKey, -00539| newEnvKey, -00540| value, -00541| environments, -00542| ) => -00543| handleUpdateVariableInApp( -00544| app.id, -00545| oldEnvKey, -00546| newEnvKey, -00547| value, -00548| environments, -00549| ) -00550| } -00551| onDeleteVariable={(envKey) => -00552| handleDeleteVariableFromApp(app.id, envKey) -00553| } -00554| onUpdateEnvironments={(environments) => -00555| handleUpdateEnvironmentsForApp(app.id, environments) -00556| } -00557| onFrameworkChange={(fw) => -00558| handleUpdateFramework(app.id, fw) -00559| } -00560| /> -00561| )} -00562| -00563| ); -00564| })} -00565| -00566| )} -00567| -00568| -00569| ); -00570| } -00571| -00572| export default AppsPanelAlt; -00573| - -(End of file - total 573 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/modules.tsx" -} -``` -**Output:** -``` - -00001| /** -00002| * Modules Page -00003| * -00004| * Browse and configure Stack modules. This is the new unified module -00005| * browser that replaces the old extensions system. -00006| * -00007| * Modules provide: -00008| * - File generation -00009| * - Scripts/commands -00010| * - Tasks -00011| * - Health checks -00012| * - Services -00013| * - Secrets management -00014| * - Packages -00015| * - App configuration -00016| * - UI panels -00017| */ -00018| -00019| import { createFileRoute } from "@tanstack/react-router"; -00020| import { ModulesPanel } from "@/components/studio/panels/modules/modules-panel"; -00021| -00022| export const Route = createFileRoute("/studio/modules")({ -00023| component: ModulesPage, -00024| }); -00025| -00026| function ModulesPage() { -00027| return ( -00028|
-00029| -00030|
-00031| ); -00032| } -00033| - -(End of file - total 33 lines) -
-``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/panels.tsx" -} -``` -**Output:** -``` - -00001| import { createFileRoute } from "@tanstack/react-router"; -00002| import { PanelsPanel } from "@/components/studio/panels/panels-panel"; -00003| import { z } from "zod"; -00004| -00005| const panelsSearchSchema = z.object({ -00006| module: z.string().optional(), -00007| }); -00008| -00009| export const Route = createFileRoute("/studio/panels")({ -00010| component: PanelsPage, -00011| validateSearch: panelsSearchSchema, -00012| }); -00013| -00014| function PanelsPage() { -00015| return ( -00016|
-00017| -00018|
-00019| ); -00020| } -00021| - -(End of file - total 21 lines) -
-``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/infra.tsx" -} -``` -**Output:** -``` - -00001| import { createFileRoute } from "@tanstack/react-router"; -00002| import { InfraPanel } from "@/components/studio/panels/infra-panel"; -00003| -00004| export const Route = createFileRoute("/studio/infra")({ -00005| component: InfraRoute, -00006| }); -00007| -00008| function InfraRoute() { -00009| return ; -00010| } -00011| - -(End of file - total 11 lines) - -``` -``` - ---- - -## Assistant (Plan · gpt-5.3-codex) - ---- - diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 23bd8cbb..b8029439 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -3,8 +3,7 @@ This document provides a comprehensive reference for all environment variables used by Stack. > **Source of Truth**: The authoritative definitions are in: -> - Nix: `nix/stack/core/lib/envvars.nix` -> - Go: `packages/stack-go/envvars/envvars.go` +> - Go: `apps/stackpanel-go/pkg/envvars/envvars.go` ## Quick Reference @@ -604,8 +603,5 @@ stack env debug When adding new environment variables: -1. **Update Nix definitions** in `nix/stack/core/lib/envvars.nix` -2. **Update Go definitions** in `packages/stack-go/envvars/envvars.go` -3. **Update this documentation** - -Both files should stay in sync to ensure consistent behavior between Nix and Go code. +1. **Update Go definitions** in `apps/stackpanel-go/pkg/envvars/envvars.go` +2. **Update this documentation** diff --git a/docs/infra.md b/docs/infra.md deleted file mode 100644 index df58c9d0..00000000 --- a/docs/infra.md +++ /dev/null @@ -1,9566 +0,0 @@ -# Infra module deploy --adopt failure despite help showing option - -**Session ID:** ses_35892eb07ffevEFiAHEU4zQAjT -**Created:** 2/28/2026, 7:24:29 PM -**Updated:** 3/1/2026, 7:19:05 AM - ---- - -## Assistant (Build · gpt-5.3-codex · 9.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix show-config | rg \"^builders =|^max-jobs =|^builders-use-substitutes =|^fallback =\"", - "description": "Check nix builder configuration values" -} -``` -**Output:** -``` -warning: 'show-config' is a deprecated alias for 'config show' -builders = ssh://root@100.102.113.26 x86_64-linux /etc/nix/builder_ed25519 16 1 big-parallel,benchmark,kvm,nixos-test -builders-use-substitutes = true -fallback = false -max-jobs = 24 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.9s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ssh -o ConnectTimeout=5 -i /etc/nix/builder_ed25519 root@100.102.113.26 'echo ok'", - "description": "Test remote nix builder SSH connectivity" -} -``` -**Output:** -``` -ok - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.1s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix store ping --store \"ssh://root@100.102.113.26\"", - "description": "Ping remote Nix store over SSH" -} -``` -**Output:** -``` -warning: 'ping' is a deprecated alias for 'info' -Store URL: ssh://root@100.102.113.26 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 35.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix build --impure .#devShells.aarch64-darwin.default -L", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 30000, - "description": "Check if devshell build itself stalls" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 30000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 96.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure --option builders '' --option fallback true -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 90000, - "description": "Test develop without remote builder configuration" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 90000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix eval --raw --impure --expr '\"ok\"'", - "description": "Test basic nix daemon responsiveness" -} -``` -**Output:** -``` -ok -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 28.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "pgrep -af \"nix develop|bash --rcfile /tmp/nix-shell|nix-daemon __build-remote\"", - "description": "List hanging nix develop and nix-shell processes" -} -``` -**Output:** -``` -12984 -22953 -45950 -59070 -75186 -85568 -90006 -99002 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 12984,22953,45950,59070,75186,85568,90006,99002 -o pid,ppid,stat,etime,command", - "description": "Show details for lingering nix-related process IDs" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND -90006 85629 Ss 08:25 nix-daemon __build-remote 6 -12984 72659 S 02-21:38:19 bash --rcfile /tmp/nix-shell.ifHEBl -85568 5179 S+ 11:09 nix develop --verbose --debug --log-format bar-with-logs -L -22953 23851 S 10:06:45 bash --rcfile /tmp/nix-shell.RkSZvV -59070 22953 S+ 08:00:40 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O -75186 52085 S+ 01:08:59 bash --rcfile /tmp/nix-shell.5v7q3p -45950 11608 S 01-01:52:27 bash --rcfile /tmp/nix-shell.n5blbc -99002 90569 S 07:43:19 bash --rcfile /tmp/nix-shell.qZDJVz - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "kill 85568 90006", - "description": "Terminate stuck nix develop and remote build helper" -} -``` -**Output:** -``` -zsh:kill:1: kill 90006 failed: operation not permitted - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 85568,90006 -o pid,ppid,stat,etime,command", - "description": "Check whether targeted stuck processes remain" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 64.5s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Retest develop after killing stuck process" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/p67312d0vwhswgdzf38d8ffa12ky2pyf-stack-0.1.0.drv'... -building '/nix/store/26h81d1lqmd7w1qznqjmmd1n0ql7nzrh-check-files-drift.drv'... -building '/nix/store/qrd6rbkpvy6dpf4hzz9pwpwayzqs4hkr-write-files.drv'... -building '/nix/store/mq0vqf71y6cngd22sdqaqqrfz0prdv2k-check-files.drv'... -building '/nix/store/1spcb854akafmycs84cqyvnjcix3a0vl-write-files.drv'... -building '/nix/store/0hzvw2kw65bjj3mvc6xm22jagpj3l6kb-stack-scripts.drv'... -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 60000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.9s) - -_Thinking:_ - - - -Yep — I found the likely cause. - -`nix develop` wasn’t truly deadlocked in eval; it was blocked behind an already-running stuck `nix develop`/remote build worker. - -I found these long-lived processes on your machine: - -- `nix develop --verbose --debug --log-format bar-with-logs -L` (running ~11 min) -- `nix-daemon __build-remote` worker tied to it -- several old `bash --rcfile /tmp/nix-shell...` sessions - -After terminating the stuck `nix develop` process, a fresh `nix develop --impure -c true` immediately started progressing again (building derivations), instead of sitting at daemon op logs. - -**What to do now** -1. In your terminal, kill stale `nix develop` jobs: - - `pkill -f "nix develop --verbose --debug --log-format bar-with-logs -L"` - - or manually: `ps -axo pid,etime,command | rg "nix develop|nix-shell"` then `kill ` -2. If it still stalls, restart Determinate Nix daemon: - - `sudo launchctl kickstart -k system/systems.determinate.nix-daemon` -3. Retry: - - `nix develop --impure` - -If you want, I can also give you a small `bin/nix-unstick` script for this repo to automate those checks/cleanup. - ---- - -## User - -how do i setup machines in the studio UI? lets also make sure the docs site is updated and has clear, detailed docs for this deploy feature - ---- - -## Assistant (Build · claude-opus-4-6 · 174.7s) - -_Thinking:_ - -The user is asking about two things: -1. How to set up machines in the studio UI (likely referring to Fly.io machines or some deployment feature) -2. Making sure the docs site has clear, detailed documentation for the deploy feature - -Let me first understand what "machines" means in this context - it's likely related to the Fly.io deployment module that was throwing "Unsupported host fly" earlier. Let me explore the codebase to understand the deployment/machines setup in the studio UI and the docs. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore deployment/machines UI and docs", - "prompt": "Search the codebase thoroughly for:\n\n1. How \"machines\" or deployment targets are configured in the studio UI (apps/web). Look for components related to deployment, machines, infra setup, infrastructure-step, etc.\n2. The deployment module in nix/stack/infra/modules/deployment/ - understand what hosts/frameworks are supported and how apps get registered\n3. The current docs content for deployment/infra in apps/docs/content/docs/ - find all relevant deployment and infra docs\n4. How the infra panel works in the studio UI (apps/web/src/components/studio/panels/infra-panel.tsx, setup steps, etc.)\n5. The .stack/config.nix to understand how deployment is currently configured\n\nReturn:\n- File paths and key line numbers for all deployment-related UI components\n- File paths for all deployment docs\n- The current deployment config from .stack/config.nix (the deployment/infra section)\n- What hosts/frameworks the deployment module supports\n- How the studio UI handles infrastructure/deployment setup", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have a thorough understanding of the entire deployment/infra system. Here is my complete report. - ---- - -## Comprehensive Deployment & Infrastructure Search Results - -### 1. Deployment-Related UI Components (apps/web) - -#### Route Files -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/deploy.tsx` | L1-11 | Route at `/studio/deploy`, renders `DeployPanel` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/infra.tsx` | L1-11 | Route at `/studio/infra`, renders `InfraPanel` | - -#### Deploy Panel (Colmena-based NixOS Deployment) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` | L1-547 | **Primary deploy panel**. Colmena-centric deployment management. Shows machine inventory cards, app-to-machine mapping, and deploy actions (eval/build/apply). Tabs: Machines, App Targets, Actions, Settings. | -| ^^ | L48-65 | `MachineInfo` type: id, name, host, ssh, tags, roles, provider, arch, publicIp, privateIp, targetEnv, labels | -| ^^ | L67-74 | `AppDeployMapping` type: enable, targets, resolvedTargets, role, nixosModules, system | -| ^^ | L89-125 | `useColmenaData()` hook: reads `nixConfig.serializable.colmena` for machine inventory and iterates `apps` for deploy-enabled entries | -| ^^ | L131-201 | `MachineCard` component: renders individual machine with SSH info, architecture, provider, tags, roles | -| ^^ | L250-546 | `DeployPanel` main component: status overview (machines/reachable/unreachable/deploy-enabled apps), tabbed machine list, app target rows, Colmena action buttons (eval/build/apply), settings display | - -#### Infra Panel (SST/AWS Infrastructure) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra-panel.tsx` | L1-808 | **Infrastructure panel**. AWS infrastructure provisioning with SST. Tabs: Status, Deploy, Outputs, Resources, Configure | -| ^^ | L50-248 | `InfraPanel` main component: status cards (Enabled/Deployed/KMS), tabbed layout | -| ^^ | L257-363 | `StatusTabContent`: shows SST project name, region, OIDC provider, IAM role, KMS key, config path | -| ^^ | L366-451 | `DeployTabContent`: stage selector (dev/staging/production), CommandRunner for `sst deploy` and `sst remove` | -| ^^ | L453-496 | `OutputsTabContent`: displays deployed stack outputs (ARNs, etc.) | -| ^^ | L498-540 | `ResourcesTabContent`: lists deployed AWS resources by type/URN | -| ^^ | L542-807 | `ConfigureTabContent`: full SST config form -- enable toggle, project name, region, account ID, config path, KMS settings, OIDC provider (GitHub Actions/Fly.io/Roles Anywhere), IAM role name | - -#### Infra Shared Components -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/index.ts` | L1-9 | Barrel export for infra module | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/types.ts` | L1-54 | Type definitions: `SSTData`, `SSTStatus`, `SSTResource` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/constants.ts` | L1-73 | Constants: `OIDC_PROVIDERS`, `AWS_REGIONS`, `DEFAULT_SST_DATA`, `mergeWithDefaults()` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/components.tsx` | L1-114 | Shared UI: `StatusCard`, `ResourceRow`, `OutputRow` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` | L1-327 | `useSSTConfig()` hook: manages SST form state, reads `useNixData("sst")`, calls agent for status/outputs/resources, handles deploy/remove via `agentClient.deploySSTInfra()` | - -#### Setup Wizard Infrastructure Step -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/infrastructure-step.tsx` | L1-340 | **Setup wizard step** for AWS infrastructure. Guided config for IAM Role, KMS Key, SST config. Toggle enable, AWS region/account, OIDC provider (GitHub Actions specific fields), KMS key alias. Links to infra panel for deployment. | - -#### Per-App Deployment Tab -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/app-expanded-content.tsx` | L52 | Nav item `{ id: "deployment", label: "Deployment", icon: Rocket }` | -| ^^ | L85-86 | Props: `deploymentPanels?: AppModulePanel[]` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps-panel-alt.tsx` | L88-101 | Filters module panels: deployment panels are `module === "deployment-fly"` or `module === "deployment-cloudflare"`, routed to app Deployment tab | - -#### Sidebar Navigation -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-sidebar.tsx` | L91-92 | Panel IDs include `"infra"` and `"deploy"` | -| ^^ | L122 | Main nav: `{ id: "deploy", label: "Deploy", icon: Rocket }` | -| ^^ | L129 | Tools nav: `{ id: "infra", label: "Infrastructure", icon: Cloud }` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-header.tsx` | L44 | Label mapping: `infra: "Infrastructure"` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/landing/infrastructure-section.tsx` | -- | Landing page infrastructure section | - ---- - -### 2. Deployment Module in Nix (nix/stack/) - -#### Top-Level Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/default.nix` | L1-39 | **Aggregator** -- imports `./fly` and `./cloudflare` | - -#### Infra Modules Deployment (Alchemy-Based) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix` | L1-157 | **Infra deployment module** -- reads `framework` x `deployment.host` from each app and creates Alchemy resources | - -**Supported Framework x Host Matrix** (from lines 12-19): - -| Framework | Host | Alchemy Resource | -|---|---|---| -| `tanstack-start` | `cloudflare` | `TanStackStart` | -| `nextjs` | `cloudflare` | `Nextjs` | -| `vite` | `cloudflare` | `cloudflare.Vite` | -| `hono` | `cloudflare` | `cloudflare.Worker` | -| `astro` | `cloudflare` | `Astro` | -| `remix` | `cloudflare` | `Remix` (Worker-based) | -| `nuxt` | `cloudflare` | (listed in frameworkNames, line 51) | -| `*` | `fly` | Fly container (separate module) | -| `*` | `vercel` | (planned, checked on line 80) | -| `*` | `aws` | (planned, checked on line 81) | - -**How apps get registered** (lines 64-69): `deployableApps` = apps where `deployment.enable == true` AND a framework is detected AND `deployment.host != null`. The module auto-enables `stack.infra.enable` and registers itself as `stack.infra.modules.deployment` with pure-data inputs per app (framework, host, path, bindings, secrets + framework-specific options). - -#### Fly.io Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix` | L1-603 | **Full Fly.io deployment module**. Container-based deployment using nix2container. Generates fly.toml, deploy scripts, wrapped `fly-` CLI commands, turbo tasks, health checks | -| ^^ | L59-135 | Per-app options added via `deploymentAppModule`: fly.appName, fly.region, fly.memory, fly.cpus, fly.cpuKind (shared/performance), fly.autoStop (off/stop/suspend), fly.autoStart, fly.minMachines, fly.forceHttps, fly.env; container.type (bun/node/go/static/custom), container.port, container.entrypoint | -| ^^ | L380-415 | **Global options**: `stack.deployment.defaultHost` (enum: cloudflare/fly/vercel/aws), `stack.deployment.fly.organization`, `stack.deployment.fly.defaultRegion` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/meta.nix` | L1-64 | Metadata: id=`deployment-fly`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/schema.nix` | -- | SpField schema definitions for Fly options | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/container.nix` | -- | Container configuration | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/reference.nix` | -- | Reference docs | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix` | -- | UI panel definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/lib/fly-oidc.nix` | -- | Fly.io OIDC integration | - -#### Cloudflare Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/module.nix` | L1-110 | **Cloudflare Workers per-app options**. Adds `deployment.cloudflare.*` to each app via appModules: workerName, route, compatibility (node/browser), kvNamespaces, d1Databases, r2Buckets. Global options: accountId, compatibilityDate, defaultRoute | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/meta.nix` | L1-43 | Metadata: id=`deployment-cloudflare`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/schema.nix` | -- | SpField schema definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix` | -- | UI panel definitions | - -#### Machine Inventory Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` | L1-307 | **Machine inventory** for Colmena deployments. Sources: `static` or `aws-ec2`. Machine type: id, name, host, ssh config, tags, roles, provider, arch, publicIp, privateIp, labels, nixosProfile, nixosModules, targetEnv, env, metadata. AWS EC2 discovery via filters, tag-based role/tag/env resolution, host preference order | - -#### Infrastructure Module System -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` | L1-38 | **Aggregator** -- imports options.nix, codegen.nix, and all 11 infra sub-modules | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix` | L1-335 | **Core options**: enable, framework (alchemy only), output-dir, key-format, storage-backend (chamber/sops/ssm/none), modules registry (infraModuleType), package config, outputs stub | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix` | L1-968 | **Code generation engine**: generates `src/index.ts` (Infra class), `src/types.ts`, custom alchemy resources (KMS, IAM, EC2, ALB, etc.), `alchemy.run.ts` orchestrator, `package.json`, `tsconfig.json`, `infra-inputs.json`. Registers scripts: `infra:deploy`, `infra:destroy`, `infra:dev`, `infra:pull-outputs`. Registers as module `infra` in category `infrastructure` | - -All 11 infra sub-modules: -- `aws-secrets` -- KMS + IAM roles -- `deployment` -- App hosting via Alchemy -- `database` -- Neon Postgres / devenv / Docker -- `cache` -- Upstash Redis / devenv / Docker -- `machines` -- Machine inventory (static/AWS EC2) -- `aws-network` -- VPC, subnets -- `aws-security-groups` -- Security groups -- `aws-key-pairs` -- SSH key pairs -- `aws-iam` -- IAM roles/policies -- `aws-ec2` -- EC2 instances -- `aws-ec2-app` -- Per-app EC2 resources (ALB, target groups, etc.) - ---- - -### 3. Deployment Documentation - -#### Public Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx` | Deployment overview | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx` | Fly.io deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx` | Cloudflare deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx` | Container deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx` | SST infrastructure guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json` | Section metadata | - -#### Internal/Reference Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment.mdx` | Deployment module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra.mdx` | Infra module system reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/options.mdx` | Infra options reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/codegen.mdx` | Infra codegen reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/templates.mdx` | Infra templates reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/deployment/module.mdx` | Deployment infra module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-secrets/module.mdx` | AWS secrets module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/database/module.mdx` | Database module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/cache/module.mdx` | Cache module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/machines/module.mdx` | Machines module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-network/module.mdx` | AWS network module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/module.mdx` | Fly module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/container.mdx` | Fly container reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/reference.mdx` | Fly reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/schema.mdx` | Fly schema reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/lib/fly-oidc.mdx` | Fly OIDC reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/module.mdx` | Cloudflare module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/schema.mdx` | Cloudflare schema reference | - ---- - -### 4. Current Deployment Config from .stack/config.nix - -From `/Users/cm/git/darkmatter/stack/.stack/config.nix`: - -**Deployment section** (lines 218-223): -```nix -deployment = { - fly = { - organization = "darkmatter"; - }; -}; -``` - -**Per-app deployment** -- `web` app (lines 76-96): -```nix -web = { - deployment = { - bindings = [ "DATABASE_URL" "CORS_ORIGIN" "BETTER_AUTH_SECRET" "BETTER_AUTH_URL" "POLAR_ACCESS_TOKEN" "POLAR_SUCCESS_URL" ]; - enable = true; - host = "fly"; - secrets = [ "DATABASE_URL" "BETTER_AUTH_SECRET" "POLAR_ACCESS_TOKEN" ]; - fly = { - appName = "stack-web"; - region = "iad"; - }; - }; - framework = { tanstack-start = { enable = true; }; }; - container = { enable = true; type = "bun"; }; -}; -``` - -**Infra section** (lines 447-475): -```nix -infra = { - enable = true; - storage-backend = { - type = "sops"; - sops.group = "dev"; - }; - database = { - enable = true; - provider = "neon"; - neon = { region = "aws-us-east-1"; api-key-ssm-path = "/common/neon-api-key"; }; - }; - machines = { - enable = true; - source = "aws-ec2"; - aws = { - region = "us-west-2"; - filters = [{ name = "instance-state-name"; values = [ "running" ]; }]; - }; - machines = {}; - }; -}; -``` - -**SST section** (lines 480-509): -```nix -sst = { - account-id = "950224716579"; - config-path = "packages/infra/sst.config.ts"; - enable = true; - iam = { role-name = "stack-secrets-role"; }; - kms = { alias = "stack-secrets"; deletion-window-days = 30; enable = true; }; - oidc = { - flyio = { app-name = "*"; org-id = ""; }; - github-actions = { branch = "*"; org = "darkmatter"; repo = "stack"; }; - provider = "github-actions"; - roles-anywhere = { trust-anchor-arn = ""; }; - }; - project-name = "stack"; - region = "us-west-2"; -}; -``` - ---- - -### 5. How the Studio UI Handles Infrastructure/Deployment Setup - -The studio UI has **two separate panels** for deployment, plus a setup wizard step: - -**A. Deploy Panel (`/studio/deploy`)** -- Colmena-based NixOS deployment to machines: -- Reads machine inventory from `nixConfig.colmena.machinesComputed` and app deploy configs from `nixConfig.apps.*.deploy` -- Displays a **machine inventory grid** (MachineCard components showing hostname, SSH, arch, provider, tags, roles) -- Shows **app-to-machine mapping** (which apps deploy to which machines, resolved by targets/roles) -- Provides **Colmena actions**: eval, build, apply buttons -- Displays **settings** for Colmena config (machine source, hive config path, etc.) -- Data flow: Agent evaluates Nix config -> serializes colmena data -> SSE events -> React queries `useNixConfig()` - -**B. Infra Panel (`/studio/infra`)** -- SST/AWS infrastructure provisioning: -- Uses `useSSTConfig()` hook that reads from `useNixData("sst")` for persistent config and calls agent REST endpoints (`getSSTStatus`, `getSSTOutputs`, `getSSTResources`, `deploySSTInfra`, `removeSSTInfra`) -- **Status tab**: shows project overview, IAM role, KMS key, config path status -- **Deploy tab**: stage selector (dev/staging/production) + CommandRunner that executes `sst deploy --stage ` or `sst remove --stage ` via the agent -- **Outputs tab**: displays stack outputs (ARNs, URLs) with copy-to-clipboard -- **Resources tab**: lists deployed AWS resources by type/URN -- **Configure tab**: full SST config editor (enable, project name, region, account ID, KMS settings, OIDC provider, IAM role). Links to the Setup wizard for initial configuration. - -**C. Setup Wizard Infrastructure Step** (`infrastructure-step.tsx`): -- Part of the Project Setup wizard at `/studio/setup` -- Guided step for initial AWS infrastructure config -- Shows a table of what gets deployed (IAM Role, KMS Key, SST Config) -- Form: enable toggle, AWS region/account, OIDC provider (GitHub Actions with org/repo), KMS key toggle + alias -- Saves to `.stack/data/sst.nix` via the agent - -**D. Per-App Deployment Tab** (within the Apps panel): -- Each expanded app has a "Deployment" tab (nav item at line 52 of app-expanded-content.tsx) -- Deployment panels are filtered by module: `deployment-fly` and `deployment-cloudflare` panels are separated from generic module panels (apps-panel-alt.tsx lines 88-101) -- These panels render the per-app deployment config form via `AppConfigFormRenderer` using the schema defined in the Nix module's `ui.nix` - - -session_id: ses_35680e048ffea12FLMy7pJZVXe - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.6s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers & Deployment -00003| description: Build containers and deploy your applications to production -00004| icon: cloud -00005| --- -00006| -00007| import { Cards, Card } from "fumadocs-ui/components/card"; -00008| import { Callout } from "fumadocs-ui/components/callout"; -00009| -00010| Stack supports building containers and deploying to multiple platforms—all configured from the same Nix source of truth as your dev environment. Container definitions, deployment targets, and infrastructure provisioning are declared alongside your services and apps, so everything stays in sync. -00011| -00012| ## The Deployment Story -00013| -00014| Stack's approach to deployment follows the same philosophy as everything else: **declare it once, generate the artifacts your tools expect.** -00015| -00016| - Need a `Dockerfile`? Stack generates one optimized for your stack. -00017| - Need a Cloudflare Workers config? Declared in Nix, generated as standard config files. -00018| - Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up. -00019| -00020| The generated files are standard—`Dockerfile`, `fly.toml`, `sst.config.ts`—committed to Git and usable without Nix. Your CI pipeline reads them as normal config. If you eject from Stack, the files stay. -00021| -00022| ## What's Available -00023| -00024| -00025| -00026| Build OCI-compatible container images using Nix or Dockerfile generation -00027| -00028| -00029| Deploy to Cloudflare Workers and Pages -00030| -00031| -00032| Deploy to Fly.io with generated fly.toml and Dockerfile -00033| -00034| -00035| AWS infrastructure provisioning with KMS, IAM, and OIDC authentication -00036| -00037| -00038| -00039| ## Quick Example -00040| -00041| A typical deployment setup might combine containers with a deployment target: -00042| -00043| ```nix -00044| { -00045| # Define your apps -00046| stack.apps.api = { -00047| port = 1; -00048| root = "./apps/api"; -00049| build = "bun run build"; -00050| }; -00051| -00052| # Build a container for the API -00053| stack.containers.api = { -00054| enable = true; -00055| app = "api"; -00056| registry = "ghcr.io/my-org/myapp-api"; -00057| }; -00058| -00059| # Deploy to Fly.io -00060| stack.deployment.fly = { -00061| enable = true; -00062| apps.api = { -00063| region = "iad"; -00064| vm-size = "shared-cpu-1x"; -00065| }; -00066| }; -00067| } -00068| ``` -00069| -00070| From this, Stack generates a `Dockerfile` for the API, a `fly.toml` with the correct configuration, and optionally CI steps that build and deploy on merge to `main`. -00071| -00072| ## How Deployment Connects to CI -00073| -00074| Deployment configuration feeds into [CI generation](/docs/apps-ci/ci-generation). When you define a deployment target and enable the CI extension, the generated GitHub Actions workflow includes the appropriate deploy steps: -00075| -00076| ``` -00077| stack.apps.api.container -00078| → Generates container image -00079| → CI workflow gets docker build + push steps -00080| -00081| stack.apps.api.deployment.host = "fly" -00082| → Generates fly.toml -00083| → CI workflow gets flyctl deploy step -00084| -00085| stack.apps.api.deployment.host = "cloudflare" -00086| → Generates wrangler config -00087| → CI workflow gets cloudflare deploy step -00088| ``` -00089| -00090| Each deployment platform adds its own steps to the CI pipeline. Multiple platforms can coexist—deploy your frontend to Cloudflare and your API to Fly.io from the same workflow. -00091| -00092| -00093| All deployment configuration generates standard files that work without Nix. Your CI runners don't need Nix installed—they use Docker, flyctl, wrangler, or sst directly. -00094| -00095| -00096| ## Container Strategies -00097| -00098| Stack supports two approaches to building containers: -00099| -00100| | Strategy | How It Works | Best For | -00101| |----------|-------------|----------| -00102| | **Dockerfile generation** | Generates a standard `Dockerfile` optimized for your stack | Most projects, standard CI/CD pipelines | -00103| | **Nix-based containers** | Uses `nix2container` to build minimal OCI images directly from Nix | Minimal image size, reproducible builds | -00104| -00105| The Dockerfile approach is more portable—any Docker-compatible CI can build it. The Nix approach produces smaller, more reproducible images but requires Nix at build time. See [Containers](./containers) for details on both strategies. -00106| -00107| ## Platform-Specific Notes -00108| -00109| ### Cloudflare -00110| -00111| Cloudflare Workers and Pages deployments are lightweight—no containers involved. Stack generates the `wrangler.toml` configuration and can produce the correct build output format for your framework (static export for Pages, Worker bundles for Workers). -00112| -00113| ### Fly.io -00114| -00115| Fly.io deployments use containers. Stack generates both the `Dockerfile` and `fly.toml`, with the correct ports, health checks, and environment variables wired up from your Stack config. -00116| -00117| ### SST (AWS) -00118| -00119| SST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. -00120| -00121| ## Reference -00122| -00123| - [Options Reference → CI](/docs/reference/ci) for CI generation options -00124| - [Apps & CI](/docs/apps-ci) for how app definitions drive deployment -00125| - [Core Concepts → Building Source](/docs/concepts/building-source) for why deployment config is generated -00126| - [Core Concepts → File Generation](/docs/concepts/file-generation) for how generated files work - -(End of file - total 126 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Fly.io -00003| description: Deploy applications to Fly.io with generated configuration and OIDC authentication -00004| icon: plane -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can generate Fly.io deployment configuration from your project definition—`fly.toml` files, Dockerfiles, and OIDC-based authentication for passwordless deploys from CI. Instead of hand-maintaining Fly config that drifts from your actual app setup, the deployment extension derives everything from your Stack config. -00010| -00011| ## Enable Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Fly extension" - just configure your apps with `deployment.host = "fly"`. -00014| -00015| Stack will automatically add the `flyctl` CLI to your devshell when you have apps configured for Fly.io deployment. -00016| -00017| ## Defining Fly Apps -00018| -00019| Each app that should be deployed to Fly needs deployment configuration: -00020| -00021| ```nix -00022| stack.apps = { -00023| api = { -00024| port = 1; -00025| root = "./apps/api"; -00026| build = "bun run build"; -00027| -00028| deployment = { -00029| enable = true; -00030| host = "fly"; -00031| -00032| fly = { -00033| appName = "myapp-api"; -00034| region = "iad"; -00035| memory = "512mb"; -00036| cpus = 1; -00037| forceHttps = true; -00038| }; -00039| }; -00040| }; -00041| -00042| web = { -00043| port = 0; -00044| root = "./apps/web"; -00045| build = "bun run build"; -00046| -00047| deployment = { -00048| enable = true; -00049| host = "fly"; -00050| -00051| fly = { -00052| appName = "myapp-web"; -00053| region = "iad"; -00054| }; -00055| }; -00056| }; -00057| }; -00058| ``` -00059| -00060| Stack generates deployment configuration for each app, wiring up the correct ports, health checks, and resource settings. -00061| -00062| ## Generated Files -00063| -00064| When the Fly extension is enabled, Stack generates: -00065| -00066| | File | Description | -00067| |------|-------------| -00068| | `apps//fly.toml` | Fly.io app configuration | -00069| | `apps//Dockerfile` | Container image (if not already defined) | -00070| -00071| These are standard Fly.io files—`flyctl` reads them directly. Like all Stack-generated files, they're committed to Git so that deploys work without Nix. -00072| -00073| ```nix -00074| # The extension generates fly.toml from your config -00075| stack.files.entries."apps/api/fly.toml" = { -00076| # ... generated automatically from deployment.fly settings -00077| }; -00078| ``` -00079| -00080| -00081| You can inspect the generated `fly.toml` at any time. It's a standard file that `flyctl` understands—no proprietary format or wrapper. -00082| -00083| -00084| ## Container Builds -00085| -00086| Fly.io deploys container images. Configure containers alongside your app deployment: -00087| -00088| ```nix -00089| stack.apps.api = { -00090| port = 1; -00091| root = "./apps/api"; -00092| build = "bun run build"; -00093| -00094| deployment = { -00095| enable = true; -00096| host = "fly"; -00097| -00098| fly = { -00099| appName = "myapp-api"; -00100| region = "iad"; -00101| }; -00102| }; -00103| -00104| container = { -00105| enable = true; -00106| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00107| }; -00108| }; -00109| ``` -00110| -00111| Or define containers separately using the [containers module](/docs/deployment/containers) with matching names. -00112| -00113| ## OIDC Authentication -00114| -00115| For CI/CD deployments, configure OIDC authentication at the global deployment level: -00116| -00117| ```nix -00118| stack.deployment = { -00119| fly = { -00120| organization = "my-fly-org"; -00121| }; -00122| }; -00123| ``` -00124| -00125| When configured, the [CI generation](/docs/apps-ci/ci-generation) can produce GitHub Actions steps that authenticate with Fly using GitHub's OIDC token. This means: -00126| -00127| - No `FLY_API_TOKEN` secret to manage -00128| - Tokens are short-lived and scoped to each CI run -00129| - Authentication is tied to your GitHub org/repo identity -00130| - No token rotation or expiration to worry about -00131| -00132| ```yaml -00133| # Generated CI step (simplified) -00134| - name: Deploy to Fly -00135| uses: superfly/flyctl-actions/setup-flyctl@master -00136| - run: flyctl deploy --remote-only -00137| env: -00138| FLY_API_TOKEN: ${{ steps.fly-oidc.outputs.token }} -00139| ``` -00140| -00141| -00142| OIDC authentication requires your Fly.io organization to have OIDC configured with your GitHub repository. See the [Fly.io OIDC docs](https://fly.io/docs/reference/openid-connect/) for setup instructions. -00143| -00144| -00145| ## Multi-Region Deployment -00146| -00147| Deploy to multiple Fly.io regions for lower latency: -00148| -00149| ```nix -00150| stack.apps.api.deployment.fly = { -00151| appName = "myapp-api"; -00152| region = "iad"; -00153| -00154| # Note: Multi-region configuration is managed through Fly.io directly -00155| # Set your primary region here and use flyctl to scale to additional regions -00156| }; -00157| ``` -00158| -00159| After deploying to your primary region, use `flyctl scale` to add machines in additional regions. -00160| -00161| ## Environment and Secrets -00162| -00163| Configure environment variables and secrets for your Fly.io deployments: -00164| -00165| ```nix -00166| stack.apps.api.deployment.fly = { -00167| appName = "myapp-api"; -00168| -00169| # These get set as Fly secrets -00170| secrets = [ -00171| "DATABASE_URL" -00172| "API_KEY" -00173| "STRIPE_SECRET_KEY" -00174| ]; -00175| -00176| # Static environment variables (non-secret) -00177| env = { -00178| NODE_ENV = "production"; -00179| LOG_LEVEL = "info"; -00180| }; -00181| }; -00182| ``` -00183| -00184| Environment variables are included in deployment configuration. Secrets are managed through Stack's [secrets system](/docs/secrets/variables) and synced to Fly.io. -00185| -00186| ## Deployment Scripts -00187| -00188| The Fly extension registers scripts for common deployment tasks: -00189| -00190| ```bash -00191| # Deploy a specific app -00192| fly:deploy api -00193| -00194| # Deploy all Fly-configured apps -00195| fly:deploy-all -00196| -00197| # Check deployment status -00198| fly:status -00199| -00200| # Open the Fly dashboard -00201| fly:dashboard -00202| ``` -00203| -00204| These are standard Stack [scripts](/docs/dev-environment/packages-scripts) that wrap `flyctl` with the correct app names and configuration. -00205| -00206| ## Databases on Fly -00207| -00208| For managed databases, use Fly's CLI to create Postgres instances, then reference them in your app: -00209| -00210| ```bash -00211| # Create a Postgres cluster -00212| flyctl postgres create --name myapp-db --region iad -00213| -00214| # Attach it to your app -00215| flyctl postgres attach myapp-db --app myapp-api -00216| ``` -00217| -00218| Then reference the database in your deployment config: -00219| -00220| ```nix -00221| stack.apps.api.deployment.fly = { -00222| appName = "myapp-api"; -00223| -00224| secrets = [ -00225| "DATABASE_URL" # Automatically set by Fly when attached -00226| ]; -00227| }; -00228| ``` -00229| -00230| -00231| For local development, use Stack's built-in [PostgreSQL service](/docs/services/postgres) instead of Fly Postgres. -00232| -00233| -00234| ## Combining with Other Deployment Targets -00235| -00236| You can deploy different apps in your monorepo to different platforms: -00237| -00238| ```nix -00239| stack.apps = { -00240| web = { -00241| port = 0; -00242| # Deploy the frontend to Cloudflare -00243| deployment = { -00244| enable = true; -00245| host = "cloudflare"; -00246| }; -00247| }; -00248| -00249| api = { -00250| port = 1; -00251| # Deploy the API to Fly.io -00252| deployment = { -00253| enable = true; -00254| host = "fly"; -00255| fly.appName = "myapp-api"; -00256| }; -00257| }; -00258| }; -00259| ``` -00260| -00261| Each deployment target generates its own configuration independently. The [CI generation](/docs/apps-ci/ci-generation) produces workflow steps for all enabled deployment targets. -00262| -00263| ## CLI Commands -00264| -00265| When you have apps configured for Fly.io deployment, `flyctl` is automatically added to your devshell: -00266| -00267| ```bash -00268| # Direct flyctl access -00269| flyctl status --app myapp-api -00270| flyctl logs --app myapp-api -00271| flyctl ssh console --app myapp-api -00272| -00273| # Build containers for deployment -00274| nix build .#packages.x86_64-linux.container-api -00275| ``` -00276| -00277| ## Troubleshooting -00278| -00279| ### "App not found" errors -00280| -00281| Make sure the Fly app exists. On first deployment, you may need to create it: -00282| -00283| ```bash -00284| flyctl apps create myapp-api --org my-org -00285| ``` -00286| -00287| Or let `flyctl deploy` create it automatically on the first run. -00288| -00289| ### Build failures in CI -00290| -00291| Check that the generated Dockerfile is correct: -00292| -00293| ```bash -00294| cat apps/api/Dockerfile -00295| ``` -00296| -00297| If the auto-generated Dockerfile doesn't work for your app, you can provide a custom one and point the extension at it. -00298| -00299| ### OIDC token errors -00300| -00301| Verify that your Fly.io organization has OIDC configured for your GitHub repository. The OIDC subject must match what Fly expects—typically `repo:/:ref:refs/heads/main` or similar. -00302| -00303| ```bash -00304| # Test OIDC locally (requires flyctl >= 0.1.130) -00305| flyctl auth token --oidc -00306| ``` -00307| -00308| ### Secrets not available in the deployed app -00309| -00310| Fly secrets are set separately from `fly.toml` environment variables. Make sure secrets are actually set: -00311| -00312| ```bash -00313| flyctl secrets list --app myapp-api -00314| ``` -00315| -00316| If a secret is missing, set it manually or re-run the deployment script that syncs Stack variables to Fly secrets. -00317| -00318| ## Reference -00319| -00320| - [Options Reference → Deployment](/docs/reference) for Fly deployment options -00321| - [Containers](/docs/deployment/containers) for container image configuration -00322| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00323| - [Fly.io documentation](https://fly.io/docs/) for platform-specific details -00324| - [Fly.io OIDC](https://fly.io/docs/reference/openid-connect/) for passwordless CI authentication - -(End of file - total 324 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Cloudflare -00003| description: Deploy applications to Cloudflare Workers and Pages -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack includes a Cloudflare deployment module that generates the configuration needed to deploy your applications to [Cloudflare Workers](https://workers.cloudflare.com/) and [Cloudflare Pages](https://pages.cloudflare.com/). Like everything in Stack, the deployment config is derived from your project's source of truth—no hand-maintained `wrangler.toml` files that drift out of sync. -00010| -00011| ## Enable Cloudflare Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Cloudflare extension" - just configure your apps with `deployment.host = "cloudflare"`. -00014| -00015| Stack will automatically add the `wrangler` CLI to your devshell when you have apps configured for Cloudflare deployment. -00016| -00017| ## Configuration -00018| -00019| ### Global Settings -00020| -00021| Set your Cloudflare account ID at the global deployment level: -00022| -00023| ```nix -00024| stack.deployment = { -00025| cloudflare = { -00026| accountId = "your-cloudflare-account-id"; -00027| compatibilityDate = "2024-01-01"; -00028| }; -00029| }; -00030| ``` -00031| -00032| ### Per-App Deployment -00033| -00034| Configure each app with Cloudflare-specific deployment settings: -00035| -00036| ```nix -00037| stack.apps.web = { -00038| port = 0; -00039| framework = "tanstack-start"; -00040| root = "./apps/web"; -00041| build = "bun run build"; -00042| -00043| deployment = { -00044| enable = true; -00045| host = "cloudflare"; -00046| -00047| cloudflare = { -00048| workerName = "myapp-web"; -00049| type = "CLOUDFLARE_WORKER_TYPE_VITE"; -00050| route = "myapp.com/*"; -00051| bindings = { -00052| DATABASE_URL = "..."; -00053| }; -00054| }; -00055| }; -00056| }; -00057| ``` -00058| -00059| ### Environment Variables -00060| -00061| Pass environment variables to your Workers at deploy time: -00062| -00063| ```nix -00064| stack.apps.api = { -00065| port = 1; -00066| root = "./apps/api"; -00067| -00068| deployment = { -00069| enable = true; -00070| host = "cloudflare"; -00071| -00072| cloudflare = { -00073| workerName = "myapp-api"; -00074| bindings = { -00075| APP_ENV = "production"; -00076| API_VERSION = "v1"; -00077| }; -00078| }; -00079| }; -00080| }; -00081| ``` -00082| -00083| For sensitive values, use Cloudflare's secret bindings instead of plain vars. You can set these through the Wrangler CLI: -00084| -00085| ```bash -00086| wrangler secret put API_KEY --name myapp-api -00087| ``` -00088| -00089| -00090| Never put sensitive values in `vars`—they're visible in your generated config and committed to Git. Use Wrangler secrets or Stack's [secrets system](/docs/secrets) for anything confidential. -00091| -00092| -00093| ## Generated Files -00094| -00095| The Cloudflare module generates standard Wrangler configuration files: -00096| -00097| ``` -00098| wrangler.toml # or per-app: apps/web/wrangler.toml -00099| ``` -00100| -00101| These are normal Wrangler config files that the `wrangler` CLI reads directly. If you eject from Stack, the files stay and work as-is. -00102| -00103| Like all Stack-generated files, they're committed to Git so that team members and CI can deploy without Nix. -00104| -00105| ## Scripts -00106| -00107| When Cloudflare deployment is enabled, the following scripts are registered: -00108| -00109| | Script | Description | -00110| |--------|-------------| -00111| | `cloudflare:deploy` | Deploy all configured Workers | -00112| | `cloudflare:deploy:` | Deploy a specific Worker | -00113| | `cloudflare:dev` | Start Wrangler dev mode | -00114| | `cloudflare:tail` | Tail Worker logs in real time | -00115| -00116| ```bash -00117| # Deploy everything -00118| cloudflare:deploy -00119| -00120| # Deploy just the API -00121| cloudflare:deploy:api -00122| -00123| # Start local dev with Wrangler -00124| cloudflare:dev -00125| -00126| # Tail production logs -00127| cloudflare:tail -00128| ``` -00129| -00130| ## CI Integration -00131| -00132| When you have apps configured for Cloudflare deployment and [CI generation](/docs/apps-ci/ci-generation) is enabled, the generated GitHub Actions workflow includes Cloudflare deploy steps: -00133| -00134| ```nix -00135| { -00136| stack.deployment = { -00137| cloudflare = { -00138| accountId = "your-account-id"; -00139| }; -00140| }; -00141| -00142| stack.apps.api = { -00143| port = 1; -00144| deployment = { -00145| enable = true; -00146| host = "cloudflare"; -00147| cloudflare.workerName = "myapp-api"; -00148| }; -00149| }; -00150| -00151| stack.ci = { -00152| enable = true; -00153| secrets = { -00154| "CLOUDFLARE_API_TOKEN" = "CF_API_TOKEN"; # GitHub secret name -00155| }; -00156| }; -00157| } -00158| ``` -00159| -00160| The generated workflow uses `wrangler deploy` with proper authentication via the `CLOUDFLARE_API_TOKEN` GitHub Actions secret. -00161| -00162| -00163| You'll need to create a Cloudflare API token with the appropriate permissions and add it as a GitHub Actions secret. See the [Cloudflare docs](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) for token creation. -00164| -00165| -00166| ## Workers with Bindings -00167| -00168| Cloudflare Workers support various bindings—KV namespaces, R2 buckets, D1 databases, Durable Objects, and more. Configure them through the module: -00169| -00170| ```nix -00171| stack.apps.api = { -00172| port = 1; -00173| -00174| deployment = { -00175| enable = true; -00176| host = "cloudflare"; -00177| -00178| cloudflare = { -00179| workerName = "myapp-api"; -00180| kvNamespaces = [ "CACHE" ]; -00181| r2Buckets = [ "UPLOADS" ]; -00182| d1Databases = [ "DB" ]; -00183| bindings = { -00184| CACHE = "abc123"; -00185| DB = "def456"; -00186| }; -00187| }; -00188| }; -00189| }; -00190| ``` -00191| -00192| These bindings are written into the generated `wrangler.toml` and available as typed bindings in your Worker code. -00193| -00194| ## Pages Deployment -00195| -00196| For static sites or frameworks with static export (like Next.js static export or Astro), use Cloudflare Pages: -00197| -00198| ```nix -00199| stack.apps.docs = { -00200| port = 2; -00201| -00202| deployment = { -00203| enable = true; -00204| host = "cloudflare"; -00205| -00206| cloudflare = { -00207| workerName = "myapp-docs"; -00208| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00209| }; -00210| }; -00211| }; -00212| ``` -00213| -00214| Pages deployments use `wrangler pages deploy` under the hood. -00215| -00216| ## Local Development with Wrangler -00217| -00218| The Cloudflare module adds `wrangler` to your devshell, so you can use Wrangler's local development features alongside Stack's dev environment: -00219| -00220| ```bash -00221| # Start Wrangler dev server (uses miniflare locally) -00222| wrangler dev --config apps/api/wrangler.toml -00223| -00224| # Or use the registered script -00225| cloudflare:dev -00226| ``` -00227| -00228| Wrangler's dev mode emulates the Workers runtime locally, including KV, R2, D1, and other bindings. This complements Stack's [process-compose](/docs/services/process-compose) setup—you can run Wrangler alongside your other local services. -00229| -00230| ## Combining with Other Deployment Targets -00231| -00232| Not every app in your project needs to deploy to Cloudflare. You can mix deployment targets: -00233| -00234| ```nix -00235| { -00236| stack.apps = { -00237| web = { -00238| port = 0; -00239| framework = "nextjs"; -00240| deployment = { -00241| enable = true; -00242| host = "fly"; -00243| fly.appName = "myapp-web"; -00244| }; -00245| }; -00246| -00247| api = { -00248| port = 1; -00249| framework = "hono"; -00250| deployment = { -00251| enable = true; -00252| host = "cloudflare"; -00253| cloudflare.workerName = "myapp-api"; -00254| }; -00255| }; -00256| -00257| docs = { -00258| port = 2; -00259| framework = "nextjs"; -00260| deployment = { -00261| enable = true; -00262| host = "cloudflare"; -00263| cloudflare = { -00264| workerName = "myapp-docs"; -00265| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00266| }; -00267| }; -00268| }; -00269| }; -00270| } -00271| ``` -00272| -00273| Each deployment target generates its own config files and scripts independently. -00274| -00275| ## Troubleshooting -00276| -00277| ### "Authentication error" during deploy -00278| -00279| Make sure your Cloudflare API token is set. For local deploys: -00280| -00281| ```bash -00282| export CLOUDFLARE_API_TOKEN="your-token" -00283| cloudflare:deploy -00284| ``` -00285| -00286| Or log in interactively: -00287| -00288| ```bash -00289| wrangler login -00290| ``` -00291| -00292| ### "Worker not found" errors -00293| -00294| Verify the Worker name matches what's in your Cloudflare dashboard. Workers are created on first deploy if they don't exist, but the account ID must be correct. -00295| -00296| ### Generated `wrangler.toml` not updating -00297| -00298| If your Stack config changed but the generated file hasn't, trigger a rebuild: -00299| -00300| ```bash -00301| # The agent watches for changes, but you can force a rebuild -00302| stack status -00303| ``` -00304| -00305| Or check the file status in [Studio](/docs/studio) to see if it's marked as stale. -00306| -00307| ## Reference -00308| -00309| - [Options Reference → CI](/docs/reference/ci) for CI configuration that includes Cloudflare deploys -00310| - [Apps & CI → CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00311| - [Fly.io](./fly) for an alternative deployment target -00312| - [SST](./sst) for AWS infrastructure provisioning -00313| - [Cloudflare Workers docs](https://developers.cloudflare.com/workers/) for Workers-specific configuration - -(End of file - total 313 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: SST -00003| description: AWS infrastructure provisioning with SST and OIDC-based authentication -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| [SST](https://sst.dev/) is an infrastructure-as-code framework for deploying to AWS. Stack's SST extension generates configuration, manages IAM roles with OIDC authentication, and provisions KMS keys for secrets encryption—all from your Nix config. -00010| -00011| ## Enable SST -00012| -00013| ```nix -00014| stack.sst = { -00015| enable = true; -00016| project-name = "myapp"; -00017| region = "us-west-2"; -00018| }; -00019| ``` -00020| -00021| When enabled, the SST extension: -00022| -00023| - Adds the SST CLI to your `PATH` -00024| - Generates an `sst.config.ts` from your Nix configuration -00025| - Registers `sst:deploy`, `sst:dev`, and other scripts in your devshell -00026| - Optionally provisions KMS keys and IAM roles for secure deployments -00027| -00028| ## Configuration -00029| -00030| ### Basic Setup -00031| -00032| ```nix -00033| stack.sst = { -00034| enable = true; -00035| project-name = "myapp"; -00036| region = "us-west-2"; -00037| }; -00038| ``` -00039| -00040| This generates a minimal `sst.config.ts` that you can extend with your own infrastructure definitions. -00041| -00042| ### KMS for Secrets Encryption -00043| -00044| SST can provision a KMS key for encrypting secrets at rest: -00045| -00046| ```nix -00047| stack.sst = { -00048| enable = true; -00049| project-name = "myapp"; -00050| region = "us-west-2"; -00051| kms.enable = true; -00052| }; -00053| ``` -00054| -00055| The KMS key integrates with Stack's [secrets system](/docs/secrets)—secrets encrypted with this key can be decrypted in your deployed application using the IAM role's KMS permissions. -00056| -00057| ### OIDC Authentication -00058| -00059| Instead of long-lived AWS credentials, the SST extension supports OIDC-based authentication. This gives you short-lived, automatically rotated credentials for CI/CD and local development. -00060| -00061| #### GitHub Actions OIDC -00062| -00063| ```nix -00064| stack.sst = { -00065| enable = true; -00066| project-name = "myapp"; -00067| region = "us-west-2"; -00068| -00069| oidc = { -00070| provider = "github-actions"; -00071| github-actions = { -00072| org = "my-org"; -00073| repo = "my-repo"; -00074| }; -00075| }; -00076| }; -00077| ``` -00078| -00079| This provisions: -00080| -00081| - An IAM OIDC identity provider for GitHub Actions -00082| - An IAM role that GitHub Actions can assume via OIDC -00083| - Trust policies scoped to your specific org and repo -00084| -00085| Your [CI workflows](/docs/apps-ci/ci-generation) automatically get the correct role ARN and OIDC configuration—no manual secret management. -00086| -00087| #### Fly.io OIDC -00088| -00089| ```nix -00090| stack.sst = { -00091| enable = true; -00092| project-name = "myapp"; -00093| region = "us-west-2"; -00094| -00095| oidc = { -00096| provider = "fly"; -00097| fly = { -00098| org = "my-fly-org"; -00099| }; -00100| }; -00101| }; -00102| ``` -00103| -00104| #### AWS Roles Anywhere -00105| -00106| For environments that don't support OIDC natively, use [AWS Roles Anywhere](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html) with mTLS certificates from [Step CA](/docs/networking/certificates): -00107| -00108| ```nix -00109| stack.sst = { -00110| enable = true; -00111| project-name = "myapp"; -00112| region = "us-west-2"; -00113| -00114| oidc = { -00115| provider = "roles-anywhere"; -00116| }; -00117| }; -00118| -00119| stack.aws.roles-anywhere = { -00120| enable = true; -00121| region = "us-west-2"; -00122| account-id = "123456789012"; -00123| role-name = "dev-role"; -00124| trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:trust-anchor/abc123"; -00125| profile-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:profile/def456"; -00126| }; -00127| ``` -00128| -00129| This provisions certificate-based AWS authentication through your team's [Step CA](/docs/networking/certificates) instance, giving every developer short-lived AWS credentials without any shared secrets. -00130| -00131| -00132| Roles Anywhere is the most secure option for local development—each developer gets their own short-lived certificate, and all access is logged in CloudTrail. It requires a [Step CA](/docs/networking/certificates) instance but provides the strongest security guarantees. -00133| -00134| -00135| ## Generated Files -00136| -00137| The SST extension generates: -00138| -00139| | File | Purpose | -00140| |------|---------| -00141| | `sst.config.ts` | SST project configuration with app name, region, and settings | -00142| -00143| Like all Stack-generated files, `sst.config.ts` is committed to Git and works without Nix. If you eject from Stack, you have a standard SST project. -00144| -00145| -00146| Don't edit `sst.config.ts` by hand—your changes will be overwritten on the next build. Add custom infrastructure in separate files that SST imports, or configure everything through the Nix options. -00147| -00148| -00149| ## Scripts -00150| -00151| When SST is enabled, the following scripts are available in your devshell: -00152| -00153| | Script | Description | -00154| |--------|-------------| -00155| | `sst:dev` | Start SST in development mode with live Lambda reloading | -00156| | `sst:deploy` | Deploy infrastructure to AWS | -00157| | `sst:remove` | Remove all deployed resources | -00158| -00159| These scripts handle credential injection automatically—whether you're using OIDC, Roles Anywhere, or environment-based credentials. -00160| -00161| ## Using with CI -00162| -00163| The SST extension integrates with [CI generation](/docs/apps-ci/ci-generation). When both are enabled, the generated GitHub Actions workflow includes: -00164| -00165| - AWS OIDC credential setup (if GitHub Actions OIDC is configured) -00166| - SST deploy steps with the correct region and project name -00167| - Environment variable injection for secrets -00168| -00169| ```nix -00170| { -00171| stack.sst = { -00172| enable = true; -00173| project-name = "myapp"; -00174| region = "us-west-2"; -00175| oidc.provider = "github-actions"; -00176| oidc.github-actions = { -00177| org = "my-org"; -00178| repo = "my-repo"; -00179| }; -00180| }; -00181| -00182| stack.ci.enable = true; -00183| } -00184| ``` -00185| -00186| This generates a CI pipeline that authenticates to AWS via OIDC and deploys your SST infrastructure—no long-lived AWS credentials stored in GitHub Secrets. -00187| -00188| ## Full Example -00189| -00190| A complete SST setup with KMS, OIDC, and secrets: -00191| -00192| ```nix -00193| { -00194| # SST infrastructure -00195| stack.sst = { -00196| enable = true; -00197| project-name = "myapp"; -00198| region = "us-west-2"; -00199| -00200| # KMS key for secrets encryption -00201| kms.enable = true; -00202| -00203| # GitHub Actions OIDC for CI deployments -00204| oidc = { -00205| provider = "github-actions"; -00206| github-actions = { -00207| org = "my-org"; -00208| repo = "my-repo"; -00209| }; -00210| }; -00211| }; -00212| -00213| # Local AWS access via Roles Anywhere -00214| stack.aws.roles-anywhere = { -00215| enable = true; -00216| region = "us-west-2"; -00217| account-id = "123456789012"; -00218| role-name = "dev-role"; -00219| trust-anchor-arn = "arn:aws:rolesanywhere:..."; -00220| profile-arn = "arn:aws:rolesanywhere:..."; -00221| }; -00222| -00223| # Secrets encrypted with KMS -00224| stack.variables."/prod/api-key" = { -00225| key = "API_KEY"; -00226| type = "SECRET"; -00227| master-keys = [ "prod" ]; -00228| }; -00229| -00230| # CI pipeline with SST deploy -00231| stack.ci.enable = true; -00232| } -00233| ``` -00234| -00235| ## Troubleshooting -00236| -00237| ### "Unable to assume role" in CI -00238| -00239| Verify your OIDC configuration matches your GitHub repository: -00240| -00241| ```nix -00242| oidc.github-actions = { -00243| org = "my-org"; # Must match your GitHub org exactly -00244| repo = "my-repo"; # Must match your GitHub repo name exactly -00245| }; -00246| ``` -00247| -00248| Also confirm the IAM OIDC provider and role have been provisioned. The SST extension creates these resources on first deploy—you may need to run `sst deploy` locally with admin credentials once to bootstrap. -00249| -00250| ### "KMS key not found" -00251| -00252| If KMS is enabled but not yet provisioned, run the initial deployment: -00253| -00254| ```bash -00255| sst:deploy -00256| ``` -00257| -00258| The KMS key is created as part of the SST infrastructure stack. After the first deploy, it's available for encrypting secrets. -00259| -00260| ### SST dev mode not connecting -00261| -00262| Ensure your AWS credentials are working: -00263| -00264| ```bash -00265| aws sts get-caller-identity -00266| ``` -00267| -00268| If using Roles Anywhere, verify your [Step CA](/docs/networking/certificates) certificate is valid and the trust anchor is configured correctly. -00269| -00270| ## Reference -00271| -00272| - [Options Reference → AWS](/docs/reference/aws) for AWS Roles Anywhere configuration -00273| - [Secrets](/docs/secrets) for encrypted secrets management -00274| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00275| - [Certificates](/docs/networking/certificates) for Step CA and Roles Anywhere -00276| - [SST documentation](https://sst.dev/docs) for SST-specific configuration - -(End of file - total 276 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers -00003| description: Build OCI containers from your Nix configuration -00004| icon: box -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can build OCI-compatible container images directly from your Nix configuration—no Dockerfile required. Images are built with [nix2container](https://github.com/nlewo/nix2container), producing minimal, reproducible containers with only the dependencies your application actually needs. -00010| -00011| ## Why Nix-Built Containers? -00012| -00013| Traditional Dockerfiles are imperative scripts that install packages, copy files, and run commands. The result depends on the base image version, network availability during build, and the order of operations. Two builds of the same Dockerfile can produce different images. -00014| -00015| Nix-built containers are **declarative and reproducible**. You describe what goes in the image, and Nix computes the exact result. The same input always produces the same image, byte-for-byte. -00016| -00017| Additional benefits: -00018| -00019| - **Minimal images** — Only your application and its runtime dependencies. No package manager, no shell, no leftover build tools. -00020| - **Layer caching** — Nix automatically splits your image into layers based on the dependency graph. Dependencies that don't change produce identical layers, giving you efficient caching without manual multi-stage builds. -00021| - **No base image** — Images are built from scratch. No inherited vulnerabilities from a base image you don't control. -00022| - **Shared with your devshell** — The same Nix packages used in your dev environment end up in your container. No "works locally but not in Docker" surprises. -00023| -00024| ## Enable Containers -00025| -00026| Container definitions live under `stack.containers`: -00027| -00028| ```nix -00029| stack.containers.api = { -00030| enable = true; -00031| name = "myapp-api"; -00032| tag = "latest"; -00033| -00034| # What goes in the image -00035| config = { -00036| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00037| env = { -00038| NODE_ENV = "production"; -00039| }; -00040| exposedPorts = { -00041| "4201/tcp" = {}; -00042| }; -00043| }; -00044| -00045| # Files to include -00046| copyToRoot = [ -00047| ./apps/api/dist -00048| ./apps/api/package.json -00049| ]; -00050| }; -00051| ``` -00052| -00053| ## Building Images -00054| -00055| Build a container image from the command line: -00056| -00057| ```bash -00058| # Build a specific container -00059| nix build --impure .#packages.x86_64-linux.container-api -00060| -00061| # Load it into Docker -00062| docker load < result -00063| -00064| # Or build and load in one step -00065| nix build --impure .#packages.x86_64-linux.container-api && docker load < result -00066| ``` -00067| -00068| The output is a standard OCI image that works with Docker, Podman, containerd, or any OCI-compatible runtime. -00069| -00070| ## Per-App Containers -00071| -00072| When you define [apps](/docs/apps-ci/defining-apps), you can attach container definitions directly: -00073| -00074| ```nix -00075| stack.apps.api = { -00076| port = 1; -00077| root = "./apps/api"; -00078| build = "bun run build"; -00079| }; -00080| -00081| stack.containers.api = { -00082| enable = true; -00083| name = "ghcr.io/my-org/myapp-api"; -00084| -00085| config = { -00086| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00087| env = { -00088| NODE_ENV = "production"; -00089| PORT = "4201"; -00090| }; -00091| exposedPorts = { -00092| "4201/tcp" = {}; -00093| }; -00094| }; -00095| }; -00096| ``` -00097| -00098| ## Multi-Stage Patterns -00099| -00100| Nix replaces the need for multi-stage Docker builds. Instead of copying between build and runtime stages, you define separate derivations: -00101| -00102| ```nix -00103| { pkgs, ... }: -00104| let -00105| # Build stage — produces the compiled output -00106| apiBundle = pkgs.stdenv.mkDerivation { -00107| name = "api-bundle"; -00108| src = ./apps/api; -00109| buildInputs = [ pkgs.bun ]; -00110| buildPhase = '' -00111| bun install --frozen-lockfile -00112| bun run build -00113| ''; -00114| installPhase = '' -00115| mkdir -p $out -00116| cp -r dist $out/ -00117| cp package.json $out/ -00118| ''; -00119| }; -00120| in -00121| { -00122| stack.containers.api = { -00123| enable = true; -00124| name = "ghcr.io/my-org/myapp-api"; -00125| -00126| # Only the built output and runtime go in the image -00127| # Build tools (bun, node headers, etc.) are NOT included -00128| copyToRoot = [ apiBundle ]; -00129| -00130| config = { -00131| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00132| workingDir = "/"; -00133| env.NODE_ENV = "production"; -00134| }; -00135| }; -00136| } -00137| ``` -00138| -00139| The resulting image contains only Node.js and your compiled code—no build tools, no source files, no `node_modules` with devDependencies. -00140| -00141| ## Image Layers -00142| -00143| nix2container automatically creates efficient image layers based on the Nix store dependency graph. Each Nix store path becomes a potential layer boundary, so: -00144| -00145| - **Runtime dependencies** (Node.js, system libraries) rarely change → cached layers -00146| - **Application code** changes frequently → small top layer -00147| - **No manual layer optimization** — Nix computes the optimal layer split -00148| -00149| This gives you Docker-like layer caching without writing `COPY package.json` tricks or managing multi-stage builds. -00150| -00151| ## Registry Push -00152| -00153| Push built images to a container registry: -00154| -00155| ```bash -00156| # Build the container -00157| nix build --impure .#packages.x86_64-linux.container-api -00158| docker load < result -00159| -00160| # Push to registry -00161| docker tag myapp-api:latest ghcr.io/my-org/myapp-api:latest -00162| docker push ghcr.io/my-org/myapp-api:latest -00163| -00164| # Or use skopeo for direct copy -00165| nix run nixpkgs#skopeo -- copy \ -00166| docker-archive:./result \ -00167| docker://ghcr.io/my-org/myapp-api:latest -00168| ``` -00169| -00170| For CI, the [CI generation](/docs/apps-ci/ci-generation) system can produce Docker build and push steps in your GitHub Actions workflow automatically. -00171| -00172| ## Configuration Reference -00173| -00174| Each container supports these options: -00175| -00176| | Option | Type | Description | -00177| |--------|------|-------------| -00178| | `enable` | `bool` | Whether to build this container | -00179| | `name` | `string` | Image name (including registry prefix) | -00180| | `tag` | `string` | Image tag (default: `"latest"`) | -00181| | `config.entrypoint` | `list` | Container entrypoint command | -00182| | `config.cmd` | `list` | Default command arguments | -00183| | `config.env` | `attrset` | Environment variables baked into the image | -00184| | `config.exposedPorts` | `attrset` | Ports to expose | -00185| | `config.workingDir` | `string` | Working directory inside the container | -00186| | `config.user` | `string` | User to run as | -00187| | `copyToRoot` | `list` | Files and derivations to include in the image | -00188| | `layers` | `list` | Explicit layer definitions (advanced) | -00189| | `maxLayers` | `int` | Maximum number of layers (default: 127) | -00190| -00191| ## Comparing to Dockerfiles -00192| -00193| If you're already using Dockerfiles, you can continue to do so. Stack's container system is an alternative, not a replacement. Here's when each makes sense: -00194| -00195| | Use Nix containers when... | Use Dockerfiles when... | -00196| |---------------------------|------------------------| -00197| | You want reproducible builds | You need specific base images (e.g., CUDA) | -00198| | You want minimal image size | Your team is more comfortable with Docker | -00199| | You're already using Nix for your devshell | You need Docker-specific features (BuildKit secrets, etc.) | -00200| | You want shared dependencies with your dev environment | You're deploying to a platform that expects Dockerfiles | -00201| -00202| -00203| Stack can also generate Dockerfiles through the [Docker module](/docs/deployment/index), giving you a middle ground: Nix-generated Dockerfiles that are standard files on disk. -00204| -00205| -00206| ## Troubleshooting -00207| -00208| ### Image is larger than expected -00209| -00210| Check what's being pulled in as dependencies: -00211| -00212| ```bash -00213| nix path-info -rsSh .#packages.x86_64-linux.container-api -00214| ``` -00215| -00216| This shows every store path in the image closure and its size. Look for unexpected dependencies—a common issue is accidentally including build-time tools in the runtime closure. -00217| -00218| ### "No such file or directory" at runtime -00219| -00220| The container's filesystem only contains what you explicitly include via `copyToRoot` and what Nix determines as runtime dependencies. If a file is missing, add it to `copyToRoot` or ensure the derivation that produces it is in the dependency graph. -00221| -00222| ### Can't run shell commands in the container -00223| -00224| By default, Nix-built containers don't include a shell (`bash`, `sh`). If you need one for debugging: -00225| -00226| ```nix -00227| copyToRoot = [ -00228| apiBundle -00229| pkgs.bashInteractive # Adds bash for debugging -00230| pkgs.coreutils # Adds ls, cat, etc. -00231| ]; -00232| ``` -00233| -00234| Remove these before deploying to production to keep the image minimal. -00235| -00236| ## Reference -00237| -00238| - [Deployment Overview](/docs/deployment) for other deployment options -00239| - [Apps → Defining Apps](/docs/apps-ci/defining-apps) for the app definitions containers can reference -00240| - [CI Generation](/docs/apps-ci/ci-generation) for automated container builds in CI -00241| - [nix2container](https://github.com/nlewo/nix2container) for the underlying container builder - -(End of file - total 241 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json" -} -``` -**Output:** -``` - -00001| { -00002| "title": "Containers & Deployment", -00003| "description": "Build containers and deploy to production", -00004| "icon": "cloud-upload", -00005| "pages": ["index", "containers", "cloudflare", "fly", "sst"] -00006| } -00007| - -(End of file - total 7 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 5.7s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/machines/module.nix -00003| # -00004| # Machine inventory infra module. -00005| # -00006| # Accepts a machine inventory definition in Nix and emits a JSON string -00007| # output that can be stored via the infra output backend. Colmena consumes -00008| # this inventory from stack.infra.outputs.machines. -00009| # ============================================================================== -00010| { -00011| lib, -00012| config, -00013| ... -00014| }: -00015| let -00016| cfg = config.stack.infra.machines; -00017| -00018| sshConfigType = lib.types.submodule { -00019| options = { -00020| user = lib.mkOption { -00021| type = lib.types.str; -00022| default = "root"; -00023| description = "SSH user for connecting to the machine."; -00024| }; -00025| -00026| port = lib.mkOption { -00027| type = lib.types.int; -00028| default = 22; -00029| description = "SSH port for connecting to the machine."; -00030| }; -00031| -00032| keyPath = lib.mkOption { -00033| type = lib.types.nullOr lib.types.str; -00034| default = null; -00035| description = "Path to the SSH private key for this machine."; -00036| }; -00037| }; -00038| }; -00039| -00040| awsFilterType = lib.types.submodule { -00041| options = { -00042| name = lib.mkOption { -00043| type = lib.types.str; -00044| description = "EC2 filter name (e.g., instance-state-name, tag:Name)."; -00045| }; -00046| -00047| values = lib.mkOption { -00048| type = lib.types.listOf lib.types.str; -00049| default = [ ]; -00050| description = "Values for the EC2 filter."; -00051| }; -00052| }; -00053| }; -00054| -00055| machineType = lib.types.submodule { -00056| options = { -00057| id = lib.mkOption { -00058| type = lib.types.nullOr lib.types.str; -00059| default = null; -00060| description = "Optional machine identifier (defaults to the attrset key)."; -00061| }; -00062| -00063| name = lib.mkOption { -00064| type = lib.types.nullOr lib.types.str; -00065| default = null; -00066| description = "Human-friendly machine name."; -00067| }; -00068| -00069| host = lib.mkOption { -00070| type = lib.types.nullOr lib.types.str; -00071| default = null; -00072| description = "SSH host or hostname for the machine."; -00073| }; -00074| -00075| ssh = lib.mkOption { -00076| type = sshConfigType; -00077| default = { }; -00078| description = "SSH connection settings for the machine."; -00079| }; -00080| -00081| tags = lib.mkOption { -00082| type = lib.types.listOf lib.types.str; -00083| default = [ ]; -00084| description = "Tags used for grouping and target selection."; -00085| }; -00086| -00087| roles = lib.mkOption { -00088| type = lib.types.listOf lib.types.str; -00089| default = [ ]; -00090| description = "Roles associated with this machine."; -00091| }; -00092| -00093| provider = lib.mkOption { -00094| type = lib.types.nullOr lib.types.str; -00095| default = null; -00096| description = "Infrastructure provider name (aws, gcp, hetzner, etc.)."; -00097| }; -00098| -00099| arch = lib.mkOption { -00100| type = lib.types.nullOr lib.types.str; -00101| default = null; -00102| description = "Target system architecture (e.g., x86_64-linux)."; -00103| }; -00104| -00105| publicIp = lib.mkOption { -00106| type = lib.types.nullOr lib.types.str; -00107| default = null; -00108| description = "Public IPv4/IPv6 address for the machine."; -00109| }; -00110| -00111| privateIp = lib.mkOption { -00112| type = lib.types.nullOr lib.types.str; -00113| default = null; -00114| description = "Private IPv4/IPv6 address for the machine."; -00115| }; -00116| -00117| labels = lib.mkOption { -00118| type = lib.types.attrsOf lib.types.str; -00119| default = { }; -00120| description = "Arbitrary labels attached to the machine."; -00121| }; -00122| -00123| nixosProfile = lib.mkOption { -00124| type = lib.types.nullOr lib.types.str; -00125| default = null; -00126| description = "NixOS profile name to deploy on this machine."; -00127| }; -00128| -00129| nixosModules = lib.mkOption { -00130| type = lib.types.listOf lib.types.str; -00131| default = [ ]; -00132| description = "Extra NixOS modules to include for this machine."; -00133| }; -00134| -00135| targetEnv = lib.mkOption { -00136| type = lib.types.nullOr lib.types.str; -00137| default = null; -00138| description = "Deployment environment label for this machine."; -00139| }; -00140| -00141| env = lib.mkOption { -00142| type = lib.types.attrsOf lib.types.str; -00143| default = { }; -00144| description = "Environment variables applied to this machine."; -00145| }; -00146| -00147| metadata = lib.mkOption { -00148| type = lib.types.attrsOf lib.types.anything; -00149| default = { }; -00150| description = "Extra metadata for downstream tooling."; -00151| }; -00152| }; -00153| }; -00154| in -00155| { -00156| options.stack.infra.machines = { -00157| enable = lib.mkOption { -00158| type = lib.types.bool; -00159| default = false; -00160| description = "Enable machine inventory provisioning via infra."; -00161| }; -00162| -00163| source = lib.mkOption { -00164| type = lib.types.enum [ -00165| "static" -00166| "aws-ec2" -00167| ]; -00168| default = "static"; -00169| description = "Machine inventory source (static or AWS EC2)."; -00170| }; -00171| -00172| aws = { -00173| region = lib.mkOption { -00174| type = lib.types.nullOr lib.types.str; -00175| default = config.stack.aws.roles-anywhere.region or null; -00176| description = "AWS region for EC2 inventory (falls back to AWS env defaults)."; -00177| }; -00178| -00179| instance-ids = lib.mkOption { -00180| type = lib.types.listOf lib.types.str; -00181| default = [ ]; -00182| description = "Explicit EC2 instance IDs to include in inventory."; -00183| }; -00184| -00185| filters = lib.mkOption { -00186| type = lib.types.listOf awsFilterType; -00187| default = [ -00188| { -00189| name = "instance-state-name"; -00190| values = [ "running" ]; -00191| } -00192| ]; -00193| description = "EC2 filters for inventory discovery."; -00194| }; -00195| -00196| name-tag-keys = lib.mkOption { -00197| type = lib.types.listOf lib.types.str; -00198| default = [ "Name" ]; -00199| description = "Tag keys used to derive machine names."; -00200| }; -00201| -00202| role-tag-keys = lib.mkOption { -00203| type = lib.types.listOf lib.types.str; -00204| default = [ -00205| "stack:role" -00206| "role" -00207| ]; -00208| description = "Tag keys used to derive machine roles."; -00209| }; -00210| -00211| tag-keys = lib.mkOption { -00212| type = lib.types.listOf lib.types.str; -00213| default = [ -00214| "stack:tag" -00215| "tag" -00216| ]; -00217| description = "Tag keys used to derive machine tags."; -00218| }; -00219| -00220| env-tag-keys = lib.mkOption { -00221| type = lib.types.listOf lib.types.str; -00222| default = [ -00223| "stack:env" -00224| "env" -00225| "stage" -00226| ]; -00227| description = "Tag keys used to derive machine target environments."; -00228| }; -00229| -00230| host-preference = lib.mkOption { -00231| type = lib.types.listOf (lib.types.enum [ -00232| "publicDns" -00233| "publicIp" -00234| "privateIp" -00235| ]); -00236| default = [ -00237| "publicDns" -00238| "publicIp" -00239| "privateIp" -00240| ]; -00241| description = "Preferred host fields for connecting to EC2 machines."; -00242| }; -00243| -00244| ssh = lib.mkOption { -00245| type = sshConfigType; -00246| default = { }; -00247| description = "Default SSH settings for EC2 machines."; -00248| }; -00249| }; -00250| -00251| machines = lib.mkOption { -00252| type = lib.types.attrsOf machineType; -00253| default = { }; -00254| description = "Machine inventory definitions to emit via infra outputs."; -00255| }; -00256| -00257| sync-outputs = lib.mkOption { -00258| type = lib.types.listOf lib.types.str; -00259| default = [ "machines" ]; -00260| description = "Which outputs to sync to the storage backend."; -00261| }; -00262| }; -00263| -00264| config = lib.mkIf cfg.enable { -00265| stack.infra.enable = lib.mkDefault true; -00266| -00267| stack.infra.modules.machines = { -00268| name = "Machine Inventory"; -00269| description = "Machine inventory provider for Colmena deployments"; -00270| path = ./index.ts; -00271| inputs = { -00272| source = cfg.source; -00273| machines = cfg.machines; -00274| aws = { -00275| region = cfg.aws.region; -00276| instanceIds = cfg.aws.instance-ids; -00277| filters = cfg.aws.filters; -00278| nameTagKeys = cfg.aws.name-tag-keys; -00279| roleTagKeys = cfg.aws.role-tag-keys; -00280| tagKeys = cfg.aws.tag-keys; -00281| envTagKeys = cfg.aws.env-tag-keys; -00282| hostPreference = cfg.aws.host-preference; -00283| ssh = { -00284| user = cfg.aws.ssh.user; -00285| port = cfg.aws.ssh.port; -00286| keyPath = cfg.aws.ssh.keyPath; -00287| }; -00288| }; -00289| }; -00290| dependencies = lib.optionalAttrs (cfg.source == "aws-ec2") { -00291| "@aws-sdk/client-ec2" = "catalog:"; -00292| }; -00293| outputs = -00294| let -00295| mkOutput = key: desc: { -00296| description = desc; -00297| sensitive = false; -00298| sync = builtins.elem key cfg.sync-outputs; -00299| }; -00300| in -00301| { -00302| machines = mkOutput "machines" "Machine inventory (JSON)"; -00303| }; -00304| }; -00305| }; -00306| } -00307| - -(End of file - total 307 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Data comes from the agent via colmena-machines.json -00008| * and colmena-app-deploy.json state files. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, -00017| CardDescription, -00018| CardHeader, -00019| CardTitle, -00020| } from "@ui/card"; -00021| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs"; -00022| import { -00023| Activity, -00024| AlertCircle, -00025| CheckCircle, -00026| CloudOff, -00027| Cpu, -00028| HardDrive, -00029| Loader2, -00030| Network, -00031| Play, -00032| RefreshCw, -00033| Rocket, -00034| Server, -00035| Settings, -00036| Shield, -00037| XCircle, -00038| } from "lucide-react"; -00039| import { useAgentContext } from "@/lib/agent-provider"; -00040| import { useNixConfig } from "@/lib/use-agent"; -00041| import { PanelHeader } from "../shared/panel-header"; -00042| import { cn } from "@/lib/utils"; -00043| -00044| // ============================================================================= -00045| // Types -00046| // ============================================================================= -00047| -00048| interface MachineInfo { -00049| id: string; -00050| name: string; -00051| host: string | null; -00052| ssh: { -00053| user: string; -00054| port: number; -00055| keyPath: string | null; -00056| }; -00057| tags: string[]; -00058| roles: string[]; -00059| provider: string | null; -00060| arch: string | null; -00061| publicIp: string | null; -00062| privateIp: string | null; -00063| targetEnv: string | null; -00064| labels: Record; -00065| } -00066| -00067| interface AppDeployMapping { -00068| enable: boolean; -00069| targets: string[]; -00070| resolvedTargets: string[]; -00071| role: string | null; -00072| nixosModules: string[]; -00073| system: string | null; -00074| } -00075| -00076| interface ColmenaConfig { -00077| enable: boolean; -00078| machineSource: string; -00079| generateHive: boolean; -00080| config: string; -00081| machineCount: number; -00082| machineIds: string[]; -00083| } -00084| -00085| // ============================================================================= -00086| // Hooks -00087| // ============================================================================= -00088| -00089| function useColmenaData() { -00090| const { data: nixConfig, isLoading, refetch } = useNixConfig(); -00091| -00092| const result = useMemo(() => { -00093| const cfg = nixConfig as Record | null | undefined; -00094| if (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null }; -00095| -00096| const serializable = cfg.serializable as Record | undefined; -00097| const colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null; -00098| -00099| // Try to get machines from colmena serialized data or panels -00100| const colmenaData = cfg.colmena as Record | undefined; -00101| const machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record; -00102| -00103| // Apps with deploy config -00104| const rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>; -00105| const appDeploy: Record = {}; -00106| -00107| for (const [appName, appCfg] of Object.entries(rawApps)) { -00108| const deploy = appCfg.deploy as Record | undefined; -00109| if (deploy?.enable) { -00110| appDeploy[appName] = { -00111| enable: true, -00112| targets: (deploy.targets as string[]) ?? [], -00113| resolvedTargets: (deploy.resolvedTargets as string[]) ?? [], -00114| role: (deploy.role as string | null) ?? null, -00115| nixosModules: (deploy.nixosModules as string[]) ?? [], -00116| system: (deploy.system as string | null) ?? null, -00117| }; -00118| } -00119| } -00120| -00121| return { machines: machinesComputed, appDeploy, colmenaConfig }; -00122| }, [nixConfig]); -00123| -00124| return { ...result, isLoading, refetch }; -00125| } -00126| -00127| // ============================================================================= -00128| // Sub-components -00129| // ============================================================================= -00130| -00131| function MachineCard({ machine }: { machine: MachineInfo }) { -00132| const isReachable = machine.host !== null; -00133| -00134| return ( -00135| -00139| -00140|
-00141|
-00142| -00143| {machine.name} -00144|
-00145|
-00146| {isReachable ? ( -00147| -00148| -00149| {machine.host} -00150| -00151| ) : ( -00152| -00153| -00154| no host -00155| -00156| )} -00157|
-00158|
-00159| -00160|
-00161|
-00162| -00163| {machine.ssh.user}@{machine.ssh.port} -00164|
-00165| {machine.arch && ( -00166|
-00167| -00168| {machine.arch} -00169|
-00170| )} -00171| {machine.provider && ( -00172|
-00173| -00174| {machine.provider} -00175|
-00176| )} -00177| {machine.targetEnv && ( -00178|
-00179| -00180| {machine.targetEnv} -00181|
-00182| )} -00183|
-00184| -00185| {(machine.tags.length > 0 || machine.roles.length > 0) && ( -00186|
-00187| {machine.roles.map((role) => ( -00188| -00189| {role} -00190| -00191| ))} -00192| {machine.tags.map((tag) => ( -00193| -00194| {tag} -00195| -00196| ))} -00197|
-00198| )} -00199|
-00200|
-00201| ); -00202| } -00203| -00204| function AppTargetRow({ -00205| appName, -00206| deploy, -00207| machines, -00208| }: { -00209| appName: string; -00210| deploy: AppDeployMapping; -00211| machines: Record; -00212| }) { -00213| const resolvedNames = deploy.resolvedTargets.map( -00214| (id) => machines[id]?.name ?? id, -00215| ); -00216| -00217| return ( -00218|
-00219|
-00220|
-00221| -00222|
-00223|
-00224|

{appName}

-00225|

-00226| {deploy.targets.length > 0 -00227| ? `Targets: ${deploy.targets.join(", ")}` -00228| : "No targets defined"} -00229|

-00230|
-00231|
-00232|
-00233| {deploy.role && ( -00234| -00235| {deploy.role} -00236| -00237| )} -00238| -00239| {resolvedNames.length} machine{resolvedNames.length !== 1 ? "s" : ""} -00240| -00241|
-00242|
-00243| ); -00244| } -00245| -00246| // ============================================================================= -00247| // Main Component -00248| // ============================================================================= -00249| -00250| export function DeployPanel() { -00251| const { isConnected } = useAgentContext(); -00252| const { machines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData(); -00253| const [isRefreshing, setIsRefreshing] = useState(false); -00254| -00255| const machineList = Object.values(machines); -00256| const machineCount = machineList.length; -00257| const appDeployEntries = Object.entries(appDeploy); -00258| const healthyCount = machineList.filter((m) => m.host !== null).length; -00259| const unhealthyCount = machineCount - healthyCount; -00260| -00261| const handleRefresh = async () => { -00262| setIsRefreshing(true); -00263| try { -00264| await refetch(); -00265| } finally { -00266| setIsRefreshing(false); -00267| } -00268| }; -00269| -00270| if (!isConnected) { -00271| return ( -00272|
-00273| -00277| -00278| -00279| -00280|
-00281|

Agent Not Connected

-00282|

-00283| Connect to the stack agent to manage deployments. -00284|

-00285|
-00286|
-00287|
-00288|
-00289| ); -00290| } -00291| -00292| if (isLoading) { -00293| return ( -00294|
-00295| -00296|
-00297| ); -00298| } -00299| -00300| return ( -00301|
-00302| -00312| -00315| {isRefreshing ? "Refreshing..." : "Refresh"} -00316| -00317| } -00318| /> -00319| -00320| {/* Status overview */} -00321|
-00322| -00323| -00324|
-00325| -00326|
-00327|
-00328|

Machines

-00329|

{machineCount}

-00330|
-00331|
-00332|
-00333| -00334| -00335|
0 ? "bg-green-500/10" : "bg-secondary", -00338| )}> -00339| 0 ? "text-green-500" : "text-muted-foreground", -00342| )} /> -00343|
-00344|
-00345|

Reachable

-00346|

{healthyCount}

-00347|
-00348|
-00349|
-00350| -00351| -00352|
0 ? "bg-amber-500/10" : "bg-secondary", -00355| )}> -00356| 0 ? "text-amber-500" : "text-muted-foreground", -00359| )} /> -00360|
-00361|
-00362|

Unreachable

-00363|

{unhealthyCount}

-00364|
-00365|
-00366|
-00367| -00368| -00369|
0 ? "bg-blue-500/10" : "bg-secondary", -00372| )}> -00373| 0 ? "text-blue-500" : "text-muted-foreground", -00376| )} /> -00377|
-00378|
-00379|

Deploy-enabled apps

-00380|

{appDeployEntries.length}

-00381|
-00382|
-00383|
-00384|
-00385| -00386| -00387| -00388| -00389| Machines -00390| {machineCount > 0 && ( -00391| -00392| {machineCount} -00393| -00394| )} -00395| -00396| -00397| App Targets -00398| {appDeployEntries.length > 0 && ( -00399| -00400| {appDeployEntries.length} -00401| -00402| )} -00403| -00404| Actions -00405| Settings -00406| -00407| -00408| {/* Machines Tab */} -00409| -00410| {machineCount === 0 ? ( -00411| -00412| -00413| -00414|
-00415|

No Machines

-00416|

-00417| Machine inventory is empty. Run infra:deploy and infra:pull-outputs to populate it, then reload the shell. -00418|

-00419|
-00420|
-00421|
-00422| ) : ( -00423|
-00424| {machineList.map((machine) => ( -00425| -00426| ))} -00427|
-00428| )} -00429|
-00430| -00431| {/* App Targets Tab */} -00432| -00433| {appDeployEntries.length === 0 ? ( -00434| -00435| -00436| -00437|
-00438|

No App Targets

-00439|

-00440| No apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config. -00441|

-00442|
-00443|
-00444|
-00445| ) : ( -00446|
-00447| {appDeployEntries.map(([appName, deploy]) => ( -00448| -00454| ))} -00455|
-00456| )} -00457|
-00458| -00459| {/* Actions Tab */} -00460| -00461| -00462| -00463| Colmena Actions -00464| -00465| Run Colmena commands against your fleet -00466| -00467| -00468| -00469| {machineCount === 0 ? ( -00470|
-00471|
-00472| -00473|

-00474| No machines in inventory. Provision infrastructure first. -00475|

-00476|
-00477|
-00478| ) : ( -00479| <> -00480|
-00481| -00485| -00489| -00493|
-00494|

-00495| Actions run the generated wrapper scripts with your configured defaults. -00496| Use the CLI for advanced options: colmena-apply --on tag:prod -00497|

-00498| -00499| )} -00500|
-00501|
-00502|
-00503| -00504| {/* Settings Tab */} -00505| -00506| -00507| -00508| Colmena Configuration -00509| -00510| Current Colmena module settings -00511| -00512| -00513| -00514|
-00515|
-00516|

Machine Source

-00517|

-00518| {colmenaConfig?.machineSource ?? "infra"} -00519|

-00520|
-00521|
-00522|

Hive Config

-00523|

-00524| {colmenaConfig?.config ?? ".stack/state/colmena/hive.nix"} -00525|

-00526|
-00527|
-00528|

Generate Hive

-00529|

-00530| {colmenaConfig?.generateHive ? "Yes" : "No"} -00531|

-00532|
-00533|
-00534|

Machine Count

-00535|

-00536| {colmenaConfig?.machineCount ?? machineCount} -00537|

-00538|
-00539|
-00540|
-00541|
-00542|
-00543|
-00544|
-00545| ); -00546| } -00547| - -(End of file - total 547 lines) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -Now I have a thorough understanding of the system. Let me create the docs. - -**How machines work in Studio UI**: The Deploy panel (`/studio/deploy`) reads machine inventory from `nixConfig.serializable.colmena` and `nixConfig.colmena.machinesComputed`. When the Machines tab is empty, it shows guidance to run `infra:deploy` and `infra:pull-outputs`. The machines come from either static Nix definitions or AWS EC2 discovery via the machines infra module. - -Let me create the new docs and update the index. - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "content": "---\ntitle: Machines\ndescription: Define and discover your deployment target machines for NixOS deployments\nicon: server\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel's machine inventory system lets you define the servers your applications deploy to. Machines can be declared statically in Nix or discovered dynamically from AWS EC2. The inventory feeds into Colmena for NixOS deployments and is visible in the Studio UI's Deploy panel.\n\n## How It Works\n\nMachine inventory flows through three layers:\n\n1. **Nix config** -- You declare machines (static) or EC2 discovery rules in `.stack/config.nix`\n2. **Infra module** -- The machines infra module resolves the inventory at deploy time (for EC2, it queries the AWS API)\n3. **Studio UI** -- The Deploy panel reads the resolved inventory and displays machine cards, app-to-machine mappings, and Colmena actions\n\n## Quick Start\n\n\n\n\n### Enable the machines module\n\nAdd to your `.stack/config.nix`:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\"; # or \"aws-ec2\"\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"10.0.1.10\";\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n ssh.user = \"deploy\";\n };\n };\n};\n```\n\n\n\n\n### Deploy infrastructure\n\nRun `infra:deploy` to provision and register the machine inventory:\n\n```bash\ninfra:deploy\n```\n\n\n\n\n### Pull outputs and reload\n\nPull the resolved inventory into your local state and reload the devshell:\n\n```bash\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\n\n\n\n### View in Studio\n\nOpen the Studio UI and navigate to **Deploy**. Your machines appear in the Machines tab with their status, roles, and connection info.\n\n\n\n\n## Static Machines\n\nFor fixed infrastructure (bare metal, pre-provisioned VMs, Hetzner, etc.), declare machines directly:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\";\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"web1.example.com\";\n roles = [ \"web\" \"app\" ];\n tags = [ \"production\" \"us-east\" ];\n provider = \"hetzner\";\n arch = \"x86_64-linux\";\n publicIp = \"203.0.113.10\";\n privateIp = \"10.0.1.10\";\n targetEnv = \"production\";\n\n ssh = {\n user = \"deploy\";\n port = 22;\n keyPath = \"~/.ssh/deploy_ed25519\";\n };\n\n labels = {\n team = \"platform\";\n cost-center = \"infra\";\n };\n };\n\n db-1 = {\n name = \"Database Primary\";\n host = \"10.0.2.10\";\n roles = [ \"database\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n targetEnv = \"production\";\n ssh.user = \"root\";\n };\n };\n};\n```\n\n### Machine Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | `string` | Human-friendly display name |\n| `host` | `string` | SSH hostname or IP address |\n| `ssh.user` | `string` | SSH user (default: `root`) |\n| `ssh.port` | `int` | SSH port (default: `22`) |\n| `ssh.keyPath` | `string?` | Path to SSH private key |\n| `roles` | `[string]` | Roles for app-to-machine targeting |\n| `tags` | `[string]` | Tags for grouping and filtering |\n| `provider` | `string?` | Infrastructure provider (aws, hetzner, etc.) |\n| `arch` | `string?` | System architecture (e.g., `x86_64-linux`) |\n| `publicIp` | `string?` | Public IP address |\n| `privateIp` | `string?` | Private IP address |\n| `targetEnv` | `string?` | Environment label (production, staging, etc.) |\n| `labels` | `{string: string}` | Arbitrary key-value metadata |\n| `nixosProfile` | `string?` | NixOS profile to deploy |\n| `nixosModules` | `[string]` | Extra NixOS modules for this machine |\n| `env` | `{string: string}` | Environment variables for this machine |\n| `metadata` | `{string: any}` | Extra metadata for downstream tooling |\n\n## AWS EC2 Discovery\n\nFor dynamic infrastructure, Stack can discover machines from AWS EC2 at deploy time:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"aws-ec2\";\n\n aws = {\n region = \"us-west-2\";\n\n # EC2 filters (same syntax as AWS CLI --filters)\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n };\n};\n```\n\nWhen `infra:deploy` runs, the machines module queries the EC2 API using the specified filters and builds an inventory from the results.\n\n### EC2 Tag Mapping\n\nEC2 instances are mapped to machine properties using configurable tag keys:\n\n| Machine Property | Default Tag Keys | Example Tag Value |\n|-----------------|-----------------|-------------------|\n| `name` | `Name` | `web-server-1` |\n| `roles` | `stack:role`, `role` | `web,app` |\n| `tags` | `stack:tag`, `tag` | `production,us-west` |\n| `targetEnv` | `stack:env`, `env`, `stage` | `production` |\n\nCustomize the tag keys used for mapping:\n\n```nix\nstackpanel.infra.machines.aws = {\n region = \"us-west-2\";\n\n # Which EC2 tags map to machine roles\n role-tag-keys = [ \"stack:role\" \"Role\" ];\n\n # Which EC2 tags map to machine tags\n tag-keys = [ \"stack:tag\" \"Environment\" ];\n\n # Which EC2 tags map to target environment\n env-tag-keys = [ \"stack:env\" \"Stage\" ];\n\n # Which EC2 tags provide the machine name\n name-tag-keys = [ \"Name\" ];\n\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n ];\n};\n```\n\n### Host Resolution\n\nWhen discovering EC2 instances, Stack picks the SSH host using a preference order:\n\n```nix\nstackpanel.infra.machines.aws = {\n # Try public DNS first, then public IP, then private IP\n host-preference = [ \"publicDns\" \"publicIp\" \"privateIp\" ];\n};\n```\n\nFor VPN or private-network setups where you connect to private IPs:\n\n```nix\nstackpanel.infra.machines.aws = {\n host-preference = [ \"privateIp\" ];\n};\n```\n\n### Default SSH Settings for EC2\n\nApply SSH defaults to all discovered EC2 machines:\n\n```nix\nstackpanel.infra.machines.aws = {\n ssh = {\n user = \"ec2-user\";\n port = 22;\n keyPath = \"~/.ssh/aws_deploy\";\n };\n};\n```\n\n### Filtering by Instance IDs\n\nTarget specific instances instead of using filters:\n\n```nix\nstackpanel.infra.machines.aws = {\n instance-ids = [\n \"i-0abc123def456789a\"\n \"i-0def456789abc1230\"\n ];\n};\n```\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:\n\n- Machine name and hostname\n- SSH connection info (user, port)\n- Architecture and provider\n- Roles (colored badges) and tags\n- Reachability status (green check or amber warning)\n\nWhen the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab\n\nShows which apps are configured for deployment and their target machines. Each row displays:\n\n- App name\n- Target machines (resolved from roles or explicit targets)\n- Role badge (if targeting by role)\n- Machine count\n\n### Actions Tab\n\nProvides buttons for Colmena deployment actions:\n\n- **colmena eval** -- Evaluate the NixOS configuration\n- **colmena build** -- Build the NixOS closures\n- **colmena apply** -- Deploy to target machines\n\n\nThe Actions tab is disabled when no machines are in the inventory. Provision infrastructure first with `infra:deploy`.\n\n\n### Settings Tab\n\nDisplays the current Colmena configuration: machine source, hive config path, whether hive generation is enabled, and total machine count.\n\n## Mapping Apps to Machines\n\nAfter machines are in your inventory, configure apps to deploy to them:\n\n```nix\nstackpanel.apps.api = {\n port = 1;\n root = \"./apps/api\";\n\n # Deploy this app to machines with the \"web\" role\n deploy = {\n enable = true;\n role = \"web\";\n };\n};\n\nstackpanel.apps.worker = {\n port = 2;\n root = \"./apps/worker\";\n\n # Deploy to specific machines by name\n deploy = {\n enable = true;\n targets = [ \"worker-1\" \"worker-2\" ];\n };\n};\n```\n\n## End-to-End Example\n\nHere is a complete setup with AWS EC2 discovery, app targeting, and infra provisioning:\n\n```nix\n{\n # Infrastructure provisioning\n stack.infra = {\n enable = true;\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n\n # AWS secrets (IAM role, KMS key)\n aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n };\n\n # Machine discovery\n machines = {\n enable = true;\n source = \"aws-ec2\";\n aws = {\n region = \"us-west-2\";\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n ssh.user = \"deploy\";\n };\n };\n };\n\n # Apps with deployment targets\n stack.apps = {\n web = {\n port = 0;\n root = \"./apps/web\";\n deploy = {\n enable = true;\n role = \"web\";\n };\n };\n\n api = {\n port = 1;\n root = \"./apps/api\";\n deploy = {\n enable = true;\n role = \"api\";\n };\n };\n };\n}\n```\n\nThen deploy:\n\n```bash\n# Provision infrastructure and discover machines\ninfra:deploy\n\n# Pull machine inventory into local state\ninfra:pull-outputs\n\n# Reload shell to pick up new inventory\nexit && nix develop --impure\n\n# View machines in Studio\n# Navigate to /studio/deploy\n\n# Deploy with Colmena\ncolmena apply\n```\n\n## Troubleshooting\n\n### \"No Machines\" in the Deploy panel\n\nThe machine inventory is populated by `infra:deploy`. If you see no machines:\n\n1. Run `infra:deploy` to execute the machines infra module\n2. Run `infra:pull-outputs` to sync the inventory to local state\n3. Reload your devshell (`exit && nix develop --impure`)\n4. Refresh the Deploy panel in Studio\n\n### EC2 discovery returns no instances\n\nCheck your filters and region. Test directly with the AWS CLI:\n\n```bash\naws ec2 describe-instances \\\n --region us-west-2 \\\n --filters \"Name=instance-state-name,Values=running\" \"Name=tag:Project,Values=myapp\" \\\n --query 'Reservations[].Instances[].{Id:InstanceId,Name:Tags[?Key==`Name`].Value|[0]}'\n```\n\n### SSH connection failures\n\nVerify SSH connectivity to the resolved host:\n\n```bash\nssh -o ConnectTimeout=5 deploy@ 'echo ok'\n```\n\nCommon issues:\n- Security group doesn't allow SSH from your IP\n- SSH key path is incorrect\n- Using public DNS but instance only has a private IP (adjust `host-preference`)\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Infrastructure](/docs/deployment/infrastructure) for the infra module system\n- [Fly.io](/docs/deployment/fly) for container-based deployment\n- [Cloudflare](/docs/deployment/cloudflare) for edge deployment\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/infrastructure.mdx", - "content": "---\ntitle: Infrastructure\ndescription: Provision cloud infrastructure with Alchemy and the infra module system\nicon: layers\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel includes a modular infrastructure provisioning system built on [Alchemy](https://alchemy.run). It provisions AWS resources (IAM roles, KMS keys, OIDC providers), databases (Neon), caches (Upstash), deployment targets (Cloudflare Workers), and machine inventories -- all declared in Nix and deployed with a single command.\n\n## How It Works\n\nThe infra system has three parts:\n\n1. **Nix modules** declare what infrastructure you need (`.stack/config.nix`)\n2. **Code generation** produces TypeScript modules in `packages/infra/` with typed inputs\n3. **Alchemy runtime** provisions the actual cloud resources when you run `infra:deploy`\n\nInfrastructure outputs (ARNs, URLs, keys) are synced back to your storage backend (SOPS, SSM, or Chamber) so other parts of your stack can reference them.\n\n## Quick Start\n\n\n\n\n### Enable infrastructure\n\nAdd to `.stack/config.nix`:\n\n```nix\nstackpanel.infra = {\n enable = true;\n\n # Where to store provisioned outputs\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n};\n```\n\n\n\n\n### Enable modules\n\nTurn on the infrastructure modules you need:\n\n```nix\n# AWS IAM + KMS for secrets\nstackpanel.infra.aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n};\n\n# Database (Neon Postgres)\nstackpanel.infra.database = {\n enable = true;\n provider = \"neon\";\n neon.region = \"aws-us-east-1\";\n};\n```\n\n\n\n\n### Reload and deploy\n\n```bash\n# Reload to pick up config changes\nexit && nix develop --impure\n\n# Deploy all infrastructure\ninfra:deploy\n```\n\n\n\n\n## Available Modules\n\n| Module | What it provisions | Key options |\n|--------|-------------------|-------------|\n| **aws-secrets** | IAM role, OIDC provider, KMS key + alias | `oidc.provider`, `iam.role-name`, `kms.alias` |\n| **deployment** | Cloudflare Workers/Pages per app | Per-app `deployment.host = \"cloudflare\"` |\n| **database** | Neon Postgres or devenv/Docker Postgres | `provider` (neon/devenv/docker) |\n| **cache** | Upstash Redis or devenv/Docker Redis | `provider` (upstash/devenv/docker) |\n| **machines** | Machine inventory (static or AWS EC2) | `source` (static/aws-ec2) |\n\n## Deploying Infrastructure\n\n### Basic Deploy\n\n```bash\n# Deploy all modules\ninfra:deploy\n```\n\nThis runs `alchemy deploy` in the generated `packages/infra/` directory. Alchemy evaluates each module, creates or updates resources, and writes state to its `.alchemy/` directory.\n\n### Adopting Existing Resources\n\nIf your AWS resources already exist (e.g., you created an IAM role manually), use the `--adopt` flag to bring them under Alchemy management without recreating them:\n\n```bash\ninfra:deploy --adopt\n```\n\nThis tells Alchemy to adopt existing resources instead of failing with \"already exists\" errors. The infra modules' custom resources (IAM roles, KMS keys) are built to handle adoption gracefully -- they detect existing resources and update them in place.\n\n\nThe `--adopt` flag is an Alchemy CLI option, not a per-resource setting. However, the Stack infra modules are written to be adopt-safe by default -- IAM roles use get-or-create logic, and KMS keys look up existing aliases before creating new keys.\n\n\n### Stage Selection\n\nDeploy to a specific stage (environment):\n\n```bash\ninfra:deploy --stage production\ninfra:deploy --stage staging\n```\n\nWithout `--stage`, Alchemy defaults to your username as the stage name. This lets each developer have isolated infrastructure for testing.\n\n### Destroying Infrastructure\n\n```bash\n# Remove all provisioned resources\ninfra:destroy\n\n# Remove a specific stage\ninfra:destroy --stage staging\n```\n\n\n`infra:destroy` removes cloud resources permanently. Adopted resources (IAM roles, KMS keys) are not deleted -- they're only unlinked from Alchemy state.\n\n\n## Storage Backends\n\nInfrastructure outputs (ARNs, URLs, connection strings) need to be stored somewhere accessible to your apps and CI. Stack supports three backends:\n\n### SOPS (Recommended for teams)\n\nOutputs are written to a SOPS-encrypted YAML file in your repo:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"sops\";\n sops.group = \"dev\"; # writes to .stack/secrets/vars/dev.sops.yaml\n};\n```\n\nAfter `infra:deploy`, outputs like `aws-secrets-roleArn` appear in the SOPS file. They're decrypted at runtime using your team's AGE keys.\n\n### AWS SSM Parameter Store\n\nOutputs are written to SSM parameters:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"ssm\";\n ssm.prefix = \"/myapp/infra\";\n};\n```\n\n### Chamber\n\nOutputs are written via Chamber:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"chamber\";\n chamber.service = \"myapp\";\n};\n```\n\n### None\n\nSkip output sync entirely (outputs are only in Alchemy state):\n\n```nix\nstackpanel.infra.storage-backend.type = \"none\";\n```\n\n## Studio UI: Infrastructure Panel\n\nThe Studio UI's **Infrastructure** panel (`/studio/infra`) provides a visual interface for managing your provisioned infrastructure. It has five tabs:\n\n### Status Tab\n\nShows an overview of your infrastructure configuration:\n- SST project name and region\n- OIDC provider type\n- IAM role name and ARN\n- KMS key status\n- Config file path\n\n### Deploy Tab\n\nDeploy or destroy infrastructure from the UI:\n- Stage selector (dev, staging, production)\n- Deploy button (runs `sst deploy --stage `)\n- Remove button (runs `sst remove --stage `)\n- Live command output\n\n### Outputs Tab\n\nDisplays all provisioned infrastructure outputs:\n- ARNs, URLs, and connection strings\n- Copy-to-clipboard for each value\n- Output source module\n\n### Resources Tab\n\nLists all deployed AWS resources by type and URN.\n\n### Configure Tab\n\nFull configuration form for SST/infrastructure settings:\n- Enable toggle\n- Project name, region, account ID\n- KMS key settings\n- OIDC provider selection (GitHub Actions, Fly.io, Roles Anywhere)\n- IAM role name\n\n## AWS Secrets Module\n\nThe `aws-secrets` module is the most commonly used infra module. It provisions:\n\n- **IAM Role** -- For CI/CD and local AWS access\n- **OIDC Provider** -- For passwordless authentication from GitHub Actions or Fly.io\n- **KMS Key + Alias** -- For encrypting secrets at rest\n- **KMS Access Policy** -- Grants the IAM role encrypt/decrypt permissions\n\n```nix\nstackpanel.infra.aws.secrets = {\n enable = true;\n region = \"us-west-2\";\n\n iam = {\n role-name = \"myapp-secrets-role\";\n additional-policies = [\n \"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess\"\n ];\n };\n\n kms = {\n alias = \"myapp-secrets\";\n deletion-window-days = 30;\n };\n\n oidc = {\n provider = \"github-actions\";\n github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n branch = \"*\";\n };\n };\n};\n```\n\n### OIDC Providers\n\n| Provider | Use case | Authentication flow |\n|----------|----------|-------------------|\n| `github-actions` | CI/CD from GitHub | GitHub OIDC token -> STS AssumeRoleWithWebIdentity |\n| `flyio` | Fly.io Machines | Fly OIDC token -> STS AssumeRoleWithWebIdentity |\n| `roles-anywhere` | Local dev with Step CA | mTLS certificate -> IAM Roles Anywhere |\n\n## Deployment Module\n\nThe deployment infra module provisions hosting resources for your apps. Currently supports Cloudflare Workers:\n\n```nix\nstackpanel.apps.web = {\n port = 0;\n framework.tanstack-start.enable = true;\n\n deployment = {\n enable = true;\n host = \"cloudflare\";\n bindings = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n secrets = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n };\n};\n```\n\nSupported framework/host combinations:\n\n| Framework | Cloudflare | Fly.io |\n|-----------|-----------|--------|\n| `tanstack-start` | TanStackStart Worker | Container |\n| `nextjs` | Nextjs Worker | Container |\n| `vite` | Vite Worker | Container |\n| `hono` | Worker (Node compat) | Container |\n| `astro` | Astro Worker | Container |\n| `remix` | Remix Worker | Container |\n| `nuxt` | Nuxt Worker | Container |\n\n## Generated Files\n\nThe infra system generates a complete TypeScript package at `packages/infra/`:\n\n```\npackages/infra/\n alchemy.run.ts # Orchestrator (imports all modules, syncs outputs)\n package.json # Dependencies from all active modules\n tsconfig.json # TypeScript config\n src/\n index.ts # Infra class (input resolution, output sync)\n types.ts # Per-module input TypeScript interfaces\n resources/\n iam-role.ts # Custom adopt-safe IAM Role resource\n kms-key.ts # Custom adopt-safe KMS Key resource\n kms-alias.ts # Custom KMS Alias resource\n modules/\n aws-secrets/\n index.ts # AWS secrets provisioning\n policies.ts # IAM policy builders\n deployment.ts # App deployment to Cloudflare\n database.ts # Database provisioning\n```\n\nThese files are regenerated on each devshell entry. The `alchemy.run.ts` orchestrator imports all active modules and runs `Infra.syncAll()` to write outputs to your storage backend.\n\n## Pulling Outputs\n\nAfter deploying, pull outputs from your storage backend into the local Nix state:\n\n```bash\ninfra:pull-outputs\n```\n\nThis reads outputs from Chamber/SSM and writes them to `.stack/data/infra-outputs.nix`, making them available as `config.stack.infra.outputs.*` in your Nix config.\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `infra:deploy` | Deploy all infrastructure modules |\n| `infra:deploy --adopt` | Deploy, adopting existing resources |\n| `infra:deploy --stage ` | Deploy to a specific stage |\n| `infra:destroy` | Remove all provisioned resources |\n| `infra:dev` | Start infrastructure dev mode |\n| `infra:pull-outputs` | Pull outputs from storage backend |\n\nAll commands accept additional Alchemy CLI options. Run `infra:deploy --help` for the full list.\n\n## Troubleshooting\n\n### \"EntityAlreadyExists\" on first deploy\n\nYour AWS resources already exist from a previous provisioning tool. Use `--adopt`:\n\n```bash\ninfra:deploy --adopt\n```\n\n### \"Resource already exists\" Alchemy error\n\nThis is different from the AWS error above. It means the Alchemy resource type is registered twice. This can happen if you have conflicting Alchemy versions. Check `packages/infra/package.json` and ensure a single `alchemy` version.\n\n### Outputs not showing up after deploy\n\n1. Check that `storage-backend` is configured (not `\"none\"`)\n2. Run `infra:pull-outputs` to sync outputs to local state\n3. Reload the devshell to pick up new outputs\n\n### \"storage-bakend\" typo error\n\nIf you see `The option 'stack.infra.storage-bakend' does not exist`, check your config for the typo. The correct option is `storage-backend` (with a `c`).\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Machines](/docs/deployment/machines) for machine inventory\n- [SST](/docs/deployment/sst) for the older SST-based infrastructure\n- [Secrets](/docs/secrets) for encrypted secrets management\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.8s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.1s) - -Now update the deployment index and meta.json: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "\n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n", - "newString": "\n \n Provision cloud resources with the Alchemy-based infra module system\n \n \n Define and discover deployment target machines (static or AWS EC2)\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "- Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up.", - "newString": "- Need AWS infrastructure? The [infra module system](./infrastructure) provisions it with IAM, KMS, and OIDC already wired up.\n- Need to manage deployment targets? The [machines module](./machines) discovers EC2 instances or accepts static definitions." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "### SST (AWS)\n\nSST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials.", - "newString": "### Infrastructure (Alchemy)\n\nThe [infra module system](./infrastructure) is the most comprehensive approach -- it provisions cloud infrastructure (KMS keys, IAM roles, databases, caches) alongside your deployment using Alchemy. It handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. Use `infra:deploy --adopt` to bring existing resources under management.\n\n### Machines\n\nThe [machines module](./machines) maintains an inventory of your deployment targets. Machines can be declared statically or discovered from AWS EC2 at deploy time. The inventory feeds into the Studio UI's Deploy panel and Colmena for NixOS deployments.\n\n### SST (Legacy)\n\n[SST](./sst) is an alternative infrastructure approach using the SST framework directly. For new projects, the [infra module system](./infrastructure) is recommended as it provides more flexibility and integrates with the broader Stack infra pipeline." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"containers\", \"cloudflare\", \"fly\", \"sst\"]\n}", - "newString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.6s) - -Here's a summary of what was done: - -## How to Set Up Machines in Studio UI - -The **Deploy** panel in Studio (`/studio/deploy`) shows your machine inventory. To populate it: - -1. Enable the machines module in `.stack/config.nix` with either `source = "static"` (declare machines in Nix) or `source = "aws-ec2"` (discover from EC2 at deploy time) -2. Run `infra:deploy` to provision/discover machines -3. Run `infra:pull-outputs` to sync inventory to local state -4. Reload devshell and open Studio -- machines appear in the Deploy panel's Machines tab - -The Deploy panel has four tabs: **Machines** (card grid with host/SSH/roles/tags), **App Targets** (which apps deploy where), **Actions** (Colmena eval/build/apply buttons), and **Settings** (Colmena config). - -## Docs Created/Updated - -**New pages:** -- `apps/docs/content/docs/deployment/machines.mdx` -- Comprehensive machine inventory docs covering static machines, AWS EC2 discovery (tag mapping, host resolution, SSH defaults), Studio UI Deploy panel walkthrough, app-to-machine mapping, and end-to-end example -- `apps/docs/content/docs/deployment/infrastructure.mdx` -- Infrastructure provisioning docs covering the Alchemy-based infra module system, available modules (aws-secrets, deployment, database, cache, machines), `--adopt` for existing resources, stage selection, storage backends (SOPS/SSM/Chamber), generated files, Studio UI Infrastructure panel, and CLI reference - -**Updated pages:** -- `apps/docs/content/docs/deployment/index.mdx` -- Added Infrastructure and Machines cards at the top, updated platform-specific notes to reference new docs and mark SST as legacy -- `apps/docs/content/docs/deployment/meta.json` -- Added `infrastructure` and `machines` to the page order - ---- - -## User - -the studio UI should allow me to add/configure machines fully - ---- - -## Assistant (Build · claude-opus-4-6 · 168.7s) - -_Thinking:_ - -The user wants the Studio UI to have full machine configuration capabilities - not just viewing machines, but adding, editing, and configuring them directly from the UI. Let me explore what's currently there and what needs to be built. - -Let me look at: -1. The current deploy panel to understand the read-only view -2. How other panels handle config editing (e.g., the infra panel's Configure tab, the apps panel's config forms) -3. How data gets written back to Nix config via the agent -4. The agent API endpoints for writing nix data - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore config editing patterns in Studio", - "prompt": "I need to understand how the Studio UI writes configuration back to Nix. Search thoroughly for:\n\n1. How `useNixData` and `useNixDataMutation` work - find the hooks in apps/web/src/lib/use-agent.ts or similar\n2. How the infra panel's Configure tab saves changes (apps/web/src/components/studio/panels/infra/use-sst-config.ts)\n3. How other panels handle form state + saving to Nix (look for patterns using agent client methods like `updateNixData`, `setNixData`, `writeNixData`, or similar)\n4. The agent HTTP client methods for writing data back - search in apps/web/src/lib/agent-http-client.ts or packages/agent-client\n5. Find examples of dialogs/forms that add new items to Nix config (e.g., add-variable-dialog, add-user dialogs, etc.)\n\nReturn:\n- The exact hook signatures for reading/writing nix data\n- The agent client method signatures for writing data\n- 2-3 concrete examples of how existing panels save config changes (file paths, line numbers, patterns)\n- How the data flows: UI form -> hook -> agent client -> Go agent -> nix file write", - "subagent_type": "explore" -} -``` -**Output:** -``` -Now I have a complete picture. Here is the comprehensive analysis: - ---- - -## Complete Data Flow: Studio UI -> Nix File Writes - -### 1. Hook Signatures for Reading/Writing Nix Data - -There are **two generations** of hooks and **three write mechanisms**: - -#### A. Legacy Generic Hook: `useNixData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1153-1200 - -```typescript -function useNixData( - entity: string, - options: { initialData?: T; autoRefetch?: boolean } = {}, -): { - // ... all useQuery return fields - data: T | null; - mutate: (data: T) => Promise; // writes the entire entity - refetch: () => Promise<...>; -} -``` - -- **Read:** `GET /api/nix/data?entity=` -- transforms response keys from kebab-case to snake_case via `kebabToSnake()` -- **Write (mutate):** `POST /api/nix/data` with `{ entity, data: snakeToKebab(data) }` -- transforms keys from snake_case to kebab-case - -#### B. Legacy Map Hook: `useNixMapData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1210-1267 - -```typescript -function useNixMapData( - entity: string, - options: { initialData?: Record; autoRefetch?: boolean } = {}, -): { - data: Record; - set: (key: string, value: V) => Promise; // key-level upsert - update: (key: string, updates: Partial) => Promise; // merge update - remove: (key: string) => Promise; // key-level delete - refetch: () => Promise<...>; -} -``` - -- Uses `MapEntityClient` internally (in `agent.ts`, lines 112-163) -- **set** -> `POST /api/nix/data { entity, key, data }` -- **remove** -> `POST /api/nix/data { entity, key, delete: true }` - -#### C. Modern Typed Hooks: Connect-RPC (e.g., `useApps`, `useSetApps`) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 336-540 - -```typescript -function useApps(): UseQueryResult>; -function useSetApps(): UseMutationResult; // calls client.setApps(apps) -function useVariables(): UseQueryResult>; -function useSetVariables(): UseMutationResult; // calls client.setVariables(variables) -function useUsers(): UseQueryResult<...>; -function useSetUsers(): UseMutationResult; // calls client.setUsers(users) -function useConfig(): UseQueryResult<...>; -function useSetConfig(): UseMutationResult; // calls client.setConfig(config) -function useSecrets(): UseQueryResult<...>; -function useSetSecrets(): UseMutationResult; // calls client.setSecrets(secrets) -function useAws(): UseQueryResult<...>; -function useSetAws(): UseMutationResult; // calls client.setAws(aws) -``` - -These use `createClient(AgentService, transport)` to call the proto-defined RPCs. - -#### D. Patch Hook: `usePatchNixData` (Connect-RPC) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1718-1750 - -```typescript -function usePatchNixData(): UseMutationResult; - -interface PatchNixDataParams { - entity: string; // e.g., "apps", "config" - key: string; // e.g., "web" (for map entities), "" for non-map - path: string; // dot-separated camelCase path, e.g., "go.mainPackage" - value: string; // JSON-encoded value, e.g., '"./cmd/api"' - valueType: string; // "string" | "bool" | "number" | "list" | "object" | "null" -} -``` - -This is the surgical editor -- changes a single field without replacing the whole entity. - ---- - -### 2. Agent HTTP Client Methods for Writing Data - -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent.ts` - -#### Generic REST methods (lines 607-675): -```typescript -class AgentHttpClient { - async get(path: string): Promise; - async post(path: string, body: unknown): Promise; - async delete(path: string): Promise; -} -``` - -#### Nix entity clients (lines 514-557): -```typescript -nix = { - apps: new MapEntityClient(this, "apps"), - services: new MapEntityClient(this, "services"), - users: new MapEntityClient(this, "users"), - variables: new MapEntityClient(this, "variables"), - tasks: new MapEntityClient(this, "tasks"), - generatedFiles: new MapEntityClient(this, "generated-files"), - entity: (name: string) => new MapEntityClient(this, name), - config: async (options) => { /* GET /api/nix/config */ }, - refreshConfig: async () => { /* POST /api/nix/config */ }, -}; -``` - -#### MapEntityClient (lines 112-163): -```typescript -class MapEntityClient { - async all(): Promise>; // GET /api/nix/data?entity=... - async get(key: string): Promise; // reads all, extracts key - async set(key: string, value: V): Promise; // POST /api/nix/data {entity, key, data} - async update(key: string, updates: Partial): Promise; // read-merge-set - async remove(key: string): Promise; // POST /api/nix/data {entity, key, delete:true} -} -``` - ---- - -### 3. Concrete Examples of How Panels Save Config Changes - -#### Example 1: SST Infra Panel -- Configuration Tab -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` - -**Pattern:** `useNixData` (whole-entity replacement) - -1. **Hook setup** (line 78-82): `const { data: sstData, mutate: setSstData } = useNixData("sst", { initialData: DEFAULT_SST_DATA });` -2. **Local form state** (lines 85-97): `useState` for each field (`formData`, `hasChanges`, etc.) -3. **Sync effect** (lines 100-154): When `sstData` changes, merge with inherited defaults and populate form state -4. **Field updaters** (lines 157-192): `updateField()`, `updateNestedField()`, `updateOidcProviderField()` set local state and mark `hasChanges = true` -5. **Save** (lines 195-208): `handleSave()` calls `await setSstData(formData)` -- this triggers: - - `useNixData.mutation.mutationFn` -> `client.post("/api/nix/data", { entity: "sst", data: snakeToKebab(formData) })` - - Go agent `handleNixDataWrite` -> `s.store.WriteEntity("sst", data)` -> serializes to Nix, writes `.stack/config.nix` - -#### Example 2: Configuration Panel -- AWS/Step CA/Theme/IDE -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/configuration/use-configuration.ts` - -**Pattern:** Multiple independent `useNixData` hooks with per-section save callbacks - -1. **Hook setup** (lines 116-140): Multiple calls: - ```typescript - const { data: stepCaData, mutate: setStepCa } = useNixData("step-ca"); - const { data: awsData, mutate: setAws } = useNixData("aws"); - const { data: themeData, mutate: setTheme } = useNixData("theme"); - const { data: ideData, mutate: setIde } = useNixData("ide"); - ``` -2. **Per-section save** (lines 459-475 for Step CA): - ```typescript - const saveStepCa = useCallback(async () => { - await setStepCa({ - ...(stepCaData ?? {}), - enable: stepEnabled, - "ca-url": optionalValue(stepCaUrl), - "ca-fingerprint": optionalValue(stepCaFingerprint), - // ... other fields - }); - }, [...deps]); - ``` - Each `setXxx()` call replaces the entire entity in its data file. - -#### Example 3: Add Variable Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/variables/add-variable-dialog.tsx` - -**Pattern:** Direct `MapEntityClient` for key-level writes (not whole entity) - -1. **Get client** (line 64): `const agentClient = useAgentClient();` -2. **On submit** (lines 214-238): - ```typescript - const variablesClient = client.nix.mapEntity("variables"); - const existing = await variablesClient.get(fullId); - if (existing) { toast.error("already exists"); return; } - const newVariable: Variable = { id: fullId, value: trimmedValue }; - await variablesClient.set(fullId, newVariable); - ``` -3. For **secrets** (lines 204-221): Also writes to SOPS via `client.writeGroupSecret({...})` plus creates a variable stub entry. -4. The `variablesClient.set(key, value)` call -> `POST /api/nix/data { entity: "variables", key: fullId, data: snakeToKebabValues({...}) }` -> Go agent `handleKeyLevelWrite` -> `s.store.SetKey("variables", key, value)` -- reads existing map, inserts key, serializes entire map back to Nix. - -#### Example 4: Add App Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx` - -Same pattern as variables (lines 76-100): -```typescript -const appsClient = client.nix.mapEntity("apps"); -const existingApp = await appsClient.get(values.id); -if (existingApp) { toast.error("already exists"); return; } -const newApp: AppEntity = { id: values.id, name: ..., path: ..., ... }; -await appsClient.set(values.id, newApp); -``` - -#### Example 5: Packages Panel -- Whole-Entity Array Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/packages/use-packages.ts` - -```typescript -const { data: userPackages, mutate: setUserPackages } = useNixData("packages", { initialData: [] }); - -const handleAddPackage = async (pkg) => { - const newPackages = [...currentPackages, attrPath]; - await setUserPackages(newPackages); // replaces entire packages list -}; -``` - ---- - -### 4. Complete Data Flow Diagram - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ UI Form (React Component) │ -│ ├── useState for local form state │ -│ ├── onChange handlers update local state │ -│ └── onSave calls hook mutate/set method │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Hook Layer (use-agent.ts) │ -│ │ -│ Path A: useNixData(entity).mutate(data) │ -│ ├── snakeToKebab(data) transforms keys to Nix convention │ -│ └── client.post("/api/nix/data", { entity, data }) │ -│ │ -│ Path B: MapEntityClient.set(key, value) │ -│ ├── snakeToKebabValues({[key]: value}) transforms inner keys │ -│ └── client.post("/api/nix/data", { entity, key, data }) │ -│ │ -│ Path C: usePatchNixData().mutate({entity,key,path,value,valueType}) │ -│ └── Connect-RPC: client.patchNixData({...}) │ -│ │ -│ Path D: useSetApps/useSetVariables etc. │ -│ └── Connect-RPC: client.setApps(proto_message) │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ HTTP or Connect-RPC to localhost:9876 - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Go Agent (apps/stack-go/internal/agent/server/) │ -│ │ -│ Path A/B: handleNixData (nix_data.go:42) │ -│ ├── POST: handleNixDataWrite (line 191) │ -│ │ ├── With key: handleKeyLevelWrite -> store.SetKey(entity,key,val)│ -│ │ │ Reads existing map, inserts/updates key, writes back │ -│ │ └── Without key: store.WriteEntity(entity, data) │ -│ │ Replaces entire entity │ -│ └── GET: handleNixDataRead -> store.ReadEntity / evaluateConfig │ -│ │ -│ Path C: PatchNixData (connect_patch.go:25) │ -│ ├── Builds full path: entity + "." + key + "." + path │ -│ ├── Prepends "stack." -> strips it -> gets config-internal path │ -│ └── store.PatchConsolidatedData(configPath, parsedValue) │ -│ Walks dot-separated path, creates intermediate maps, sets value │ -│ │ -│ Path D: SetApps etc. (connect_entities_gen.go) │ -│ └── writeNixEntityJSON(entity, protojsonBytes) │ -│ CamelCaseToNixJSON -> unwrap envelope -> WriteEntity │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Store Layer (apps/stack-go/pkg/nixdata/store.go) │ -│ │ -│ WriteEntity(entity, data) (line 177): │ -│ ├── If consolidated config: Read config.nix -> update entity key │ -│ │ -> WriteConsolidatedData (line 427) │ -│ │ -> nixser.SerializeWithSections(data) -> write .stack/ │ -│ │ config.nix │ -│ └── If legacy per-file: nixser.SerializeIndented(data) │ -│ -> write .stack/data/.nix │ -│ │ -│ SetKey(entity, key, value) (line 267): │ -│ readExistingMap -> set key in map -> writeMap │ -│ -> WriteConsolidatedData or per-file write │ -│ │ -│ PatchConsolidatedData(path, value) (line 456): │ -│ ReadConsolidatedData -> walk path with CamelToKebab conversion │ -│ -> set leaf value -> WriteConsolidatedData │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Nix File on Disk │ -│ │ -│ .stack/config.nix (consolidated, primary target) │ -│ Contains: { apps = {...}; variables = {...}; sst = {...}; ... } │ -│ │ -│ .stack/data/.nix (legacy per-entity files) │ -│ Contains: { key1 = {...}; key2 = {...}; } │ -│ │ -│ Serialization: Go nixser package converts Go map[string]any -> Nix │ -│ expression syntax (attrsets, strings, lists, booleans, numbers) │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - -### Key-Case Transformation Boundary - -The system maintains two naming conventions: -- **TypeScript/Proto side:** `snake_case` (matching protobuf conventions) -- **Nix side:** `kebab-case` (matching Nix conventions) - -Transformation happens in two places: -1. **TypeScript:** `snakeToKebab()` / `kebabToSnake()` in `/Users/cm/git/darkmatter/stack/apps/web/src/lib/nix-data/index.ts` (lines 48-84). Map keys (user-defined IDs like app names, variable names) are explicitly preserved using the `MAP_FIELD_NAMES` set (lines 20-42). -2. **Go:** `CamelCaseToNixJSON()` / `NixJSONToCamelCase()` in `/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/transform.go` for the Connect-RPC path. Same map-field-names exemption list. - -### SSE Feedback Loop - -After any write, the Go agent broadcasts an SSE event (`config.changed`) which the `AgentSSEProvider` in the browser receives, triggering TanStack Query cache invalidation so all UI panels automatically refresh with the new data. - - -session_id: ses_35678281cffeylSnbvcqulDIO4 - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.5s) - -Let me look at a concrete add dialog example and the nix data hooks more closely to match the patterns exactly. - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { Button } from "@ui/button"; -00004| import { -00005| Dialog, -00006| DialogContent, -00007| DialogDescription, -00008| DialogFooter, -00009| DialogHeader, -00010| DialogTitle, -00011| } from "@ui/dialog"; -00012| import { Loader2, Plus } from "lucide-react"; -00013| import { useCallback, useState } from "react"; -00014| import { toast } from "sonner"; -00015| import { useAgentContext, useAgentClient } from "@/lib/agent-provider"; -00016| import type { AppEntity } from "@/lib/types"; -00017| -00018| import { -00019| type AppForm, -00020| AppFormFields, -00021| type AppFormValues, -00022| parsePortValue, -00023| } from "./app-form-fields"; -00024| -00025| interface AddAppDialogProps { -00026| onSuccess: () => void; -00027| } -00028| -00029| export function AddAppDialog({ onSuccess }: AddAppDialogProps) { -00030| const { token } = useAgentContext(); -00031| const [dialogOpen, setDialogOpen] = useState(false); -00032| const [isSaving, setIsSaving] = useState(false); -00033| const [formRef, setFormRef] = useState(null); -00034| const [isFormValid, setIsFormValid] = useState(false); -00035| -00036| const handleFormReady = useCallback((form: AppForm) => { -00037| setFormRef(form); -00038| }, []); -00039| -00040| const handleValuesChange = useCallback((values: AppFormValues) => { -00041| const hasId = values.id?.trim().length > 0; -00042| const hasPath = values.path?.trim().length > 0; -00043| setIsFormValid(hasId && hasPath); -00044| }, []); -00045| -00046| const handleOpenChange = (open: boolean) => { -00047| setDialogOpen(open); -00048| if (!open) { -00049| // Reset form and validity when closing -00050| formRef?.reset(); -00051| setIsFormValid(false); -00052| } -00053| }; -00054| -00055| const handleSubmit = async () => { -00056| if (!formRef || !token) { -00057| toast.error("Not connected to agent"); -00058| return; -00059| } -00060| -00061| // Trigger validation -00062| const isValid = await formRef.trigger(); -00063| if (!isValid) return; -00064| -00065| const values = formRef.getValues(); -00066| -00067| if (!values.id.trim()) { -00068| toast.error("Please enter an app ID"); -00069| return; -00070| } -00071| -00072| setIsSaving(true); -00073| try { -00074| const client = useAgentClient(); -00075| if (token) client.setToken(token); -00076| const appsClient = client.nix.mapEntity("apps"); -00077| -00078| const existingApp = await appsClient.get(values.id); -00079| if (existingApp) { -00080| toast.error(`App "${values.id}" already exists`); -00081| setIsSaving(false); -00082| return; -00083| } -00084| -00085| // With simplified schema: environments is Record -00086| // Start with default "dev" environment -00087| const newApp: AppEntity = { -00088| id: values.id, -00089| name: values.name || values.id, -00090| description: values.description || undefined, -00091| path: values.path || `apps/${values.id}`, -00092| type: values.type || "bun", -00093| port: parsePortValue(values.port), -00094| domain: values.domain || undefined, -00095| environments: { -00096| dev: { name: "dev", env: {} }, -00097| }, -00098| }; -00099| -00100| await appsClient.set(values.id, newApp); -00101| toast.success( -00102| `Created app "${values.id}". You can now configure tasks and variables.`, -00103| ); -00104| handleOpenChange(false); -00105| onSuccess(); -00106| } catch (err) { -00107| toast.error(err instanceof Error ? err.message : "Failed to create app"); -00108| } finally { -00109| setIsSaving(false); -00110| } -00111| }; -00112| -00113| return ( -00114| -00115| -00123| -00124| -00125| Add New App -00126| -00127| Create a new app. You can configure tasks and variables after -00128| creation. -00129| -00130| -00131|
-00132| -00138|
-00139| -00140| -00143| -00147| -00148|
-00149|
-00150| ); -00151| } -00152| - -(End of file - total 152 lines) -
-``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts", - "offset": 1150, - "limit": 130 -} -``` -**Output:** -``` - -01151| * - aws → useAws() -01152| */ -01153| export function useNixData( -01154| entity: string, -01155| options: { initialData?: T; autoRefetch?: boolean } = {}, -01156| ) { -01157| const { initialData, autoRefetch = true } = options; -01158| const client = useAgentClient(); -01159| const queryClient = useQueryClient(); -01160| -01161| const queryKey = [...agentQueryKeys.all, "entity", entity]; -01162| -01163| const query = useQuery({ -01164| queryKey, -01165| queryFn: async () => { -01166| // Use the underlying HTTP API to fetch entity data -01167| const response = await client.get<{ -01168| success: boolean; -01169| data: { entity: string; exists: boolean; data: T }; -01170| }>(`/api/nix/data?entity=${encodeURIComponent(entity)}`); -01171| -01172| if (!response.success || !response.data.exists) { -01173| return null; -01174| } -01175| return kebabToSnake(response.data.data) as T; -01176| }, -01177| initialData, -01178| refetchOnWindowFocus: autoRefetch, -01179| }); -01180| -01181| const mutation = useMutation({ -01182| mutationFn: async (data: T) => { -01183| await client.post("/api/nix/data", { -01184| entity, -01185| data: snakeToKebab(data), -01186| }); -01187| // Return the new data -01188| return data; -01189| }, -01190| onSuccess: () => { -01191| queryClient.invalidateQueries({ queryKey }); -01192| }, -01193| }); -01194| -01195| return { -01196| ...query, -01197| mutate: mutation.mutateAsync, -01198| refetch: query.refetch, -01199| }; -01200| } -01201| -01202| /** -01203| * Generic hook for map-style Nix data entities with key-level operations. -01204| * Uses the legacy HTTP client under the hood. -01205| * -01206| * @deprecated For standard entities, prefer specific hooks: -01207| * - apps → useApps() + useSetApps() -01208| * - variables → useVariables() + useSetVariables() -01209| */ -01210| export function useNixMapData( -01211| entity: string, -01212| options: { initialData?: Record; autoRefetch?: boolean } = {}, -01213| ) { -01214| const { initialData, autoRefetch = true } = options; -01215| const client = useAgentClient(); -01216| const queryClient = useQueryClient(); -01217| const mapClient = useMemo( -01218| () => client.mapEntity(entity), -01219| [client, entity], -01220| ); -01221| -01222| const queryKey = [...agentQueryKeys.all, "mapEntity", entity]; -01223| -01224| const query = useQuery({ -01225| queryKey, -01226| queryFn: async () => { -01227| return mapClient.all(); -01228| }, -01229| initialData, -01230| refetchOnWindowFocus: autoRefetch, -01231| }); -01232| -01233| const setMutation = useMutation({ -01234| mutationFn: async ({ key, value }: { key: string; value: V }) => { -01235| await mapClient.set(key, value); -01236| }, -01237| onSuccess: () => { -01238| queryClient.invalidateQueries({ queryKey }); -01239| }, -01240| }); -01241| -01242| const updateMutation = useMutation({ -01243| mutationFn: async ({ key, updates }: { key: string; updates: Partial }) => { -01244| await mapClient.update(key, updates); -01245| }, -01246| onSuccess: () => { -01247| queryClient.invalidateQueries({ queryKey }); -01248| }, -01249| }); -01250| -01251| const removeMutation = useMutation({ -01252| mutationFn: async (key: string) => { -01253| await mapClient.remove(key); -01254| }, -01255| onSuccess: () => { -01256| queryClient.invalidateQueries({ queryKey }); -01257| }, -01258| }); -01259| -01260| return { -01261| ...query, -01262| set: (key: string, value: V) => setMutation.mutateAsync({ key, value }), -01263| update: (key: string, updates: Partial) => updateMutation.mutateAsync({ key, updates }), -01264| remove: (key: string) => removeMutation.mutateAsync(key), -01265| refetch: query.refetch, -01266| }; -01267| } -01268| -01269| // ============================================================================= -01270| // Turbo Package Graph -01271| // ============================================================================= -01272| -01273| export interface TurboTask { -01274| name: string; -01275| command?: string; -01276| } -01277| -01278| export interface TurboPackage { -01279| name: string; -01280| path: string; - -(File has more lines. Use 'offset' parameter to read beyond line 1280) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -Now let me look at what imports are available in the UI components and how dialogs are structured: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent-provider.tsx", - "offset": 1, - "limit": 50 -} -``` -**Output:** -``` - -00002| -00003| import { -00004| createContext, -00005| type ReactNode, -00006| useCallback, -00007| useContext, -00008| useEffect, -00009| useMemo, -00010| useRef, -00011| useState, -00012| } from "react"; -00013| import { AgentHttpClient, AGENT_AUTH_ERROR_EVENT } from "./agent"; -00014| import { useAgent, useAgentHealth } from "@/lib/use-agent"; -00015| import { useAgentSSEOptional } from "@/lib/agent-sse-provider"; -00016| -00017| const STORAGE_KEY = "stack.agent.token"; -00018| -00019| /** -00020| * Get the initial token from localStorage (runs synchronously during useState init). -00021| * This prevents a flash of "Connect to Agent" on page refresh. -00022| */ -00023| function getInitialToken(): string | null { -00024| if (typeof window === "undefined") return null; -00025| -00026| // Check for token in query parameter first -00027| const urlParams = new URLSearchParams(window.location.search); -00028| const queryToken = urlParams.get("token"); -00029| if (queryToken && queryToken.length >= 10) { -00030| return queryToken; -00031| } -00032| -00033| // Otherwise try localStorage -00034| return localStorage.getItem(STORAGE_KEY); -00035| } -00036| -00037| /** -00038| * Decode a JWT token without verifying the signature. -00039| * Returns the payload or null if invalid. -00040| */ -00041| function decodeJWT(token: string): { agent_id?: string; exp?: number } | null { -00042| try { -00043| const parts = token.split("."); -00044| if (parts.length !== 3) return null; -00045| -00046| // Decode the payload (second part) -00047| const payload = parts[1]; -00048| // Handle URL-safe base64 -00049| const base64 = payload.replace(/-/g, "+").replace(/_/g, "/"); -00050| const jsonPayload = atob(base64); -00051| return JSON.parse(jsonPayload); - -(File has more lines. Use 'offset' parameter to read beyond line 51) - -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/web/src/components/studio/panels/deploy/*" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 36.1s) - -Good -- there's only one file in the deploy folder. Now I'll build the complete machine management UI. I'll create: - -1. A `use-machines.ts` hook for reading/writing machine config -2. A `machine-form-fields.tsx` shared form component -3. An `add-machine-dialog.tsx` for adding machines -4. An `edit-machine-dialog.tsx` for editing machines -5. Update `deploy-panel.tsx` to integrate everything - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "content": "\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";\n\n// =============================================================================\n// Types — match the Nix module schema (nix/stack/infra/modules/machines)\n// =============================================================================\n\nexport interface MachineSSH {\n\tuser: string;\n\tport: number;\n\tkey_path: string | null;\n}\n\nexport interface MachineConfig {\n\tid?: string | null;\n\tname: string | null;\n\thost: string | null;\n\tssh: MachineSSH;\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublic_ip: string | null;\n\tprivate_ip: string | null;\n\ttarget_env: string | null;\n\tlabels: Record;\n\tnixos_profile: string | null;\n\tnixos_modules: string[];\n\tenv: Record;\n\tmetadata: Record;\n}\n\nexport interface AwsFilter {\n\tname: string;\n\tvalues: string[];\n}\n\nexport interface AwsMachinesConfig {\n\tregion: string | null;\n\tinstance_ids: string[];\n\tfilters: AwsFilter[];\n\tname_tag_keys: string[];\n\trole_tag_keys: string[];\n\ttag_keys: string[];\n\tenv_tag_keys: string[];\n\thost_preference: string[];\n\tssh: MachineSSH;\n}\n\nexport interface MachinesModuleConfig {\n\tenable: boolean;\n\tsource: \"static\" | \"aws-ec2\";\n\taws: AwsMachinesConfig;\n\tmachines: Record;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nexport const DEFAULT_SSH: MachineSSH = {\n\tuser: \"root\",\n\tport: 22,\n\tkey_path: null,\n};\n\nexport const DEFAULT_MACHINE: MachineConfig = {\n\tid: null,\n\tname: null,\n\thost: null,\n\tssh: { ...DEFAULT_SSH },\n\ttags: [],\n\troles: [],\n\tprovider: null,\n\tarch: null,\n\tpublic_ip: null,\n\tprivate_ip: null,\n\ttarget_env: null,\n\tlabels: {},\n\tnixos_profile: null,\n\tnixos_modules: [],\n\tenv: {},\n\tmetadata: {},\n};\n\nconst DEFAULT_AWS_CONFIG: AwsMachinesConfig = {\n\tregion: null,\n\tinstance_ids: [],\n\tfilters: [{ name: \"instance-state-name\", values: [\"running\"] }],\n\tname_tag_keys: [\"Name\"],\n\trole_tag_keys: [\"stack:role\", \"role\"],\n\ttag_keys: [\"stack:tag\", \"tag\"],\n\tenv_tag_keys: [\"stack:env\", \"env\", \"stage\"],\n\thost_preference: [\"publicDns\", \"publicIp\", \"privateIp\"],\n\tssh: { ...DEFAULT_SSH },\n};\n\nconst DEFAULT_CONFIG: MachinesModuleConfig = {\n\tenable: false,\n\tsource: \"static\",\n\taws: DEFAULT_AWS_CONFIG,\n\tmachines: {},\n};\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_CONFIG;\n\t\tconst machines = (rawInfra as Record).machines as Partial | undefined;\n\t\tif (!machines) return DEFAULT_CONFIG;\n\t\treturn {\n\t\t\tenable: machines.enable ?? false,\n\t\t\tsource: machines.source ?? \"static\",\n\t\t\taws: {\n\t\t\t\t...DEFAULT_AWS_CONFIG,\n\t\t\t\t...(machines.aws ?? {}),\n\t\t\t},\n\t\t\tmachines: (machines.machines ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: MachinesModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\tmachines: newConfig,\n\t\t} as any);\n\t};\n\n\tconst addMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeMachine = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.machines;\n\t\tupdated.machines = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateSettings = async (settings: Partial) => {\n\t\tawait saveConfig({ ...config, ...settings });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [113:8] Variable 'client' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "export function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "export function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [5:10] Identifier 'useAgentClient' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "import { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";", - "newString": "import { useNixData } from \"@/lib/use-agent\";" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 29.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx", - "content": "\"use client\";\n\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport type { MachineConfig } from \"./use-machines\";\n\ninterface MachineFormFieldsProps {\n\tvalues: MachineConfig;\n\tonChange: (values: MachineConfig) => void;\n\tshowIdField?: boolean;\n\tmachineId?: string;\n\tonIdChange?: (id: string) => void;\n}\n\nconst ARCH_OPTIONS = [\n\t{ value: \"x86_64-linux\", label: \"x86_64-linux\" },\n\t{ value: \"aarch64-linux\", label: \"aarch64-linux\" },\n];\n\nconst PROVIDER_OPTIONS = [\n\t{ value: \"aws\", label: \"AWS\" },\n\t{ value: \"hetzner\", label: \"Hetzner\" },\n\t{ value: \"gcp\", label: \"GCP\" },\n\t{ value: \"azure\", label: \"Azure\" },\n\t{ value: \"digitalocean\", label: \"DigitalOcean\" },\n\t{ value: \"bare-metal\", label: \"Bare Metal\" },\n];\n\nconst ENV_OPTIONS = [\n\t{ value: \"production\", label: \"Production\" },\n\t{ value: \"staging\", label: \"Staging\" },\n\t{ value: \"development\", label: \"Development\" },\n];\n\nfunction updateField(\n\tvalues: MachineConfig,\n\tonChange: (v: MachineConfig) => void,\n\tkey: K,\n\tvalue: MachineConfig[K],\n) {\n\tonChange({ ...values, [key]: value });\n}\n\nexport function MachineFormFields({\n\tvalues,\n\tonChange,\n\tshowIdField,\n\tmachineId,\n\tonIdChange,\n}: MachineFormFieldsProps) {\n\treturn (\n\t\t
\n\t\t\t{/* Identity */}\n\t\t\t{showIdField && (\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t onIdChange?.(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUnique identifier used as the Nix attribute name. Use lowercase with hyphens.\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t)}\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\tupdateField(values, onChange, \"name\", e.target.value || null)\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t{/* Connection */}\n\t\t\t
\n\t\t\t\tConnection\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"host\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tuser: e.target.value || \"root\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tport: Number.parseInt(e.target.value, 10) || 22,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tkey_path: e.target.value || null,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Networking */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"public_ip\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"private_ip\",\n\t\t\t\t\t\t\t\te.target.value || null,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Classification */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"arch\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ARCH_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"provider\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{PROVIDER_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"target_env\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ENV_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Roles and Tags */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"roles\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for app-to-machine targeting\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"tags\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for grouping and filtering\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.7s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport {\n\ttype MachineConfig,\n\tDEFAULT_MACHINE,\n\ttype useMachinesConfig,\n} from \"./use-machines\";\n\ninterface AddMachineDialogProps {\n\tmachines: ReturnType;\n}\n\nexport function AddMachineDialog({ machines }: AddMachineDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\tconst [machineId, setMachineId] = useState(\"\");\n\tconst [values, setValues] = useState({ ...DEFAULT_MACHINE });\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetMachineId(\"\");\n\t\t\tsetValues({ ...DEFAULT_MACHINE });\n\t\t}\n\t};\n\n\tconst isValid = machineId.trim().length > 0 && /^[a-z0-9-]+$/.test(machineId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = machineId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"Machine ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (machines.config.machines[id]) {\n\t\t\ttoast.error(`Machine \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.addMachine(id, values);\n\t\t\ttoast.success(`Machine \"${id}\" added`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add machine\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tAdd Machine\n\t\t\t\t\t\n\t\t\t\t\t\tAdd a new machine to your deployment inventory.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2 } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport type { MachineConfig, useMachinesConfig } from \"./use-machines\";\n\ninterface EditMachineDialogProps {\n\tmachineId: string;\n\tmachine: MachineConfig;\n\tmachines: ReturnType;\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n}\n\nexport function EditMachineDialog({\n\tmachineId,\n\tmachine,\n\tmachines,\n\topen,\n\tonOpenChange,\n}: EditMachineDialogProps) {\n\tconst [saving, setSaving] = useState(false);\n\tconst [deleting, setDeleting] = useState(false);\n\tconst [values, setValues] = useState({ ...machine });\n\n\tuseEffect(() => {\n\t\tif (open) {\n\t\t\tsetValues({ ...machine });\n\t\t}\n\t}, [open, machine]);\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateMachine(machineId, values);\n\t\t\ttoast.success(`Machine \"${machineId}\" updated`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to update machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\tsetDeleting(true);\n\t\ttry {\n\t\t\tawait machines.removeMachine(machineId);\n\t\t\ttoast.success(`Machine \"${machineId}\" removed`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to remove machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetDeleting(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tEdit Machine: {machineId}\n\t\t\t\t\t\n\t\t\t\t\t\tUpdate machine configuration. Changes are written to your Nix config.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{deleting && }\n\t\t\t\t\t\tDelete\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 96.6s) - -Now update the deploy panel to integrate everything -- add machine management, edit capability, settings for AWS EC2 config: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "content": "\"use client\";\n\n/**\n * Deploy Panel - Colmena-centric deployment management.\n *\n * Shows machine inventory, app-to-machine mapping, and deploy actions\n * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and\n * AWS EC2 discovery configuration.\n */\n\nimport { useState, useMemo } from \"react\";\nimport { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@ui/card\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@ui/tabs\";\nimport {\n\tActivity,\n\tAlertCircle,\n\tCheckCircle,\n\tCloudOff,\n\tCpu,\n\tHardDrive,\n\tLoader2,\n\tNetwork,\n\tPencil,\n\tPlay,\n\tRefreshCw,\n\tRocket,\n\tServer,\n\tSettings,\n\tShield,\n\tXCircle,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { useAgentContext } from \"@/lib/agent-provider\";\nimport { useNixConfig } from \"@/lib/use-agent\";\nimport { PanelHeader } from \"../shared/panel-header\";\nimport { cn } from \"@/lib/utils\";\nimport { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface MachineInfo {\n\tid: string;\n\tname: string;\n\thost: string | null;\n\tssh: {\n\t\tuser: string;\n\t\tport: number;\n\t\tkeyPath: string | null;\n\t};\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublicIp: string | null;\n\tprivateIp: string | null;\n\ttargetEnv: string | null;\n\tlabels: Record;\n}\n\ninterface AppDeployMapping {\n\tenable: boolean;\n\ttargets: string[];\n\tresolvedTargets: string[];\n\trole: string | null;\n\tnixosModules: string[];\n\tsystem: string | null;\n}\n\ninterface ColmenaConfig {\n\tenable: boolean;\n\tmachineSource: string;\n\tgenerateHive: boolean;\n\tconfig: string;\n\tmachineCount: number;\n\tmachineIds: string[];\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nfunction useColmenaData() {\n\tconst { data: nixConfig, isLoading, refetch } = useNixConfig();\n\n\tconst result = useMemo(() => {\n\t\tconst cfg = nixConfig as Record | null | undefined;\n\t\tif (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null };\n\n\t\tconst serializable = cfg.serializable as Record | undefined;\n\t\tconst colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null;\n\n\t\tconst colmenaData = cfg.colmena as Record | undefined;\n\t\tconst machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record;\n\n\t\tconst rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>;\n\t\tconst appDeploy: Record = {};\n\n\t\tfor (const [appName, appCfg] of Object.entries(rawApps)) {\n\t\t\tconst deploy = appCfg.deploy as Record | undefined;\n\t\t\tif (deploy?.enable) {\n\t\t\t\tappDeploy[appName] = {\n\t\t\t\t\tenable: true,\n\t\t\t\t\ttargets: (deploy.targets as string[]) ?? [],\n\t\t\t\t\tresolvedTargets: (deploy.resolvedTargets as string[]) ?? [],\n\t\t\t\t\trole: (deploy.role as string | null) ?? null,\n\t\t\t\t\tnixosModules: (deploy.nixosModules as string[]) ?? [],\n\t\t\t\t\tsystem: (deploy.system as string | null) ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { machines: machinesComputed, appDeploy, colmenaConfig };\n\t}, [nixConfig]);\n\n\treturn { ...result, isLoading, refetch };\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\nfunction MachineCard({\n\tmachine,\n\tonEdit,\n}: {\n\tmachine: MachineInfo;\n\tonEdit?: () => void;\n}) {\n\tconst isReachable = machine.host !== null;\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.name || machine.id}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{onEdit && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{isReachable ? (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machine.host}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tno host\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.ssh.user}@{machine.ssh.port}\n\t\t\t\t\t
\n\t\t\t\t\t{machine.arch && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.arch}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.provider && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.provider}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.targetEnv && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.targetEnv}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{(machine.tags.length > 0 || machine.roles.length > 0) && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{machine.roles.map((role) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{role}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{machine.tags.map((tag) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{tag}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t
\n\t\t\t\t)}\n\t\t\t
\n\t\t\n\t);\n}\n\nfunction AppTargetRow({\n\tappName,\n\tdeploy,\n\tmachines,\n}: {\n\tappName: string;\n\tdeploy: AppDeployMapping;\n\tmachines: Record;\n}) {\n\tconst resolvedNames = deploy.resolvedTargets.map(\n\t\t(id) => machines[id]?.name ?? id,\n\t);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appName}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{deploy.targets.length > 0\n\t\t\t\t\t\t\t? `Targets: ${deploy.targets.join(\", \")}`\n\t\t\t\t\t\t\t: deploy.role\n\t\t\t\t\t\t\t\t? `Role: ${deploy.role}`\n\t\t\t\t\t\t\t\t: \"No targets defined\"}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{deploy.role && (\n\t\t\t\t\t\n\t\t\t\t\t\t{deploy.role}\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{resolvedNames.length} machine{resolvedNames.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Settings: AWS EC2 Discovery Configuration\n// =============================================================================\n\nfunction AwsEc2Settings({ machines }: { machines: ReturnType }) {\n\tconst cfg = machines.config.aws;\n\tconst [saving, setSaving] = useState(false);\n\tconst [region, setRegion] = useState(cfg.region ?? \"\");\n\tconst [sshUser, setSshUser] = useState(cfg.ssh.user);\n\tconst [sshPort, setSshPort] = useState(cfg.ssh.port);\n\tconst [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? \"\");\n\tconst [hostPref, setHostPref] = useState(cfg.host_preference.join(\", \"));\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateSettings({\n\t\t\t\tsource: \"aws-ec2\",\n\t\t\t\tenable: true,\n\t\t\t\taws: {\n\t\t\t\t\t...cfg,\n\t\t\t\t\tregion: region || null,\n\t\t\t\t\tssh: {\n\t\t\t\t\t\tuser: sshUser || \"root\",\n\t\t\t\t\t\tport: sshPort || 22,\n\t\t\t\t\t\tkey_path: sshKeyPath || null,\n\t\t\t\t\t},\n\t\t\t\t\thost_preference: hostPref.split(\",\").map((s) => s.trim()).filter(Boolean),\n\t\t\t\t},\n\t\t\t});\n\t\t\ttoast.success(\"AWS EC2 settings saved\");\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to save\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\tAWS EC2 Discovery\n\t\t\t\t\n\t\t\t\t\tAuto-discover machines from running EC2 instances\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRegion(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setHostPref(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\tDefault SSH\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshUser(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshPort(Number.parseInt(e.target.value, 10) || 22)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshKeyPath(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport function DeployPanel() {\n\tconst { isConnected } = useAgentContext();\n\tconst { machines: computedMachines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData();\n\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);\n\tconst [editingMachine, setEditingMachine] = useState<{ id: string; config: MachineConfig } | null>(null);\n\n\t// Merge computed machines (from colmena/infra) with static config machines\n\tconst configMachines = machinesConfig.config.machines;\n\tconst computedList = Object.values(computedMachines);\n\tconst configList = Object.entries(configMachines);\n\n\t// Build combined machine list: computed takes precedence, config fills gaps\n\tconst allMachineEntries = useMemo(() => {\n\t\tconst seen = new Set();\n\t\tconst entries: Array<{ id: string; info: MachineInfo; configurable: boolean }> = [];\n\n\t\t// Add computed machines\n\t\tfor (const m of computedList) {\n\t\t\tseen.add(m.id);\n\t\t\tentries.push({ id: m.id, info: m, configurable: !!configMachines[m.id] });\n\t\t}\n\n\t\t// Add config-only machines not in computed\n\t\tfor (const [key, cfg] of configList) {\n\t\t\tif (!seen.has(key)) {\n\t\t\t\tentries.push({\n\t\t\t\t\tid: key,\n\t\t\t\t\tinfo: {\n\t\t\t\t\t\tid: key,\n\t\t\t\t\t\tname: cfg.name ?? key,\n\t\t\t\t\t\thost: cfg.host,\n\t\t\t\t\t\tssh: { user: cfg.ssh.user, port: cfg.ssh.port, keyPath: cfg.ssh.key_path },\n\t\t\t\t\t\ttags: cfg.tags,\n\t\t\t\t\t\troles: cfg.roles,\n\t\t\t\t\t\tprovider: cfg.provider,\n\t\t\t\t\t\tarch: cfg.arch,\n\t\t\t\t\t\tpublicIp: cfg.public_ip,\n\t\t\t\t\t\tprivateIp: cfg.private_ip,\n\t\t\t\t\t\ttargetEnv: cfg.target_env,\n\t\t\t\t\t\tlabels: cfg.labels,\n\t\t\t\t\t},\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}, [computedList, configList, configMachines]);\n\n\tconst machineCount = allMachineEntries.length;\n\tconst appDeployEntries = Object.entries(appDeploy);\n\tconst healthyCount = allMachineEntries.filter((m) => m.info.host !== null).length;\n\tconst unhealthyCount = machineCount - healthyCount;\n\n\tconst handleRefresh = async () => {\n\t\tsetIsRefreshing(true);\n\t\ttry {\n\t\t\tawait Promise.all([refetch(), machinesConfig.refetch()]);\n\t\t} finally {\n\t\t\tsetIsRefreshing(false);\n\t\t}\n\t};\n\n\tif (!isConnected) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Agent Not Connected

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tConnect to the stack agent to manage deployments.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t);\n\t}\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t);\n\t}\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRefreshing ? \"Refreshing...\" : \"Refresh\"}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t}\n\t\t\t/>\n\n\t\t\t{/* Status overview */}\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Machines

\n\t\t\t\t\t\t\t

{machineCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-green-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-green-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Reachable

\n\t\t\t\t\t\t\t

{healthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-amber-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-amber-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Unreachable

\n\t\t\t\t\t\t\t

{unhealthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-blue-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-blue-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Deploy-enabled apps

\n\t\t\t\t\t\t\t

{appDeployEntries.length}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t\n\n\t\t\t\t{/* Machines Tab */}\n\t\t\t\t\n\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Machines

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tAdd machines manually using the Add Machine button above,\n\t\t\t\t\t\t\t\t\t\tor configure AWS EC2 discovery in Settings and run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:deploy.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{allMachineEntries.map((entry) => (\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst cfg = configMachines[entry.id];\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (cfg) setEditingMachine({ id: entry.id, config: cfg });\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t\n\t\t\t\t\t{appDeployEntries.length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No App Targets

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tNo apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{appDeployEntries.map(([appName, deploy]) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* Actions Tab */}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tColmena Actions\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tRun Colmena commands against your fleet\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\tNo machines in inventory. Add machines or configure EC2 discovery first.\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tActions run the generated wrapper scripts with your configured defaults.\n\t\t\t\t\t\t\t\t\t\tUse the CLI for advanced options: colmena-apply --on tag:prod\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t{/* Settings Tab */}\n\t\t\t\t\n\t\t\t\t\t{/* Module enable + source selection */}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tMachines Module\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tConfigure how machines are sourced for deployment\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tEnable the machines infra module\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({ enable: checked })\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({\n\t\t\t\t\t\t\t\t\t\t\tsource: v as \"static\" | \"aws-ec2\",\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tStatic (defined in config)\n\t\t\t\t\t\t\t\t\t\tAWS EC2 (auto-discover)\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t{colmenaConfig && (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Hive Config

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.config ?? \".stack/state/colmena/hive.nix\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Generate Hive

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.generateHive ? \"Yes\" : \"No\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* AWS EC2 settings (shown when source is aws-ec2) */}\n\t\t\t\t\t{machinesConfig.config.source === \"aws-ec2\" && (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Edit dialog */}\n\t\t\t{editingMachine && (\n\t\t\t\t {\n\t\t\t\t\t\tif (!open) setEditingMachine(null);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.9s) - -No LSP errors in our new files. The pre-existing errors in `use-sst-config.ts`, `dashboard-sidebar.tsx`, and `use-configuration.ts` are unrelated. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.0s) - -Here's what was built: - -## New Files - -**`apps/web/src/components/studio/panels/deploy/use-machines.ts`** -- `useMachinesConfig()` hook that reads/writes the `infra.machines` config via `useNixData<>("infra")` -- Full CRUD: `addMachine()`, `updateMachine()`, `removeMachine()`, `updateSettings()` -- Types matching the Nix module schema: `MachineConfig`, `MachineSSH`, `AwsMachinesConfig`, `MachinesModuleConfig` - -**`apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx`** -- Shared form component used by both add and edit dialogs -- Fields: ID (optional), display name, host, SSH (user/port/key), public/private IP, architecture (select), provider (select), environment (select), roles (comma-separated), tags (comma-separated) -- Grouped into logical fieldsets (Connection, Networking, Classification) - -**`apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx`** -- Dialog with "Add Machine" button that appears in the panel header -- Validates machine ID (lowercase + hyphens), checks for duplicates -- Writes to Nix config via `machines.addMachine()` - -**`apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx`** -- Opens when clicking a machine card or pencil icon -- Full edit form + destructive "Delete" button -- Saves via `machines.updateMachine()` / `machines.removeMachine()` - -## Updated Files - -**`apps/web/src/components/studio/panels/deploy/deploy-panel.tsx`** -- Rewritten to integrate: -- **Machines tab**: Cards are now clickable to edit, show pencil icon, merge computed + config machines -- **Header**: "Add Machine" button next to Refresh -- **Settings tab**: New sections: - - Machines module enable/disable toggle - - Source selector (Static vs AWS EC2) - - Full AWS EC2 discovery config (region, host preference, default SSH) shown when source is `aws-ec2` - - Colmena hive config display -- **Edit dialog**: Wired up with state management, opens on card click - -## Data Flow - -UI form -> `useMachinesConfig()` hook -> `useNixData("infra").mutate()` -> `POST /api/nix/data { entity: "infra", data }` -> Go agent writes `.stack/config.nix` -> SSE `config.changed` event -> UI auto-refreshes - ---- - -## User - -how do i provision the machine as well? - ---- - -## Assistant (Build · claude-opus-4-6 · 175.2s) - -_Thinking:_ - -The user wants to provision machines (create EC2 instances, not just discover existing ones) from the Studio UI. Currently the machines module only supports "static" (manually defined) and "aws-ec2" (discover existing instances). There's no provisioning path. - -Let me look at the existing AWS EC2 infra module to understand what's already there for actually creating EC2 instances. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore EC2 provisioning infra modules", - "prompt": "Search thoroughly for everything related to EC2 instance provisioning (creating instances, not just discovering them):\n\n1. Read nix/stack/infra/modules/aws-ec2/module.nix - understand how EC2 instances are provisioned\n2. Read nix/stack/infra/modules/aws-ec2/index.ts - the Alchemy module that creates EC2 instances\n3. Read nix/stack/infra/modules/aws-ec2-app/module.nix - per-app EC2 resources\n4. Read nix/stack/infra/modules/aws-network/module.nix - VPC/subnet provisioning\n5. Read nix/stack/infra/modules/aws-security-groups/module.nix - security groups\n6. Read nix/stack/infra/modules/aws-key-pairs/module.nix - SSH key pairs\n7. Read nix/stack/infra/modules/aws-iam/module.nix - IAM for EC2\n8. Check what custom Alchemy resources exist for EC2 in nix/stack/infra/templates/ (ec2-instance.ts, etc.)\n9. Check .stack/config.nix for any existing aws-ec2/aws-network config sections\n\nReturn for each module:\n- Whether it exists and what it provisions\n- The Nix options it defines (option names, types, defaults)\n- How it relates to other modules (dependencies)\n- The current config values in .stack/config.nix if any\n- Key line numbers for important sections", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have complete information. Here is the comprehensive report. - ---- - -# EC2 Instance Provisioning -- Complete Analysis - -## Overview - -The EC2 provisioning system is a layered architecture with **7 Nix modules**, **13 custom Alchemy resources** (TypeScript), and a **codegen engine** that wires them together into a deployable `packages/infra` workspace. There are two approaches to EC2 provisioning: a low-level `aws-ec2` module and a high-level `aws-ec2-app` module. - ---- - -## 1. aws-ec2 (Low-Level EC2 Provisioning) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix` -**Exists: YES (207 lines)** -**What it provisions:** Raw EC2 instances from explicit definitions. No automatic VPC/subnet/AMI resolution. - -**Nix Options** (under `options.stack.infra.aws-ec2`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 145-148 | -| `instances` | `listOf instanceType` | `[]` | 151-154 | -| `defaults` | `instanceType` | `{name="default"; ami=""; instance-type="t3.micro"; subnet-id="";}` | 157-166 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines"]` | 168-172 | - -**instanceType submodule** (lines 70-141): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `name` | `str` | *(required)* | 72-75 | -| `ami` | `str` | *(required)* | 77-80 | -| `instance-type` | `str` | `"t3.micro"` | 82-86 | -| `subnet-id` | `str` | *(required)* | 88-91 | -| `security-group-ids` | `listOf str` | `[]` | 93-97 | -| `key-name` | `nullOr str` | `null` | 99-103 | -| `iam-instance-profile` | `nullOr str` | `null` | 105-109 | -| `user-data` | `nullOr str` | `null` | 111-115 | -| `root-volume-size` | `nullOr int` | `null` | 117-121 | -| `associate-public-ip` | `bool` | `true` | 123-127 | -| `tags` | `attrsOf str` | `{}` | 129-133 | -| `machine` | `machineMetaType` | `{}` | 135-139 | - -**machineMetaType** (lines 36-68): `tags` (listOf str), `roles` (listOf str), `target-env` (nullOr str), `arch` (nullOr str), `ssh` (submodule: `user`=root, `port`=22, `key-path`=null) - -**Dependencies:** Registers into `stack.infra.modules.aws-ec2` (line 178). Requires `@aws-sdk/client-ec2`. Auto-enables `stack.infra.enable` (line 176). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines` -- all JSON strings. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/index.ts` -**119 lines.** Simple loop: merges `defaults` into each `instance`, calls `Ec2Instance()` Alchemy resource, collects outputs. Emits machine inventory with `provider: "aws-ec2"`. - -**Key lines:** Instance creation at line 67-79. Machine inventory build at 88-109. Export at 112-118. - ---- - -## 2. aws-ec2-app (High-Level App-Centric EC2) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix` -**Exists: YES (918 lines)** -**What it provisions:** Per-app EC2 infrastructure including: security groups, key pairs, IAM roles + instance profiles, ALBs + target groups + listeners + host-based routing, ECR repos + GitHub OIDC roles, SSM parameters, and EC2 instances with automatic AMI resolution (Ubuntu 24.04 or NixOS). - -**Top-level options** (under `options.stack.infra.aws-ec2-app`, lines 852-876): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 853-856 | -| `defaults` | `appType` | `{}` | 859-863 | -| `apps` | `attrsOf appType` | `{}` | 865-869 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines" "albOutputs" "ecrOutputs" "ssmOutputs"]` | 871-875 | - -**appType submodule** (lines 715-849, ~25 options): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `instance-count` | `int` | `1` | 717-720 | -| `instances` | `listOf instanceType` | `[]` | 722-726 | -| `ami` | `nullOr str` | `null` | 728-733 | -| `os-type` | `enum ["ubuntu" "nixos"]` | `"ubuntu"` | 735-739 | -| `nixos` | `nixosType` | `{}` | 741-745 | -| `instance-type` | `nullOr str` | `null` | 747-751 | -| `vpc-id` | `nullOr str` | `null` | 753-757 | -| `subnet-ids` | `listOf str` | `[]` | 759-763 | -| `security-group-ids` | `listOf str` | `[]` | 765-769 | -| `security-group` | `securityGroupType` | `{}` | 771-775 | -| `key-name` | `nullOr str` | `null` | 777-781 | -| `key-pair` | `keyPairType` | `{}` | 783-787 | -| `iam` | `iamType` | `{}` | 789-793 | -| `iam-instance-profile` | `nullOr str` | `null` | 795-799 | -| `user-data` | `nullOr str` | `null` | 801-805 | -| `root-volume-size` | `nullOr int` | `null` | 807-811 | -| `associate-public-ip` | `bool` | `true` | 813-816 | -| `tags` | `attrsOf str` | `{}` | 819-823 | -| `alb` | `albType` | `{}` | 825-829 | -| `ecr` | `ecrType` | `{}` | 831-835 | -| `ssm` | `ssmType` | `{}` | 837-841 | -| `machine` | `machineMetaType` | `{}` | 843-847 | - -**Major nested submodule types:** - -- **securityGroupType** (lines 80-118): `create`, `name`, `description`, `ingress`/`egress` (listOf ruleType), `tags` -- **ruleType** (lines 36-78): `from-port`, `to-port`, `protocol`, `cidr-blocks`, `ipv6-cidr-blocks`, `security-group-ids`, `description` -- **keyPairType** (lines 120-152): `create`, `name`, `public-key`, `tags`, `destroy-on-delete` -- **iamType** (lines 168-221): `enable`, `role-name`, `assume-role-policy`, `managed-policy-arns` (defaults include SSM + ECR policies), `inline-policies`, `tags`, `instance-profile-name`, `instance-profile-tags` -- **nixosType** (lines 223-249): `ami-id`, `flake-url`, `host-config`, `flake-version` -- **albType** (lines 331-429): `enable`, `create`, `name`, `scheme`, `ip-address-type`, `subnet-ids`, `security-group-ids`, `http`, `https`, `certificate-arn`, `ssl-policy`, `existing-listener-http-arn`, `existing-listener-https-arn`, `hostnames`, `host-rule-priority`, `target-group` -- **albTargetGroupType** (lines 309-329): `port`, `protocol`, `health-check` -- **albHealthCheckType** (lines 251-307): `enabled`, `path`, `protocol`, `port`, `interval`, `timeout`, `healthy-threshold`, `unhealthy-threshold`, `matcher` -- **ecrType** (lines 489-533): `enable`, `create`, `repo-name`, `image-tag-mutability`, `scan-on-push`, `lifecycle-policy`, `github` -- **githubOidcType** (lines 431-487): `enable`, `repo-owner`, `repo-name`, `allowed-branches`, `allowed-workflows`, `allow-tags`, `role-name`, `oidc-provider-arn`, `create-oidc-provider` -- **ssmType** (lines 535-591): `enable`, `region`, `path-prefix`, `parameters`, `secure-parameters`, `env-file-path`, `refresh-script-path`, `install-cli`, `use-chamber` -- **instanceType** (per-instance override, lines 627-713): `name`, `ami`, `os-type`, `nixos`, `instance-type`, `subnet-id`, `security-group-ids`, `key-name`, `iam-instance-profile`, `user-data`, `root-volume-size`, `associate-public-ip`, `tags`, `machine` - -**Dependencies:** `@aws-sdk/client-ec2`, `@aws-sdk/client-iam`, `@aws-sdk/client-elastic-load-balancing-v2`, `@aws-sdk/client-ecr`, `@aws-sdk/client-ssm`, `@aws-sdk/client-sts` (lines 890-896). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines`, `albOutputs`, `ecrOutputs`, `ssmOutputs` (lines 906-914). - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/index.ts` -**962 lines.** The most complex module. Key logic: - -- **Default VPC resolution** (lines 233-255): Auto-discovers default VPC and subnets via DescribeVpcs/DescribeSubnets -- **AMI resolution** (lines 297-342): Auto-resolves Ubuntu 24.04 (Canonical, owner `099720109477`) or NixOS (Determinate Systems, owner `535002876703`, `determinate/nixos/epoch-1/*`) -- **NixOS user data** (lines 344-358): Generates bootstrap script using `fh apply nixos` with FlakeHub URL -- **Per-app loop** (lines 391-946): - 1. Security group creation (lines 406-433) - 2. Key pair import (lines 435-450) - 3. IAM role + instance profile with EC2 trust policy (lines 452-488) - 4. SSM parameter creation (lines 490-523) - 5. ECR repo + GitHub OIDC role for CI push (lines 525-698) - 6. EC2 instance creation with round-robin subnet assignment (lines 700-797) - 7. ALB + target group + listeners + host-based routing (lines 799-945) - ---- - -## 3. aws-network (VPC/Subnet Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix` -**Exists: YES (103 lines)** -**What it provisions:** Nothing -- it *discovers* the default VPC and subnets. This is a read-only module. - -**Nix Options** (under `options.stack.infra.aws-network`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 19-23 | -| `region` | `nullOr str` | `config.stack.aws.roles-anywhere.region or null` | 25-29 | -| `vpc.id` | `nullOr str` | `null` | 32-36 | -| `vpc.use-default` | `bool` | `true` | 38-42 | -| `subnets.ids` | `listOf str` | `[]` | 46-49 | -| `subnets.use-default` | `bool` | `true` | 52-56 | -| `sync-outputs` | `listOf str` | `["vpcId" "subnetIds"]` | 59-63 | - -**Outputs:** `vpcId` (string), `subnetIds` (JSON array), `subnetAzs` (JSON array). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Standalone network discovery. Not required by `aws-ec2-app` which has its own built-in VPC discovery (lines 233-255 of index.ts), but useful for `aws-security-groups` and `aws-ec2` which require explicit VPC/subnet IDs. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/index.ts` -**66 lines.** Simple: discovers default VPC, then subnets in that VPC. Falls back to explicit inputs. - ---- - -## 4. aws-security-groups - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix` -**Exists: YES (145 lines)** -**What it provisions:** Standalone security groups in a given VPC. - -**Nix Options** (under `options.stack.infra.aws-security-groups`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 93-97 | -| `vpc-id` | `str` | *(required)* | 99-102 | -| `groups` | `listOf groupType` | `[]` | 104-108 | -| `sync-outputs` | `listOf str` | `["groupIds"]` | 110-114 | - -**groupType**: `name` (str, required), `description` (nullOr str), `ingress`/`egress` (listOf ruleType), `tags` (attrsOf str). - -**Outputs:** `groupIds` (JSON: `{groupName: groupId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent module. Output group IDs can be manually referenced in `aws-ec2.instances[].security-group-ids`. Not automatically wired to aws-ec2 or aws-ec2-app. The `aws-ec2-app` module has its own inline security group creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/index.ts` -**69 lines.** Loops over groups, creates each via `SecurityGroup()` resource. - ---- - -## 5. aws-key-pairs - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix` -**Exists: YES (84 lines)** -**What it provisions:** EC2 SSH key pairs (imports public keys). - -**Nix Options** (under `options.stack.infra.aws-key-pairs`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 37-40 | -| `keys` | `attrsOf keyType` | `{}` | 43-47 | -| `sync-outputs` | `listOf str` | `["keyNames" "keyPairIds"]` | 49-53 | - -**keyType**: `public-key` (str, required), `tags` (attrsOf str), `destroy-on-delete` (bool, default false). - -**Outputs:** `keyNames` (JSON array), `keyPairIds` (JSON: `{keyName: keyPairId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent. Output key names can be referenced in `aws-ec2.instances[].key-name`. The `aws-ec2-app` module has its own inline key pair creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/index.ts` -**42 lines.** Loops over keys, imports each via `KeyPair()` resource. - ---- - -## 6. aws-iam (EC2-Oriented IAM) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix` -**Exists: YES (128 lines)** -**What it provisions:** A single IAM role with EC2 trust policy + instance profile. - -**Nix Options** (under `options.stack.infra.aws-iam`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 30-33 | -| `role.name` | `str` | *(required)* | 37-39 | -| `role.assume-role-policy` | `nullOr (attrsOf anything)` | `null` (defaults to EC2 trust) | 42-46 | -| `role.managed-policy-arns` | `listOf str` | `[]` | 48-52 | -| `role.inline-policies` | `listOf inlinePolicyType` | `[]` | 54-58 | -| `role.tags` | `attrsOf str` | `{}` | 60-64 | -| `instance-profile.name` | `nullOr str` | `null` (defaults to `-profile`) | 68-72 | -| `instance-profile.tags` | `attrsOf str` | `{}` | 74-78 | -| `sync-outputs` | `listOf str` | `["roleArn" "roleName" "instanceProfileArn" "instanceProfileName"]` | 81-85 | - -**Outputs:** `roleArn`, `roleName`, `instanceProfileArn`, `instanceProfileName`. - -**Dependencies:** `@aws-sdk/client-iam`. - -**Relationship:** Independent standalone module. Output `instanceProfileName` can be referenced in `aws-ec2.instances[].iam-instance-profile`. The `aws-ec2-app` module has its own inline IAM creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/index.ts` -**71 lines.** Creates role with EC2 trust policy (line 38), attaches managed policies, creates instance profile. - ---- - -## 7. machines (Machine Inventory Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` -**Exists: YES (307 lines)** -**What it provisions:** Nothing -- it *discovers* existing EC2 instances and emits a machine inventory JSON for Colmena. - -**Nix Options** (under `options.stack.infra.machines`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 157-161 | -| `source` | `enum ["static" "aws-ec2"]` | `"static"` | 163-169 | -| `aws.region` | `nullOr str` | inherited from `aws.roles-anywhere.region` | 173-177 | -| `aws.instance-ids` | `listOf str` | `[]` | 179-183 | -| `aws.filters` | `listOf awsFilterType` | `[{name="instance-state-name"; values=["running"]}]` | 185-194 | -| `aws.name-tag-keys` | `listOf str` | `["Name"]` | 196-200 | -| `aws.role-tag-keys` | `listOf str` | `["stack:role" "role"]` | 202-209 | -| `aws.tag-keys` | `listOf str` | `["stack:tag" "tag"]` | 211-218 | -| `aws.env-tag-keys` | `listOf str` | `["stack:env" "env" "stage"]` | 220-228 | -| `aws.host-preference` | `listOf enum` | `["publicDns" "publicIp" "privateIp"]` | 230-242 | -| `aws.ssh` | `sshConfigType` | `{user="root"; port=22; keyPath=null}` | 244-248 | -| `machines` | `attrsOf machineType` | `{}` | 251-255 | - -**Outputs:** `machines` (JSON string of machine inventory). - -**Dependencies:** `@aws-sdk/client-ec2` (only when `source == "aws-ec2"`). - -**Relationship:** This is a *discovery* module, not a provisioning module. Its output feeds into Colmena deployment (see below). - ---- - -## 8. Custom Alchemy Resources (Templates) - -All in `/Users/cm/git/darkmatter/stack/nix/stack/infra/templates/`: - -| File | Resource Type | Lines | What it does | -|------|-------------|-------|-------------| -| `ec2-instance.ts` | `stack::Ec2Instance` | 145 | `RunInstancesCommand` to create, `TerminateInstancesCommand` to delete. gp3 volumes. Base64 user data encoding. | -| `security-group.ts` | `stack::SecurityGroup` | 182 | Creates SG, authorizes ingress/egress rules. Adopts existing SGs by name+VPC. | -| `key-pair.ts` | `stack::KeyPair` | 101 | Imports public key via `ImportKeyPairCommand`. Adopts existing by name. | -| `iam-role.ts` | `stack::IamRole` | 153 | Creates/adopts role, updates trust policy, attaches managed + inline policies. | -| `iam-instance-profile.ts` | `stack::IamInstanceProfile` | 137 | Creates/adopts profile, adds role. Full lifecycle (remove role + delete on destroy). | -| `application-load-balancer.ts` | `stack::ApplicationLoadBalancer` | 121 | Creates ALB, returns ARN + DNS + zone ID. | -| `target-group.ts` | `stack::TargetGroup` | 136 | Creates target group with health checks. | -| `listener.ts` | `stack::Listener` | 108 | Creates HTTP/HTTPS listener with SSL policy. | -| `listener-rule.ts` | `stack::ListenerRule` | 105 | Creates host-header routing rules. | -| `target-group-attachment.ts` | `stack::TargetGroupAttachment` | 63 | Registers/deregisters instance to target group. | -| `ecr-repository.ts` | `stack::EcrRepository` | 127 | Creates ECR repo with lifecycle policy. | - -All resources follow the Alchemy `Resource()` pattern with create-or-adopt semantics and `this.phase === "delete"` handling. They are **read at Nix eval time** and written to `packages/infra/src/resources/` by the codegen engine (`codegen.nix`, lines 561-602). - ---- - -## 9. Current config.nix Values - -### File: `/Users/cm/git/darkmatter/stack/.stack/config.nix` - -**Lines 447-475** -- the `infra` section: - -```nix -infra = { - enable = true; # line 448 - storage-backend = { - type = "sops"; # line 450 - sops.group = "dev"; # line 451 - }; - database = { # line 453 - enable = true; - provider = "neon"; - neon = { - region = "aws-us-east-1"; - api-key-ssm-path = "/common/neon-api-key"; - }; - }; - machines = { # line 461 - enable = true; - source = "aws-ec2"; # Discovery mode, not provisioning - aws = { - region = "us-west-2"; - filters = [{ - name = "instance-state-name"; - values = [ "running" ]; - }]; - }; - machines = { }; - }; -}; -``` - -**Key observations about current config:** -- **`infra.enable = true`** -- the infra system is active -- **`machines.enable = true` with `source = "aws-ec2"`** -- EC2 instance *discovery* is enabled (not provisioning) -- **No `aws-ec2` or `aws-ec2-app` sections** -- EC2 provisioning is NOT currently configured -- **No `aws-network` section** -- VPC discovery is not independently enabled -- **No `aws-security-groups` section** -- standalone SG provisioning is not enabled -- **No `aws-key-pairs` section** -- standalone key pair provisioning is not enabled -- **No `aws-iam` section** -- standalone IAM provisioning is not enabled -- Storage backend is SOPS (dev group) -- AWS region from `aws.roles-anywhere.region` = `"us-west-2"` (line 149) - ---- - -## 10. Module Dependency Graph - -``` - ┌─────────────────┐ - │ config.nix │ - │ (infra section) │ - └────────┬────────┘ - │ - ┌────────────┼────────────┐ - │ │ │ - ┌─────▼─────┐ ┌───▼────┐ ┌────▼─────┐ - │ aws-ec2 │ │aws-ec2 │ │ machines │ (discovery) - │ -app │ │(basic) │ │ │ - └─────┬─────┘ └───┬────┘ └──────────┘ - │ │ - ┌───────┬───────┼───────┬───┤ - │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ - SG KeyPair IAM EC2 ALB+TG+Listener+ECR+SSM - (inline) (inline)(inline)(Ec2Instance resource) - │ │ │ │ - ▼ ▼ ▼ ▼ - Alchemy Resources (templates/*.ts) -``` - -**Two provisioning paths:** -1. **Composable (low-level):** `aws-network` -> `aws-security-groups` -> `aws-key-pairs` -> `aws-iam` -> `aws-ec2` (each module independent, manually wire outputs) -2. **All-in-one (high-level):** `aws-ec2-app` (creates SG, key pairs, IAM, ALB, ECR, SSM, and EC2 instances all in one module) - -**Consumer:** The Colmena module (`nix/stack/modules/colmena/module.nix`, lines 577-580) reads machine inventory from `infra.outputs."aws-ec2".machines` OR `infra.outputs."aws-ec2-app".machines` OR `infra.outputs.machines.machines` (the discovery module). - ---- - -## 11. Codegen Wiring - -The codegen engine (`/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix`): -- **Line 225**: Reads `ec2-instance.ts` template -- **Lines 561-566**: Writes it to `${outputDir}/src/resources/ec2-instance.ts` -- **Lines 462-489**: Copies each module's `index.ts` to `${outputDir}/modules/.ts` -- **Lines 261-340**: Generates `alchemy.run.ts` orchestrator that imports all enabled modules -- **Lines 345-348**: Merges all module dependencies into the package.json - -The infra module system entry point is `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` (line 34-35) which imports both `aws-ec2/module.nix` and `aws-ec2-app/module.nix`. - - -session_id: ses_3565ab3f8ffehVv05KuQ9ZqSdj - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.9s) - -You already have a complete EC2 provisioning pipeline built out -- two of them actually: - -1. **`aws-ec2`** -- Low-level: you define instances with explicit AMI, subnet, security groups -2. **`aws-ec2-app`** -- High-level: per-app EC2 with auto-AMI resolution, inline SG/IAM/ALB/ECR creation - -Neither is wired into the Studio UI yet. The Deploy panel currently only manages machine *inventory* (static + discovery), not provisioning. - -Let me add EC2 provisioning configuration to the Settings tab so you can define instances and deploy them from the UI. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { useMemo } from "react"; -00004| import { useNixData } from "@/lib/use-agent"; -00005| -00006| // ============================================================================= -00007| // Types — match the Nix module schema (nix/stack/infra/modules/machines) -00008| // ============================================================================= -00009| -00010| export interface MachineSSH { -00011| user: string; -00012| port: number; -00013| key_path: string | null; -00014| } -00015| -00016| export interface MachineConfig { -00017| id?: string | null; -00018| name: string | null; -00019| host: string | null; -00020| ssh: MachineSSH; -00021| tags: string[]; -00022| roles: string[]; -00023| provider: string | null; -00024| arch: string | null; -00025| public_ip: string | null; -00026| private_ip: string | null; -00027| target_env: string | null; -00028| labels: Record; -00029| nixos_profile: string | null; -00030| nixos_modules: string[]; -00031| env: Record; -00032| metadata: Record; -00033| } -00034| -00035| export interface AwsFilter { -00036| name: string; -00037| values: string[]; -00038| } -00039| -00040| export interface AwsMachinesConfig { -00041| region: string | null; -00042| instance_ids: string[]; -00043| filters: AwsFilter[]; -00044| name_tag_keys: string[]; -00045| role_tag_keys: string[]; -00046| tag_keys: string[]; -00047| env_tag_keys: string[]; -00048| host_preference: string[]; -00049| ssh: MachineSSH; -00050| } -00051| -00052| export interface MachinesModuleConfig { -00053| enable: boolean; -00054| source: "static" | "aws-ec2"; -00055| aws: AwsMachinesConfig; -00056| machines: Record; -00057| } -00058| -00059| // ============================================================================= -00060| // Defaults -00061| // ============================================================================= -00062| -00063| export const DEFAULT_SSH: MachineSSH = { -00064| user: "root", -00065| port: 22, -00066| key_path: null, -00067| }; -00068| -00069| export const DEFAULT_MACHINE: MachineConfig = { -00070| id: null, -00071| name: null, -00072| host: null, -00073| ssh: { ...DEFAULT_SSH }, -00074| tags: [], -00075| roles: [], -00076| provider: null, -00077| arch: null, -00078| public_ip: null, -00079| private_ip: null, -00080| target_env: null, -00081| labels: {}, -00082| nixos_profile: null, -00083| nixos_modules: [], -00084| env: {}, -00085| metadata: {}, -00086| }; -00087| -00088| const DEFAULT_AWS_CONFIG: AwsMachinesConfig = { -00089| region: null, -00090| instance_ids: [], -00091| filters: [{ name: "instance-state-name", values: ["running"] }], -00092| name_tag_keys: ["Name"], -00093| role_tag_keys: ["stack:role", "role"], -00094| tag_keys: ["stack:tag", "tag"], -00095| env_tag_keys: ["stack:env", "env", "stage"], -00096| host_preference: ["publicDns", "publicIp", "privateIp"], -00097| ssh: { ...DEFAULT_SSH }, -00098| }; -00099| -00100| const DEFAULT_CONFIG: MachinesModuleConfig = { -00101| enable: false, -00102| source: "static", -00103| aws: DEFAULT_AWS_CONFIG, -00104| machines: {}, -00105| }; -00106| -00107| // ============================================================================= -00108| // Hook -00109| // ============================================================================= -00110| -00111| export function useMachinesConfig() { -00112| const { data: rawInfra, mutate: setInfra, refetch } = useNixData>("infra"); -00113| -00114| const config = useMemo(() => { -00115| if (!rawInfra) return DEFAULT_CONFIG; -00116| const machines = (rawInfra as Record).machines as Partial | undefined; -00117| if (!machines) return DEFAULT_CONFIG; -00118| return { -00119| enable: machines.enable ?? false, -00120| source: machines.source ?? "static", -00121| aws: { -00122| ...DEFAULT_AWS_CONFIG, -00123| ...(machines.aws ?? {}), -00124| }, -00125| machines: (machines.machines ?? {}) as Record, -00126| }; -00127| }, [rawInfra]); -00128| -00129| const saveConfig = async (newConfig: MachinesModuleConfig) => { -00130| const currentInfra = (rawInfra ?? {}) as Record; -00131| await setInfra({ -00132| ...currentInfra, -00133| machines: newConfig, -00134| } as any); -00135| }; -00136| -00137| const addMachine = async (key: string, machine: MachineConfig) => { -00138| const updated = { ...config }; -00139| updated.machines = { ...updated.machines, [key]: machine }; -00140| if (!updated.enable) updated.enable = true; -00141| await saveConfig(updated); -00142| }; -00143| -00144| const updateMachine = async (key: string, machine: MachineConfig) => { -00145| const updated = { ...config }; -00146| updated.machines = { ...updated.machines, [key]: machine }; -00147| await saveConfig(updated); -00148| }; -00149| -00150| const removeMachine = async (key: string) => { -00151| const updated = { ...config }; -00152| const { [key]: _, ...rest } = updated.machines; -00153| updated.machines = rest; -00154| await saveConfig(updated); -00155| }; -00156| -00157| const updateSettings = async (settings: Partial) => { -00158| await saveConfig({ ...config, ...settings }); -00159| }; -00160| -00161| return { -00162| config, -00163| addMachine, -00164| updateMachine, -00165| removeMachine, -00166| updateSettings, -00167| refetch, -00168| }; -00169| } -00170| - -(End of file - total 170 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.1s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "// =============================================================================\n// EC2 Provisioning Types — match aws-ec2-app Nix module\n// =============================================================================\n\nexport interface Ec2MachineMeta {\n\ttags: string[];\n\troles: string[];\n\ttarget_env: string | null;\n\tarch: string | null;\n\tssh: MachineSSH;\n}\n\nexport interface Ec2SecurityGroupRule {\n\tfrom_port: number;\n\tto_port: number;\n\tprotocol: string;\n\tcidr_blocks: string[];\n\tdescription: string;\n}\n\nexport interface Ec2SecurityGroup {\n\tcreate: boolean;\n\tname: string | null;\n\tdescription: string | null;\n\tingress: Ec2SecurityGroupRule[];\n\tegress: Ec2SecurityGroupRule[];\n}\n\nexport interface Ec2KeyPair {\n\tcreate: boolean;\n\tname: string | null;\n\tpublic_key: string | null;\n}\n\nexport interface Ec2IamConfig {\n\tenable: boolean;\n\trole_name: string | null;\n}\n\nexport interface Ec2AppInstance {\n\tname: string;\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tsubnet_id: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppConfig {\n\tinstance_count: number;\n\tinstances: Ec2AppInstance[];\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tvpc_id: string | null;\n\tsubnet_ids: string[];\n\tsecurity_group_ids: string[];\n\tsecurity_group: Ec2SecurityGroup;\n\tkey_name: string | null;\n\tkey_pair: Ec2KeyPair;\n\tiam: Ec2IamConfig;\n\tuser_data: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppModuleConfig {\n\tenable: boolean;\n\tdefaults: Partial;\n\tapps: Record;\n}\n\nexport const DEFAULT_EC2_MACHINE_META: Ec2MachineMeta = {\n\ttags: [],\n\troles: [],\n\ttarget_env: null,\n\tarch: null,\n\tssh: { ...DEFAULT_SSH },\n};\n\nexport const DEFAULT_EC2_APP: Ec2AppConfig = {\n\tinstance_count: 1,\n\tinstances: [],\n\tami: null,\n\tos_type: \"ubuntu\",\n\tinstance_type: \"t3.micro\",\n\tvpc_id: null,\n\tsubnet_ids: [],\n\tsecurity_group_ids: [],\n\tsecurity_group: {\n\t\tcreate: true,\n\t\tname: null,\n\t\tdescription: null,\n\t\tingress: [\n\t\t\t{ from_port: 22, to_port: 22, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"SSH\" },\n\t\t\t{ from_port: 80, to_port: 80, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTP\" },\n\t\t\t{ from_port: 443, to_port: 443, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTPS\" },\n\t\t],\n\t\tegress: [\n\t\t\t{ from_port: 0, to_port: 0, protocol: \"-1\", cidr_blocks: [\"0.0.0.0/0\"], description: \"All outbound\" },\n\t\t],\n\t},\n\tkey_name: null,\n\tkey_pair: { create: false, name: null, public_key: null },\n\tiam: { enable: true, role_name: null },\n\tuser_data: null,\n\troot_volume_size: null,\n\tassociate_public_ip: true,\n\ttags: {},\n\tmachine: { ...DEFAULT_EC2_MACHINE_META },\n};\n\nconst DEFAULT_EC2_MODULE: Ec2AppModuleConfig = {\n\tenable: false,\n\tdefaults: {},\n\tapps: {},\n};\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [224:7] Variable 'DEFAULT_EC2_MODULE' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now add the provisioning hook alongside the machines hook: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}", - "newString": "\treturn {\n\t\tconfig,\n\t\trawInfra,\n\t\tsetInfra,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n\nexport function useEc2Provisioning() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_EC2_MODULE;\n\t\tconst ec2App = (rawInfra as Record)[\"aws_ec2_app\"] as Partial | undefined;\n\t\tif (!ec2App) return DEFAULT_EC2_MODULE;\n\t\treturn {\n\t\t\tenable: ec2App.enable ?? false,\n\t\t\tdefaults: ec2App.defaults ?? {},\n\t\t\tapps: (ec2App.apps ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: Ec2AppModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\t\"aws_ec2_app\": newConfig,\n\t\t} as any);\n\t};\n\n\tconst addApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeApp = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.apps;\n\t\tupdated.apps = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst setEnabled = async (enabled: boolean) => {\n\t\tawait saveConfig({ ...config, enable: enabled });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddApp,\n\t\tupdateApp,\n\t\tremoveApp,\n\t\tsetEnabled,\n\t\trefetch,\n\t};\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 45.9s) - -Now create the provisioning UI components: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n\ttype Ec2AppConfig,\n\tDEFAULT_EC2_APP,\n\tDEFAULT_EC2_MACHINE_META,\n\ttype useEc2Provisioning,\n} from \"./use-machines\";\n\ninterface ProvisionInstanceDialogProps {\n\tec2: ReturnType;\n}\n\nconst INSTANCE_TYPES = [\n\t\"t3.micro\",\n\t\"t3.small\",\n\t\"t3.medium\",\n\t\"t3.large\",\n\t\"t3.xlarge\",\n\t\"t3.2xlarge\",\n\t\"m6i.large\",\n\t\"m6i.xlarge\",\n\t\"m6i.2xlarge\",\n\t\"c6i.large\",\n\t\"c6i.xlarge\",\n\t\"c6i.2xlarge\",\n\t\"r6i.large\",\n\t\"r6i.xlarge\",\n];\n\nconst AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\n\t// Form state\n\tconst [appId, setAppId] = useState(\"\");\n\tconst [instanceCount, setInstanceCount] = useState(1);\n\tconst [instanceType, setInstanceType] = useState(\"t3.micro\");\n\tconst [osType, setOsType] = useState<\"ubuntu\" | \"nixos\">(\"ubuntu\");\n\tconst [vpcId, setVpcId] = useState(\"\");\n\tconst [subnetIds, setSubnetIds] = useState(\"\");\n\tconst [rootVolumeSize, setRootVolumeSize] = useState(\"\");\n\tconst [associatePublicIp, setAssociatePublicIp] = useState(true);\n\tconst [createSg, setCreateSg] = useState(true);\n\tconst [createKeyPair, setCreateKeyPair] = useState(false);\n\tconst [publicKey, setPublicKey] = useState(\"\");\n\tconst [enableIam, setEnableIam] = useState(true);\n\tconst [roles, setRoles] = useState(\"\");\n\tconst [tags, setTags] = useState(\"\");\n\tconst [targetEnv, setTargetEnv] = useState(\"\");\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetAppId(\"\");\n\t\t\tsetInstanceCount(1);\n\t\t\tsetInstanceType(\"t3.micro\");\n\t\t\tsetOsType(\"ubuntu\");\n\t\t\tsetVpcId(\"\");\n\t\t\tsetSubnetIds(\"\");\n\t\t\tsetRootVolumeSize(\"\");\n\t\t\tsetAssociatePublicIp(true);\n\t\t\tsetCreateSg(true);\n\t\t\tsetCreateKeyPair(false);\n\t\t\tsetPublicKey(\"\");\n\t\t\tsetEnableIam(true);\n\t\t\tsetRoles(\"\");\n\t\t\tsetTags(\"\");\n\t\t\tsetTargetEnv(\"\");\n\t\t}\n\t};\n\n\tconst isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = appId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"App ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (ec2.config.apps[id]) {\n\t\t\ttoast.error(`EC2 app \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst appConfig: Ec2AppConfig = {\n\t\t\t\t...DEFAULT_EC2_APP,\n\t\t\t\tinstance_count: instanceCount,\n\t\t\t\tinstance_type: instanceType,\n\t\t\t\tos_type: osType,\n\t\t\t\tvpc_id: vpcId || null,\n\t\t\t\tsubnet_ids: subnetIds\n\t\t\t\t\t? subnetIds.split(\",\").map((s) => s.trim()).filter(Boolean)\n\t\t\t\t\t: [],\n\t\t\t\troot_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null,\n\t\t\t\tassociate_public_ip: associatePublicIp,\n\t\t\t\tsecurity_group: {\n\t\t\t\t\t...DEFAULT_EC2_APP.security_group,\n\t\t\t\t\tcreate: createSg,\n\t\t\t\t},\n\t\t\t\tkey_pair: {\n\t\t\t\t\tcreate: createKeyPair,\n\t\t\t\t\tname: createKeyPair ? `${id}-key` : null,\n\t\t\t\t\tpublic_key: createKeyPair ? publicKey || null : null,\n\t\t\t\t},\n\t\t\t\tiam: {\n\t\t\t\t\tenable: enableIam,\n\t\t\t\t\trole_name: enableIam ? `${id}-ec2-role` : null,\n\t\t\t\t},\n\t\t\t\ttags: {\n\t\t\t\t\tName: id,\n\t\t\t\t\tManagedBy: \"stack-infra\",\n\t\t\t\t},\n\t\t\t\tmachine: {\n\t\t\t\t\t...DEFAULT_EC2_MACHINE_META,\n\t\t\t\t\troles: roles ? roles.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttags: tags ? tags.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttarget_env: targetEnv || null,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait ec2.addApp(id, appConfig);\n\t\t\ttoast.success(`EC2 app \"${id}\" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add EC2 app\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tProvision EC2 Instances\n\t\t\t\t\t\n\t\t\t\t\t\tDefine EC2 instances to provision. Run infra:deploy after to create them.\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t{/* Identity */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setAppId(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tUnique identifier for this group of instances\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Instance sizing */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setInstanceCount(Number.parseInt(e.target.value, 10) || 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Networking */}\n\t\t\t\t\t
\n\t\t\t\t\t\tNetworking\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setVpcId(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setSubnetIds(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Associate a public IPv4 address

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Auto-create with SSH, HTTP, HTTPS ingress

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Storage */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRootVolumeSize(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* SSH Key */}\n\t\t\t\t\t
\n\t\t\t\t\t\tSSH Key Pair\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Import your public key to AWS

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{createKeyPair && (\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setPublicKey(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* IAM */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Create role with SSM + ECR access

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Machine metadata */}\n\t\t\t\t\t
\n\t\t\t\t\t\tMachine Metadata\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setRoles(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setTags(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [53:7] Variable 'AWS_REGIONS' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "const AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog", - "newString": "export function ProvisionInstanceDialog" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.6s) - -Now add the Provision tab to the deploy panel: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 1, - "limit": 15 -} -``` -**Output:** -``` - -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and -00008| * AWS EC2 discovery configuration. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, - -(File has more lines. Use 'offset' parameter to read beyond line 16) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";", - "newString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { ProvisionInstanceDialog } from \"./provision-instance-dialog\";\nimport {\n\tuseMachinesConfig,\n\tuseEc2Provisioning,\n\ttype MachineConfig,\n\ttype Ec2AppConfig,\n} from \"./use-machines\";" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [62:7] Type 'Ec2AppConfig' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.7s) - -Now integrate the provisioning hook and tab into the main component. Let me find the right insertion points: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 280, - "limit": 30 -} -``` -**Output:** -``` - -00281| // ============================================================================= -00282| // Settings: AWS EC2 Discovery Configuration -00283| // ============================================================================= -00284| -00285| function AwsEc2Settings({ machines }: { machines: ReturnType }) { -00286| const cfg = machines.config.aws; -00287| const [saving, setSaving] = useState(false); -00288| const [region, setRegion] = useState(cfg.region ?? ""); -00289| const [sshUser, setSshUser] = useState(cfg.ssh.user); -00290| const [sshPort, setSshPort] = useState(cfg.ssh.port); -00291| const [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? ""); -00292| const [hostPref, setHostPref] = useState(cfg.host_preference.join(", ")); -00293| -00294| const handleSave = async () => { -00295| setSaving(true); -00296| try { -00297| await machines.updateSettings({ -00298| source: "aws-ec2", -00299| enable: true, -00300| aws: { -00301| ...cfg, -00302| region: region || null, -00303| ssh: { -00304| user: sshUser || "root", -00305| port: sshPort || 22, -00306| key_path: sshKeyPath || null, -00307| }, -00308| host_preference: hostPref.split(",").map((s) => s.trim()).filter(Boolean), -00309| }, -00310| }); - -(File has more lines. Use 'offset' parameter to read beyond line 310) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 356, - "limit": 30 -} -``` -**Output:** -``` - -00357| -00358|
-00359| -00360| setSshPort(Number.parseInt(e.target.value, 10) || 22)} -00364| /> -00365|
-00366|
-00367| -00368| setSshKeyPath(e.target.value)} -00372| /> -00373|
-00374| -00375| -00376| -00377| -00381| -00382| -00383| ); -00384| } -00385| -00386| // ============================================================================= - -(File has more lines. Use 'offset' parameter to read beyond line 386) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.9s) - -Now I'll add the EC2 provisioning list component and wire it into the panel: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "// =============================================================================\n// Main Component\n// =============================================================================", - "newString": "// =============================================================================\n// Provisioning: EC2 Instance List\n// =============================================================================\n\nfunction Ec2AppRow({\n\tappId,\n\tapp,\n\tonRemove,\n}: {\n\tappId: string;\n\tapp: Ec2AppConfig;\n\tonRemove: () => void;\n}) {\n\tconst [removing, setRemoving] = useState(false);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appId}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{app.instance_count} x {app.instance_type ?? \"t3.micro\"} · {app.os_type}\n\t\t\t\t\t\t{app.machine.roles.length > 0 && ` · roles: ${app.machine.roles.join(\", \")}`}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{app.security_group.create && (\n\t\t\t\t\tSG\n\t\t\t\t)}\n\t\t\t\t{app.iam.enable && (\n\t\t\t\t\tIAM\n\t\t\t\t)}\n\t\t\t\t{app.key_pair.create && (\n\t\t\t\t\tKey\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{app.associate_public_ip ? \"public\" : \"private\"}\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsetRemoving(true);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait onRemove();\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tsetRemoving(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t\tdisabled={removing}\n\t\t\t\t>\n\t\t\t\t\t{removing ? (\n\t\t\t\t\t\t\n\t\t\t\t\t) : (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -Good, now wire the hook and Provision tab into the DeployPanel component: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);", - "newString": "\tconst machinesConfig = useMachinesConfig();\n\tconst ec2Provisioning = useEc2Provisioning();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. -ERROR [461:8] Variable 'ec2Provisioning' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.2s) - -Now add the Provision tab alongside the other tabs. Let me find the tabs section: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t", - "newString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tProvision\n\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.8s) - -Now add the Provision tab content. Let me find the right place -- after the Machines tab content: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t", - "newString": "\t\t\t\t{/* Provision Tab */}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

EC2 Instances to Provision

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tDefine instance groups here, then run infra:deploy to create them.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Instances Configured

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tClick Provision EC2 to define instance groups.\n\t\t\t\t\t\t\t\t\t\tEach group gets its own security group, IAM role, and optional key pair.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{Object.entries(ec2Provisioning.config.apps).map(([id, app]) => (\n\t\t\t\t\t\t\t\t ec2Provisioning.removeApp(id)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tRun infra:deploy to\n\t\t\t\t\t\t\t\t\t\tprovision these instances. After provisioning, run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:pull-outputs and\n\t\t\t\t\t\t\t\t\t\treload the shell to see them in the Machines tab.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 16.1s) - -No LSP errors in our files. Let me verify: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "cd apps/web && npx tsc --noEmit --pretty 2>&1 | rg \"deploy/\" | head -20", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Typecheck deploy panel components" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.9s) - -Zero type errors in our deploy files. Now update the docs: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "offset": 230, - "limit": 30 -} -``` -**Output:** -``` - -00231| ssh = { -00232| user = "ec2-user"; -00233| port = 22; -00234| keyPath = "~/.ssh/aws_deploy"; -00235| }; -00236| }; -00237| ``` -00238| -00239| ### Filtering by Instance IDs -00240| -00241| Target specific instances instead of using filters: -00242| -00243| ```nix -00244| stack.infra.machines.aws = { -00245| instance-ids = [ -00246| "i-0abc123def456789a" -00247| "i-0def456789abc1230" -00248| ]; -00249| }; -00250| ``` -00251| -00252| ## Studio UI: Deploy Panel -00253| -00254| The Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs: -00255| -00256| ### Machines Tab -00257| -00258| Displays a card grid of all machines in your inventory. Each card shows: -00259| -00260| - Machine name and hostname - -(File has more lines. Use 'offset' parameter to read beyond line 260) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 22.9s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:", - "newString": "## Provisioning EC2 Instances\n\nBeyond discovering existing machines, Stack can **provision new EC2 instances** via the `aws-ec2-app` infra module. This creates instances with auto-resolved AMIs (Ubuntu 24.04 or NixOS), security groups, IAM roles, and key pairs.\n\n### From the Studio UI\n\n1. Open the **Deploy** panel and go to the **Provision** tab\n2. Click **Provision EC2** to open the configuration dialog\n3. Fill in the instance group: app ID, count, instance type, OS, networking, SSH key, IAM\n4. Click **Add to Config** -- this writes to your Nix config\n5. Run `infra:deploy` from the terminal to create the instances\n6. Run `infra:pull-outputs` and reload the shell to see them in the Machines tab\n\n### From Nix Config\n\n```nix\nstackpanel.infra.aws-ec2-app = {\n enable = true;\n\n apps = {\n web-server = {\n instance-count = 2;\n instance-type = \"t3.small\";\n os-type = \"ubuntu\"; # or \"nixos\"\n associate-public-ip = true;\n\n # Auto-creates a security group with SSH + HTTP + HTTPS\n security-group.create = true;\n\n # Auto-creates IAM role with SSM + ECR access\n iam.enable = true;\n\n # Import your SSH public key\n key-pair = {\n create = true;\n public-key = \"ssh-ed25519 AAAA...\";\n };\n\n # Machine metadata for Colmena targeting\n machine = {\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n target-env = \"production\";\n };\n };\n };\n};\n```\n\nThen deploy:\n\n```bash\ninfra:deploy\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\nThe provisioned instances automatically appear in your machine inventory with their public IPs, and can be targeted by Colmena for NixOS deployments.\n\n### What Gets Created\n\nFor each app group, `aws-ec2-app` provisions:\n\n| Resource | Created when |\n|----------|-------------|\n| Security Group | `security-group.create = true` (default) |\n| SSH Key Pair | `key-pair.create = true` |\n| IAM Role + Instance Profile | `iam.enable = true` (default) |\n| EC2 Instances | Always (count from `instance-count`) |\n| ALB + Target Group | `alb.enable = true` |\n| ECR Repository | `ecr.enable = true` |\n| SSM Parameters | `ssm.enable = true` |\n\nVPC and subnets are auto-discovered from your default VPC unless you specify `vpc-id` and `subnet-ids` explicitly.\n\n\nThe `aws-ec2-app` module auto-resolves AMIs. For Ubuntu, it finds the latest Ubuntu 24.04 LTS from Canonical. For NixOS, it finds the latest Determinate Systems NixOS image. You can override with an explicit `ami` value.\n\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has five tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now update the rest of the Deploy panel docs section to reflect the new Provision tab: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "When the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab", - "newString": "Click a machine card to edit its configuration. Use the **Add Machine** button in the header to add static machines.\n\nWhen the inventory is empty, it shows guidance to add machines or configure EC2 discovery.\n\n### Provision Tab\n\nConfigure EC2 instances to create. Each row shows the instance group with its count, type, OS, and what resources will be created (SG, IAM, Key). Click **Provision EC2** to define a new group. After configuring, run `infra:deploy` to create the actual instances.\n\n### App Targets Tab" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.6s) - -Here's a summary of everything that was added: - -## How to Provision Machines - -The Deploy panel now has a **Provision** tab (between Machines and App Targets). The flow is: - -1. **Deploy panel -> Provision tab -> "Provision EC2" button** opens a dialog where you configure: - - App ID (instance group name) - - Count, instance type, OS (Ubuntu 24.04 / NixOS) - - Networking: VPC, subnets (auto-detected if blank), public IP toggle, auto-created security group - - Storage: root volume size - - SSH: optionally create + import a key pair - - IAM: auto-create role with SSM + ECR access - - Machine metadata: roles, tags, environment (for Colmena targeting) - -2. **"Add to Config"** writes to `.stack/config.nix` under `stack.infra.aws-ec2-app` - -3. **`infra:deploy`** in terminal provisions the actual EC2 instances (with SG, IAM, key pair) - -4. **`infra:pull-outputs`** + shell reload populates the Machines tab with the new instances - -## New/Updated Files - -| File | What | -|------|------| -| `deploy/use-machines.ts` | Added `Ec2AppConfig`, `Ec2AppModuleConfig` types + `useEc2Provisioning()` hook for CRUD on `infra.aws-ec2-app` | -| `deploy/provision-instance-dialog.tsx` | New dialog for configuring EC2 instance groups | -| `deploy/deploy-panel.tsx` | Added **Provision** tab with `Ec2AppRow` list, wired `ProvisionInstanceDialog` + `useEc2Provisioning` | -| `docs/deployment/machines.mdx` | Added "Provisioning EC2 Instances" section covering both Studio UI and Nix config approaches, plus the what-gets-created table | - -Zero type errors across all new files (verified with `tsc --noEmit`). - ---- - -## User - -the machine provisioning is great. but we should create some preset based on the user's apps, since it asks for thihngs like roles, environments, number of machines, etc. we could generate buttons that would fill in sane defaults for provisioning - ---- - -## Assistant (Build · claude-opus-4-6 · 178.1s) - -_Thinking:_ - -The user wants preset buttons that auto-fill the provisioning dialog based on their configured apps. For example, if they have a `web` app and a `server` app, we could generate presets like "Web Server (2x t3.small)" or "API Server (1x t3.micro)" that pre-fill the form with sensible defaults. - -Let me look at how apps are structured in the nix config to understand what data we have available, then build presets from that. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore app config shapes for presets", - "prompt": "I need to understand what app data is available in the Studio UI to generate EC2 provisioning presets. Search for:\n\n1. The MachineInfo type and AppDeployMapping type in deploy-panel.tsx - what fields are available\n2. How apps are loaded in the deploy panel - look at useColmenaData and useNixConfig\n3. The app type definitions - what fields does an app have (framework, deployment.host, container config, etc.)\n4. Look at .stack/config.nix apps section to see real app examples with their frameworks, deployment hosts, container types\n5. What app data is available via useNixConfig() - specifically the apps/appsComputed shape\n\nReturn:\n- The full app type shape available from nixConfig (framework, deployment, container fields)\n- The real app configs from .stack/config.nix\n- How to access the app list from within the deploy panel", - "subagent_type": "explore" -} -``` -**Output:** -``` -Now I have everything needed. Here is the comprehensive summary: - ---- - -## Complete Findings - -### 1. MachineInfo Type and AppDeployMapping Type (deploy-panel.tsx) - -**File**: `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` - -**MachineInfo** (lines 69-86): -```typescript -interface MachineInfo { - id: string; - name: string; - host: string | null; - ssh: { - user: string; - port: number; - keyPath: string | null; - }; - tags: string[]; - roles: string[]; - provider: string | null; - arch: string | null; - publicIp: string | null; - privateIp: string | null; - targetEnv: string | null; - labels: Record; -} -``` - -**AppDeployMapping** (lines 88-95): -```typescript -interface AppDeployMapping { - enable: boolean; - targets: string[]; - resolvedTargets: string[]; - role: string | null; - nixosModules: string[]; - system: string | null; -} -``` - -**ColmenaConfig** (lines 97-104): -```typescript -interface ColmenaConfig { - enable: boolean; - machineSource: string; - generateHive: boolean; - config: string; - machineCount: number; - machineIds: string[]; -} -``` - -### 2. How Apps Are Loaded in the Deploy Panel - -**File**: `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` - -The `useColmenaData()` hook (lines 110-144) works as follows: - -1. Calls `useNixConfig()` to get the full evaluated Nix config JSON -2. Extracts `nixConfig.serializable.colmena` as `ColmenaConfig` -3. Extracts `nixConfig.colmena.machinesComputed` as `Record` -4. Extracts apps from **either** `nixConfig.apps` or `nixConfig.appsComputed` (fallback) -5. For each app, reads `app.deploy` and builds the `AppDeployMapping` if `deploy.enable` is true - -The key line accessing app data is: -```typescript -const rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>; -``` - -The deploy panel also uses two additional hooks from `use-machines.ts`: -- `useMachinesConfig()` -- reads from `useNixData("infra")`, extracting `.machines` -- `useEc2Provisioning()` -- reads from `useNixData("infra")`, extracting `.aws_ec2_app` - -### 3. The Full App Type Shape - -There are **three layers** of app data, each progressively richer: - -#### Layer 1: Proto-generated App (base serializable fields) -**File**: `/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/apps.ts` (lines 18-57) - -```typescript -interface App { - name: string; // Display name - description?: string; // Description - path: string; // Relative path to app directory - type?: string; // Runtime: "bun" | "go" | "python" | "rust" | etc. - port?: number; // Development server port - domain?: string; // Local development domain - environments: { // Per-env config (dev, staging, prod) - [key: string]: AppEnvironment; - }; - deploy?: AppDeploy; // Colmena deployment mapping -} - -interface AppDeploy { - enable: boolean; - targets: string[]; - role?: string; - nixos_modules: string[]; - system?: string; - secrets: string[]; -} - -interface AppEnvironment { - name: string; - description?: string; - env: { [key: string]: string }; // env var name -> value or vals reference -} -``` - -#### Layer 2: Nix-specific app options (not in proto, from core/options/apps.nix) -**File**: `/Users/cm/git/darkmatter/stack/nix/stack/core/options/apps.nix` (lines 107-281) - -These are additional options on each app submodule beyond the proto schema: - -```nix -framework = { - tanstack-start.enable # TanStack Start (SSR + Nitro) - nextjs.enable # Next.js - nextjs.output # "standalone" | "export" - vite.enable # Vite SPA - vite.ssr # bool - vite.assets-dir # string (default "dist") - hono.enable # Hono API server - hono.entrypoint # string (default "src/index.ts") - astro.enable # Astro - remix.enable # Remix - nuxt.enable # Nuxt -}; - -deployment = { - enable # bool - host # null | "cloudflare" | "fly" | "vercel" | "aws" - bindings # list of env var names to bind - secrets # subset of bindings that are sensitive -}; - -tooling = { - install / build / test / dev # nullable tool step submodules - build-steps / formatters / linters # lists of tool steps -}; - -offset # nullable int (port offset from base) -tls # bool (TLS for vhost) -packageName # nullable string (NPM package name for turbo) -``` - -#### Layer 3: Module-injected app options (via appModules) -These are added by individual modules: - -**Container options** (`nix/stack/containers/module.nix`, lines 59-111): -```nix -container = { - enable # bool - name # string - version # string - port # int - registry # string - workingDir # string - buildOutputPath # string - maxLayers # int - defaultCopyArgs # list of strings - env # attrs of strings - type # "bun" | "node" | "go" | "static" | "custom" - startupCommand # nullable string or list of strings - copyToRoot # nullable path or list of paths -}; -``` - -**Fly deployment options** (`nix/stack/deployment/fly/module.nix`, lines 59-135): -```nix -deployment.fly = { - appName # string (defaults to app name) - region # string (defaults to global default or "iad") - memory # int - cpus # int - cpuKind # "shared" | "performance" - autoStart # bool - autoStop # "off" | "stop" | "suspend" - minMachines # int - forceHttps # bool - env # attrs of strings -}; - -deployment.container = { - type # "bun" | "node" | "go" | "static" | "custom" - port # int (default 3000) - entrypoint # nullable string -}; -``` - -Other modules also inject options via `appModules` (go, process-compose, oxlint, linting, etc.). - -#### Layer 4: Computed app data (appsComputed) -**File**: `/Users/cm/git/darkmatter/stack/nix/stack/core/options/apps.nix` (lines 358-406) - -```nix -appsComputed. = { - port # int (deterministic from hash) - domain # string | null (e.g., "web.stack.lan") - url # string | null (e.g., "https://web.stack.lan") - tls # bool - offset # int | null - tooling # raw tooling config - wrappedTooling # derivations (only when pkgs available) - deployTargets # list of target machine ids -}; -``` - -### 4. Real App Configs from .stack/config.nix - -**File**: `/Users/cm/git/darkmatter/stack/.stack/config.nix` (lines 15-139) - -Three apps are defined: - -**`docs`** -- Simple documentation site: -```nix -docs = { - description = "Documentation site"; - domain = "docs"; - name = "docs"; - path = "apps/docs"; - tls = true; - type = "bun"; - environments = { dev = {}; prod = {}; staging = {}; }; - linting.oxlint.enable = true; -}; -``` - -**`stack-go`** -- Go CLI/agent: -```nix -stack-go = { - description = "Stack CLI and agent (Go)"; - name = "stack"; - path = "apps/stack-go"; - type = "go"; - go = { enable = true; binaryName = "stack"; generateFiles = false; ldflags = ["-s" "-w"]; }; - environments.dev.env = { STACKPANEL_TEST_PAIRING_TOKEN = "token123"; }; -}; -``` - -**`web`** -- Full-featured app with framework, deployment, container, and linting: -```nix -web = { - description = "Main web application"; - name = "web"; - path = "apps/web"; - domain = "stack"; - tls = true; - type = "bun"; - framework.tanstack-start.enable = true; - container = { enable = true; type = "bun"; }; - deployment = { - enable = true; - host = "fly"; - bindings = ["DATABASE_URL" "CORS_ORIGIN" "BETTER_AUTH_SECRET" "BETTER_AUTH_URL" "POLAR_ACCESS_TOKEN" "POLAR_SUCCESS_URL"]; - secrets = ["DATABASE_URL" "BETTER_AUTH_SECRET" "POLAR_ACCESS_TOKEN"]; - fly = { appName = "stack-web"; region = "iad"; }; - }; - linting.oxlint = { enable = true; fix = true; plugins = ["react" "typescript"]; categories = { correctness = "error"; suspicious = "warn"; }; }; - commands.dev.command = "bun run -F web dev"; - environments = { dev = { env = { MEMO_MEMOAS_AD = "foobar"; }; }; prod = {}; staging = {}; }; -}; -``` - -There is also a real **EC2 provisioning config** at `infra.aws-ec2-app.apps.stack-staging` (lines 349-431) showing a complete EC2 app with `machine.roles = ["docs" "web"]`, instance type, key pair, security group rules, IAM, and tags. - -### 5. How to Access App Data From Within the Deploy Panel - -There are **two distinct data sources** available in the deploy panel: - -#### Source A: `useApps()` -- Proto-serialized app data (CRUD-capable) -```typescript -import { useApps } from "@/lib/use-agent"; -const { data: apps } = useApps(); -// apps: Record -// Each app has: name, path, type, port, domain, description, environments, deploy -``` -This returns the apps from the `data/apps.nix` file via the Go agent's `getApps` RPC call. This is the **user-editable** app data (proto-serialized). It is what `apps-panel-alt.tsx` uses. - -#### Source B: `useNixConfig()` -- Full evaluated Nix config (read-only) -```typescript -import { useNixConfig } from "@/lib/use-agent"; -const { data: nixConfig } = useNixConfig(); -// nixConfig is the parsed JSON of fullConfig from cli.nix -``` - -The `nixConfig` object has these top-level keys (from `cli.nix` lines 119-240): -```typescript -{ - version: 1, - projectName: string, - projectRoot: string, - basePort: number, - processComposePort: number, - paths: { state, gen, data }, - apps: Record, // computed apps - services: Record, - network: { step: { enable, caUrl } }, - ide: { vscode: { ... } }, - motd: { ... }, - packages: Array<{ name, version, attrPath, source }>, - moduleRequirements: { ... }, - missingFlakeInputs: [...], - healthchecks: [...], - ui: { - extensions: { ... }, - modules: { ... }, - modulesList: [...], - panels: { ... }, - panelModules: [...] - } -} -``` - -**For the deploy panel specifically**, `nixConfig.apps` gives you `{ port, domain, url, tls }` per app. The richer fields (framework, deployment, container) are only available in the raw Nix config which gets passed through additional paths: -- `nixConfig.colmena.machinesComputed` -- machine inventory -- `nixConfig.serializable.colmena` -- colmena module config -- The raw `apps`/`appsComputed` (with framework, deployment, container fields) are available from the **full** `nix eval` output when the FlakeWatcher returns the complete config. - -The deploy panel currently accesses apps via `useColmenaData()` which reads `cfg.apps ?? cfg.appsComputed` from the nixConfig, looking specifically at each app's `.deploy` sub-object. To access framework, container, or deployment.host fields from within the deploy panel, you would use the same `nixConfig` and drill into the app objects: - -```typescript -const { data: nixConfig } = useNixConfig(); -const rawApps = (nixConfig?.apps ?? nixConfig?.appsComputed ?? {}) as Record; - -// Access framework info: -const framework = rawApps["web"]?.framework; // { tanstack-start: { enable: true }, ... } - -// Access deployment host: -const host = rawApps["web"]?.deployment?.host; // "fly" | "cloudflare" | "vercel" | "aws" - -// Access container config: -const container = rawApps["web"]?.container; // { enable: true, type: "bun", ... } -``` - -**Important caveat**: The `fullConfig` in `cli.nix` (the JSON that `getNixConfig` returns) only serializes a **subset** of app data (port, domain, url, tls). The richer fields like `framework`, `deployment`, and `container` are part of the full Nix evaluation but may not all be present in the JSON unless the FlakeWatcher's `GetConfig()` returns the full `nix eval .#stackpanelConfig` output rather than the trimmed `fullConfig`. The deploy panel's `useColmenaData()` already accesses `cfg.apps` expecting the richer shape, which suggests the FlakeWatcher returns the complete evaluated config (not the trimmed CLI version). - - -session_id: ses_35635a1b0ffepGzuwZKyYy6NOD - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/.stack/config.nix", - "offset": 15, - "limit": 140 -} -``` -**Output:** -``` - -00016| docs = { -00017| description = "Documentation site"; -00018| domain = "docs"; -00019| environments = { -00020| dev = { -00021| env = { }; -00022| name = "dev"; -00023| }; -00024| prod = { -00025| env = { }; -00026| name = "prod"; -00027| }; -00028| staging = { -00029| env = { }; -00030| name = "staging"; -00031| }; -00032| }; -00033| linting = { -00034| oxlint = { -00035| enable = true; -00036| }; -00037| }; -00038| name = "docs"; -00039| path = "apps/docs"; -00040| tls = true; -00041| type = "bun"; -00042| }; -00043| stack-go = { -00044| description = "Stack CLI and agent (Go)"; -00045| environments = { -00046| dev = { -00047| env = { -00048| STACKPANEL_TEST_PAIRING_TOKEN = "token123"; -00049| }; -00050| name = "dev"; -00051| }; -00052| }; -00053| go = { -00054| binaryName = "stack"; -00055| enable = true; -00056| generateFiles = false; -00057| ldflags = [ -00058| "-s" -00059| "-w" -00060| ]; -00061| }; -00062| name = "stack"; -00063| path = "apps/stack-go"; -00064| type = "go"; -00065| }; -00066| web = { -00067| commands = { -00068| dev = { -00069| command = "bun run -F web dev"; -00070| }; -00071| }; -00072| container = { -00073| enable = true; -00074| type = "bun"; -00075| }; -00076| deployment = { -00077| bindings = [ -00078| "DATABASE_URL" -00079| "CORS_ORIGIN" -00080| "BETTER_AUTH_SECRET" -00081| "BETTER_AUTH_URL" -00082| "POLAR_ACCESS_TOKEN" -00083| "POLAR_SUCCESS_URL" -00084| ]; -00085| enable = true; -00086| fly = { -00087| appName = "stack-web"; -00088| region = "iad"; -00089| }; -00090| host = "fly"; -00091| secrets = [ -00092| "DATABASE_URL" -00093| "BETTER_AUTH_SECRET" -00094| "POLAR_ACCESS_TOKEN" -00095| ]; -00096| }; -00097| description = "Main web application"; -00098| domain = "stack"; -00099| environments = { -00100| dev = { -00101| env = { -00102| MEMO_MEMOAS_AD = "foobar"; -00103| }; -00104| name = "dev"; -00105| }; -00106| prod = { -00107| env = { }; -00108| name = "prod"; -00109| }; -00110| staging = { -00111| env = { }; -00112| name = "staging"; -00113| }; -00114| }; -00115| framework = { -00116| tanstack-start = { -00117| enable = true; -00118| }; -00119| }; -00120| linting = { -00121| oxlint = { -00122| categories = { -00123| correctness = "error"; -00124| suspicious = "warn"; -00125| }; -00126| enable = true; -00127| fix = true; -00128| plugins = [ -00129| "react" -00130| "typescript" -00131| ]; -00132| }; -00133| }; -00134| name = "web"; -00135| path = "apps/web"; -00136| tls = true; -00137| type = "bun"; -00138| }; -00139| }; -00140| -00141| # --------------------------------------------------------------------------- -00142| # AWS -00143| # --------------------------------------------------------------------------- -00144| aws = { -00145| roles-anywhere = { -00146| account-id = "950224716579"; -00147| enable = true; -00148| profile-arn = "arn:aws:rolesanywhere:us-west-2:950224716579:profile/4e72b392-9074-4e53-8cd0-1ba50856d1ca"; -00149| region = "us-west-2"; -00150| role-name = "darkmatter-dev"; -00151| trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:950224716579:trust-anchor/c99a9383-6be1-48ba-8e63-c3ab6b7069cb"; -00152| }; -00153| }; -00154| -00155| # --------------------------------------------------------------------------- - -(File has more lines. Use 'offset' parameter to read beyond line 155) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.8s) - -Now I understand the data shape. Let me read the current provision dialog and the deploy panel to plan the presets approach: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { Button } from "@ui/button"; -00004| import { -00005| Dialog, -00006| DialogContent, -00007| DialogDescription, -00008| DialogFooter, -00009| DialogHeader, -00010| DialogTitle, -00011| } from "@ui/dialog"; -00012| import { Input } from "@ui/input"; -00013| import { Label } from "@ui/label"; -00014| import { -00015| Select, -00016| SelectContent, -00017| SelectItem, -00018| SelectTrigger, -00019| SelectValue, -00020| } from "@ui/select"; -00021| import { Switch } from "@ui/switch"; -00022| import { Loader2, Plus } from "lucide-react"; -00023| import { useState } from "react"; -00024| import { toast } from "sonner"; -00025| import { -00026| type Ec2AppConfig, -00027| DEFAULT_EC2_APP, -00028| DEFAULT_EC2_MACHINE_META, -00029| type useEc2Provisioning, -00030| } from "./use-machines"; -00031| -00032| interface ProvisionInstanceDialogProps { -00033| ec2: ReturnType; -00034| } -00035| -00036| const INSTANCE_TYPES = [ -00037| "t3.micro", -00038| "t3.small", -00039| "t3.medium", -00040| "t3.large", -00041| "t3.xlarge", -00042| "t3.2xlarge", -00043| "m6i.large", -00044| "m6i.xlarge", -00045| "m6i.2xlarge", -00046| "c6i.large", -00047| "c6i.xlarge", -00048| "c6i.2xlarge", -00049| "r6i.large", -00050| "r6i.xlarge", -00051| ]; -00052| -00053| export function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) { -00054| const [open, setOpen] = useState(false); -00055| const [saving, setSaving] = useState(false); -00056| -00057| // Form state -00058| const [appId, setAppId] = useState(""); -00059| const [instanceCount, setInstanceCount] = useState(1); -00060| const [instanceType, setInstanceType] = useState("t3.micro"); -00061| const [osType, setOsType] = useState<"ubuntu" | "nixos">("ubuntu"); -00062| const [vpcId, setVpcId] = useState(""); -00063| const [subnetIds, setSubnetIds] = useState(""); -00064| const [rootVolumeSize, setRootVolumeSize] = useState(""); -00065| const [associatePublicIp, setAssociatePublicIp] = useState(true); -00066| const [createSg, setCreateSg] = useState(true); -00067| const [createKeyPair, setCreateKeyPair] = useState(false); -00068| const [publicKey, setPublicKey] = useState(""); -00069| const [enableIam, setEnableIam] = useState(true); -00070| const [roles, setRoles] = useState(""); -00071| const [tags, setTags] = useState(""); -00072| const [targetEnv, setTargetEnv] = useState(""); -00073| -00074| const handleOpenChange = (isOpen: boolean) => { -00075| setOpen(isOpen); -00076| if (!isOpen) { -00077| setAppId(""); -00078| setInstanceCount(1); -00079| setInstanceType("t3.micro"); -00080| setOsType("ubuntu"); -00081| setVpcId(""); -00082| setSubnetIds(""); -00083| setRootVolumeSize(""); -00084| setAssociatePublicIp(true); -00085| setCreateSg(true); -00086| setCreateKeyPair(false); -00087| setPublicKey(""); -00088| setEnableIam(true); -00089| setRoles(""); -00090| setTags(""); -00091| setTargetEnv(""); -00092| } -00093| }; -00094| -00095| const isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim()); -00096| -00097| const handleSubmit = async () => { -00098| const id = appId.trim(); -00099| if (!id) { -00100| toast.error("App ID is required"); -00101| return; -00102| } -00103| if (ec2.config.apps[id]) { -00104| toast.error(`EC2 app "${id}" already exists`); -00105| return; -00106| } -00107| -00108| setSaving(true); -00109| try { -00110| const appConfig: Ec2AppConfig = { -00111| ...DEFAULT_EC2_APP, -00112| instance_count: instanceCount, -00113| instance_type: instanceType, -00114| os_type: osType, -00115| vpc_id: vpcId || null, -00116| subnet_ids: subnetIds -00117| ? subnetIds.split(",").map((s) => s.trim()).filter(Boolean) -00118| : [], -00119| root_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null, -00120| associate_public_ip: associatePublicIp, -00121| security_group: { -00122| ...DEFAULT_EC2_APP.security_group, -00123| create: createSg, -00124| }, -00125| key_pair: { -00126| create: createKeyPair, -00127| name: createKeyPair ? `${id}-key` : null, -00128| public_key: createKeyPair ? publicKey || null : null, -00129| }, -00130| iam: { -00131| enable: enableIam, -00132| role_name: enableIam ? `${id}-ec2-role` : null, -00133| }, -00134| tags: { -00135| Name: id, -00136| ManagedBy: "stack-infra", -00137| }, -00138| machine: { -00139| ...DEFAULT_EC2_MACHINE_META, -00140| roles: roles ? roles.split(",").map((s) => s.trim()).filter(Boolean) : [], -00141| tags: tags ? tags.split(",").map((s) => s.trim()).filter(Boolean) : [], -00142| target_env: targetEnv || null, -00143| }, -00144| }; -00145| -00146| await ec2.addApp(id, appConfig); -00147| toast.success(`EC2 app "${id}" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`); -00148| handleOpenChange(false); -00149| } catch (err) { -00150| toast.error(err instanceof Error ? err.message : "Failed to add EC2 app"); -00151| } finally { -00152| setSaving(false); -00153| } -00154| }; -00155| -00156| return ( -00157| -00158| -00162| -00163| -00164| Provision EC2 Instances -00165| -00166| Define EC2 instances to provision. Run infra:deploy after to create them. -00167| -00168| -00169| -00170|
-00171| {/* Identity */} -00172|
-00173| -00174| setAppId(e.target.value)} -00178| /> -00179|

-00180| Unique identifier for this group of instances -00181|

-00182|
-00183| -00184| {/* Instance sizing */} -00185|
-00186|
-00187| -00188| setInstanceCount(Number.parseInt(e.target.value, 10) || 1)} -00194| /> -00195|
-00196|
-00197| -00198| -00208|
-00209|
-00210| -00211| -00220|
-00221|
-00222| -00223| {/* Networking */} -00224|
-00225| Networking -00226| -00227|
-00228|
-00229| -00230| setVpcId(e.target.value)} -00234| /> -00235|
-00236|
-00237| -00238| setSubnetIds(e.target.value)} -00242| /> -00243|
-00244|
-00245| -00246|
-00247|
-00248| -00249|

Associate a public IPv4 address

-00250|
-00251| -00252|
-00253| -00254|
-00255|
-00256| -00257|

Auto-create with SSH, HTTP, HTTPS ingress

-00258|
-00259| -00260|
-00261|
-00262| -00263| {/* Storage */} -00264|
-00265| -00266| setRootVolumeSize(e.target.value)} -00270| /> -00271|
-00272| -00273| {/* SSH Key */} -00274|
-00275| SSH Key Pair -00276| -00277|
-00278|
-00279| -00280|

Import your public key to AWS

-00281|
-00282| -00283|
-00284| -00285| {createKeyPair && ( -00286|
-00287| -00288| setPublicKey(e.target.value)} -00292| /> -00293|
-00294| )} -00295|
-00296| -00297| {/* IAM */} -00298|
-00299|
-00300| -00301|

Create role with SSM + ECR access

-00302|
-00303| -00304|
-00305| -00306| {/* Machine metadata */} -00307|
-00308| Machine Metadata -00309| -00310|
-00311|
-00312| -00313| setRoles(e.target.value)} -00317| /> -00318|
-00319|
-00320| -00321| setTags(e.target.value)} -00325| /> -00326|
-00327|
-00328| -00329|
-00330| -00331| -00341|
-00342|
-00343|
-00344| -00345| -00346| -00347| -00351| -00352|
-00353|
-00354| ); -00355| } -00356| - -(End of file - total 356 lines) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 66.8s) - -Now I have a clear picture. I'll add preset generation that reads the user's apps from `useNixConfig()` and creates smart presets. The presets will appear as buttons above the form that fill in the fields. - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "content": "\"use client\";\n\nimport { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport {\n\tCloud,\n\tDatabase,\n\tGlobe,\n\tLoader2,\n\tPlus,\n\tServer,\n\tSparkles,\n\tTerminal,\n} from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { useNixConfig } from \"@/lib/use-agent\";\nimport {\n\ttype Ec2AppConfig,\n\tDEFAULT_EC2_APP,\n\tDEFAULT_EC2_MACHINE_META,\n\ttype useEc2Provisioning,\n} from \"./use-machines\";\n\n// =============================================================================\n// Presets\n// =============================================================================\n\ninterface Preset {\n\tid: string;\n\tlabel: string;\n\tdescription: string;\n\ticon: typeof Server;\n\tappId: string;\n\tinstanceCount: number;\n\tinstanceType: string;\n\tosType: \"ubuntu\" | \"nixos\";\n\troles: string[];\n\ttags: string[];\n\ttargetEnv: string;\n\trootVolumeSize: number | null;\n}\n\n/**\n * Derive presets from the user's configured apps.\n *\n * For each app we look at type, framework, deployment settings, and container\n * config to suggest sane EC2 defaults.\n */\nfunction derivePresets(\n\tnixConfig: Record | null | undefined,\n\texistingAppIds: Set,\n): Preset[] {\n\tif (!nixConfig) return [];\n\n\tconst rawApps = (nixConfig.apps ?? nixConfig.appsComputed ?? {}) as Record<\n\t\tstring,\n\t\tRecord\n\t>;\n\tconst presets: Preset[] = [];\n\n\tfor (const [appName, app] of Object.entries(rawApps)) {\n\t\tconst appType = (app.type as string) ?? \"bun\";\n\t\tconst framework = app.framework as Record> | undefined;\n\t\tconst deployment = app.deployment as Record | undefined;\n\t\tconst container = app.container as Record | undefined;\n\t\tconst description = (app.description as string) ?? appName;\n\n\t\t// Skip apps that already have EC2 provisioning\n\t\tif (existingAppIds.has(appName)) continue;\n\n\t\t// Determine the kind of workload for sizing heuristics\n\t\tconst isGo = appType === \"go\";\n\t\tconst hasContainer = container?.enable === true;\n\t\tconst deployHost = deployment?.host as string | undefined;\n\n\t\t// Detect framework\n\t\tconst isTanStack = framework?.[\"tanstack-start\"]?.enable === true || framework?.[\"tanstack_start\"]?.enable === true;\n\t\tconst isNextjs = framework?.nextjs?.enable === true;\n\t\tconst isVite = framework?.vite?.enable === true;\n\t\tconst isHono = framework?.hono?.enable === true;\n\t\tconst isAstro = framework?.astro?.enable === true;\n\t\tconst isStaticSite = isVite || isAstro;\n\t\tconst isSSR = isTanStack || isNextjs;\n\t\tconst isApi = isHono || isGo;\n\n\t\t// Skip apps already deployed to non-EC2 hosts (cloudflare, fly)\n\t\t// but still offer them if deployment is disabled or host is null/aws\n\t\tif (deployHost && deployHost !== \"aws\" && deployment?.enable === true) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Build preset\n\t\tlet icon = Server;\n\t\tlet instanceType = \"t3.micro\";\n\t\tlet instanceCount = 1;\n\t\tlet roles: string[] = [appName];\n\t\tlet rootVolumeSize: number | null = null;\n\n\t\tif (isSSR) {\n\t\t\t// SSR frameworks need more RAM for rendering\n\t\t\ticon = Globe;\n\t\t\tinstanceType = \"t3.small\";\n\t\t\tinstanceCount = 2;\n\t\t\troles = [appName, \"web\"];\n\t\t\trootVolumeSize = 20;\n\t\t} else if (isApi) {\n\t\t\ticon = Terminal;\n\t\t\tinstanceType = isGo ? \"t3.micro\" : \"t3.small\";\n\t\t\tinstanceCount = 2;\n\t\t\troles = [appName, \"api\"];\n\t\t} else if (isStaticSite) {\n\t\t\ticon = Cloud;\n\t\t\tinstanceType = \"t3.micro\";\n\t\t\tinstanceCount = 1;\n\t\t\troles = [appName, \"web\"];\n\t\t} else if (hasContainer) {\n\t\t\ticon = Database;\n\t\t\tinstanceType = \"t3.small\";\n\t\t\tinstanceCount = 1;\n\t\t\troles = [appName];\n\t\t\trootVolumeSize = 30;\n\t\t} else {\n\t\t\t// Generic app\n\t\t\tinstanceType = \"t3.micro\";\n\t\t\troles = [appName];\n\t\t}\n\n\t\tconst frameworkLabel = isTanStack\n\t\t\t? \"TanStack Start\"\n\t\t\t: isNextjs\n\t\t\t\t? \"Next.js\"\n\t\t\t\t: isHono\n\t\t\t\t\t? \"Hono\"\n\t\t\t\t\t: isVite\n\t\t\t\t\t\t? \"Vite\"\n\t\t\t\t\t\t: isAstro\n\t\t\t\t\t\t\t? \"Astro\"\n\t\t\t\t\t\t\t: isGo\n\t\t\t\t\t\t\t\t? \"Go\"\n\t\t\t\t\t\t\t\t: appType;\n\n\t\tpresets.push({\n\t\t\tid: appName,\n\t\t\tlabel: appName,\n\t\t\tdescription: `${frameworkLabel} · ${instanceCount}x ${instanceType}`,\n\t\t\ticon,\n\t\t\tappId: appName,\n\t\t\tinstanceCount,\n\t\t\tinstanceType,\n\t\t\tosType: \"ubuntu\",\n\t\t\troles,\n\t\t\ttags: [],\n\t\t\ttargetEnv: \"production\",\n\t\t\trootVolumeSize,\n\t\t});\n\t}\n\n\t// Add generic presets if the user has few apps\n\tif (!existingAppIds.has(\"database\")) {\n\t\tpresets.push({\n\t\t\tid: \"_preset_database\",\n\t\t\tlabel: \"Database Server\",\n\t\t\tdescription: \"PostgreSQL/Redis · 1x t3.medium · 50GB\",\n\t\t\ticon: Database,\n\t\t\tappId: \"database\",\n\t\t\tinstanceCount: 1,\n\t\t\tinstanceType: \"t3.medium\",\n\t\t\tosType: \"ubuntu\",\n\t\t\troles: [\"database\"],\n\t\t\ttags: [],\n\t\t\ttargetEnv: \"production\",\n\t\t\trootVolumeSize: 50,\n\t\t});\n\t}\n\n\treturn presets;\n}\n\n// =============================================================================\n// Dialog\n// =============================================================================\n\ninterface ProvisionInstanceDialogProps {\n\tec2: ReturnType;\n}\n\nconst INSTANCE_TYPES = [\n\t\"t3.micro\",\n\t\"t3.small\",\n\t\"t3.medium\",\n\t\"t3.large\",\n\t\"t3.xlarge\",\n\t\"t3.2xlarge\",\n\t\"m6i.large\",\n\t\"m6i.xlarge\",\n\t\"m6i.2xlarge\",\n\t\"c6i.large\",\n\t\"c6i.xlarge\",\n\t\"c6i.2xlarge\",\n\t\"r6i.large\",\n\t\"r6i.xlarge\",\n];\n\nexport function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) {\n\tconst { data: nixConfig } = useNixConfig();\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\n\t// Form state\n\tconst [appId, setAppId] = useState(\"\");\n\tconst [instanceCount, setInstanceCount] = useState(1);\n\tconst [instanceType, setInstanceType] = useState(\"t3.micro\");\n\tconst [osType, setOsType] = useState<\"ubuntu\" | \"nixos\">(\"ubuntu\");\n\tconst [vpcId, setVpcId] = useState(\"\");\n\tconst [subnetIds, setSubnetIds] = useState(\"\");\n\tconst [rootVolumeSize, setRootVolumeSize] = useState(\"\");\n\tconst [associatePublicIp, setAssociatePublicIp] = useState(true);\n\tconst [createSg, setCreateSg] = useState(true);\n\tconst [createKeyPair, setCreateKeyPair] = useState(false);\n\tconst [publicKey, setPublicKey] = useState(\"\");\n\tconst [enableIam, setEnableIam] = useState(true);\n\tconst [roles, setRoles] = useState(\"\");\n\tconst [tags, setTags] = useState(\"\");\n\tconst [targetEnv, setTargetEnv] = useState(\"\");\n\n\tconst existingAppIds = useMemo(\n\t\t() => new Set(Object.keys(ec2.config.apps)),\n\t\t[ec2.config.apps],\n\t);\n\n\tconst presets = useMemo(\n\t\t() => derivePresets(nixConfig as Record | null, existingAppIds),\n\t\t[nixConfig, existingAppIds],\n\t);\n\n\tconst applyPreset = (preset: Preset) => {\n\t\tsetAppId(preset.appId);\n\t\tsetInstanceCount(preset.instanceCount);\n\t\tsetInstanceType(preset.instanceType);\n\t\tsetOsType(preset.osType);\n\t\tsetRoles(preset.roles.join(\", \"));\n\t\tsetTags(preset.tags.join(\", \"));\n\t\tsetTargetEnv(preset.targetEnv);\n\t\tsetRootVolumeSize(preset.rootVolumeSize?.toString() ?? \"\");\n\t\t// Keep networking/SSH/IAM at their current (default) values\n\t};\n\n\tconst resetForm = () => {\n\t\tsetAppId(\"\");\n\t\tsetInstanceCount(1);\n\t\tsetInstanceType(\"t3.micro\");\n\t\tsetOsType(\"ubuntu\");\n\t\tsetVpcId(\"\");\n\t\tsetSubnetIds(\"\");\n\t\tsetRootVolumeSize(\"\");\n\t\tsetAssociatePublicIp(true);\n\t\tsetCreateSg(true);\n\t\tsetCreateKeyPair(false);\n\t\tsetPublicKey(\"\");\n\t\tsetEnableIam(true);\n\t\tsetRoles(\"\");\n\t\tsetTags(\"\");\n\t\tsetTargetEnv(\"\");\n\t};\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) resetForm();\n\t};\n\n\tconst isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = appId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"App ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (ec2.config.apps[id]) {\n\t\t\ttoast.error(`EC2 app \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst appConfig: Ec2AppConfig = {\n\t\t\t\t...DEFAULT_EC2_APP,\n\t\t\t\tinstance_count: instanceCount,\n\t\t\t\tinstance_type: instanceType,\n\t\t\t\tos_type: osType,\n\t\t\t\tvpc_id: vpcId || null,\n\t\t\t\tsubnet_ids: subnetIds\n\t\t\t\t\t? subnetIds.split(\",\").map((s) => s.trim()).filter(Boolean)\n\t\t\t\t\t: [],\n\t\t\t\troot_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null,\n\t\t\t\tassociate_public_ip: associatePublicIp,\n\t\t\t\tsecurity_group: {\n\t\t\t\t\t...DEFAULT_EC2_APP.security_group,\n\t\t\t\t\tcreate: createSg,\n\t\t\t\t},\n\t\t\t\tkey_pair: {\n\t\t\t\t\tcreate: createKeyPair,\n\t\t\t\t\tname: createKeyPair ? `${id}-key` : null,\n\t\t\t\t\tpublic_key: createKeyPair ? publicKey || null : null,\n\t\t\t\t},\n\t\t\t\tiam: {\n\t\t\t\t\tenable: enableIam,\n\t\t\t\t\trole_name: enableIam ? `${id}-ec2-role` : null,\n\t\t\t\t},\n\t\t\t\ttags: {\n\t\t\t\t\tName: id,\n\t\t\t\t\tManagedBy: \"stack-infra\",\n\t\t\t\t},\n\t\t\t\tmachine: {\n\t\t\t\t\t...DEFAULT_EC2_MACHINE_META,\n\t\t\t\t\troles: roles ? roles.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttags: tags ? tags.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttarget_env: targetEnv || null,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait ec2.addApp(id, appConfig);\n\t\t\ttoast.success(`EC2 app \"${id}\" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add EC2 app\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tProvision EC2 Instances\n\t\t\t\t\t\n\t\t\t\t\t\tDefine EC2 instances to provision. Run infra:deploy after to create them.\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t{/* Presets */}\n\t\t\t\t\t{presets.length > 0 && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tPresets from your apps\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{presets.map((preset) => {\n\t\t\t\t\t\t\t\t\tconst Icon = preset.icon;\n\t\t\t\t\t\t\t\t\tconst isActive = appId === preset.appId;\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t applyPreset(preset)}\n\t\t\t\t\t\t\t\t\t\t\tclassName={`\n\t\t\t\t\t\t\t\t\t\t\t\tinline-flex items-center gap-2 rounded-lg border px-3 py-2\n\t\t\t\t\t\t\t\t\t\t\t\ttext-left text-sm transition-colors cursor-pointer\n\t\t\t\t\t\t\t\t\t\t\t\t${isActive\n\t\t\t\t\t\t\t\t\t\t\t\t\t? \"border-primary bg-primary/5 text-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"border-border bg-card text-foreground hover:border-primary/40 hover:bg-secondary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t
{preset.label}
\n\t\t\t\t\t\t\t\t\t\t\t\t
{preset.description}
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Identity */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setAppId(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tUnique identifier for this group of instances\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Instance sizing */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setInstanceCount(Number.parseInt(e.target.value, 10) || 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Networking */}\n\t\t\t\t\t
\n\t\t\t\t\t\tNetworking\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setVpcId(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setSubnetIds(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Associate a public IPv4 address

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Auto-create with SSH, HTTP, HTTPS ingress

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Storage */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRootVolumeSize(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* SSH Key */}\n\t\t\t\t\t
\n\t\t\t\t\t\tSSH Key Pair\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Import your public key to AWS

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{createKeyPair && (\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setPublicKey(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* IAM */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Create role with SSM + ECR access

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Machine metadata */}\n\t\t\t\t\t
\n\t\t\t\t\t\tMachine Metadata\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setRoles(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setTags(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [3:10] Identifier 'Badge' is imported but never used. -help: Consider removing this import. -ERROR [85:9] Variable 'description' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "import { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";", - "newString": "import { Button } from \"@ui/button\";" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [84:9] Variable 'description' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.7s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "\t\tconst description = (app.description as string) ?? appName;\n\n\t\t// Determine the kind of workload for sizing heuristics", - "newString": "\t\t// Determine the kind of workload for sizing heuristics" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 18.1s) - -Zero LSP errors in our files now. Let me do one final typecheck: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "cd apps/web && npx tsc --noEmit --pretty 2>&1 | rg \"deploy/\" | head -20", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Typecheck deploy components for errors" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 21.4s) - -Clean. Here's what was built: - -## Preset System - -The provisioning dialog now generates smart preset buttons from your configured apps. When you open "Provision EC2", you'll see buttons like: - -``` -[web] [docs] [database] -TanStack Start Vite Database Server -2x t3.small 1x t3.micro 1x t3.medium · 50GB -``` - -Clicking a preset fills in the entire form with sane defaults derived from your app's characteristics. - -### How presets are derived - -The `derivePresets()` function in `provision-instance-dialog.tsx:66` reads the user's apps from `useNixConfig()` and applies heuristics: - -| App characteristic | Defaults applied | -|---|---| -| **SSR framework** (TanStack Start, Next.js) | 2x `t3.small`, 20GB root, roles: `[appName, "web"]` | -| **API framework** (Hono, Go) | 2x `t3.micro`/`t3.small`, roles: `[appName, "api"]` | -| **Static site** (Vite, Astro) | 1x `t3.micro`, roles: `[appName, "web"]` | -| **Container app** (no framework detected) | 1x `t3.small`, 30GB root | -| **Generic** | 1x `t3.micro` | - -### Filtering logic - -- Apps already in `ec2.config.apps` are excluded (no duplicate presets) -- Apps deployed to non-EC2 hosts (Cloudflare, Fly) with deployment enabled are skipped -- A generic "Database Server" preset is always included (unless one named `database` exists) - -### UX - -- Preset buttons appear at the top of the dialog with icons and sizing summaries -- The active preset is highlighted with a primary border -- All fields remain editable after applying a preset -- it's just a starting point -- Networking, SSH, and IAM keep their defaults (auto VPC, create SG, enable IAM) - ---- - -## User - -does it make sense to have a module system for alchemy, similar to how we have a module system for apps? that way developers can contribute alchemy modules? - ---- - -## Assistant (Build · claude-opus-4-6 · 149.3s) - -_Thinking:_ - -The user is asking about creating a module system for Alchemy (the infrastructure provisioning framework), similar to how Stack has an app module system. Let me think about this carefully before responding. - -Currently, the infra module system already exists in `nix/stack/infra/` - there are 11 infra modules (aws-secrets, deployment, database, cache, machines, aws-network, aws-security-groups, aws-key-pairs, aws-iam, aws-ec2, aws-ec2-app). Each module has: -- A `module.nix` (Nix options + config) -- An `index.ts` (Alchemy TypeScript that provisions resources) - - -- Registration into `stack.infra.modules.` - -The codegen engine already handles auto-generating the orchestrator, merging dependencies, copying module files, and supporting both single-file and directory-based modules. So a module system does exist, but the real question is whether to formalize it further to make it easy for external developers to contribute Alchemy modules, similar to how devenv has community modules or Terraform has providers. - -Looking at what's already there versus what would need to change: the registry, typed inputs/outputs, and codegen all exist, but there's no discovery mechanism, standard package format, versioning, or testing infrastructure. The TypeScript modules are also tightly coupled to the codegen with specific import paths and the Infra class. The practical question is whether this level of formalization actually makes sense when the current system already lets people add modules by dropping files into the nix directory. - -The module interface itself is well-defined—each needs a Nix options file, TypeScript implementation, declared dependencies, and outputs—which is exactly what the system already supports. The real bottleneck isn't the architecture but how external modules get discovered and loaded. Rather than building something entirely new, the honest path forward is making the existing foundation more accessible to contributors. - -You already have one. The `stack.infra.modules` registry in `nix/stack/infra/options.nix` + the codegen engine in `codegen.nix` is a module system -- each module declares inputs, outputs, dependencies, and a TypeScript implementation, and the codegen wires them into a single deployable package. - -The 11 built-in modules (`aws-secrets`, `deployment`, `database`, `cache`, `machines`, `aws-network`, `aws-security-groups`, `aws-key-pairs`, `aws-iam`, `aws-ec2`, `aws-ec2-app`) all use this system already. - -What's missing is the **external** story. Right now a contributor would have to: - -1. Create a Nix module under `nix/stack/infra/modules//module.nix` -2. Create a TypeScript implementation in the same directory -3. Import it in `nix/stack/infra/default.nix` -4. Hope their module's dependencies don't conflict - -That works for first-party modules but not for community distribution. - -Before building more infrastructure around this, a few things worth considering: - -**What the current system handles well:** -- Input/output contracts between modules -- Dependency declaration (npm packages) -- Code generation (single-file and directory modules, as of our recent change) -- Output syncing to storage backends -- TypeScript type generation from Nix inputs - -**What's actually missing for community modules:** -- No way to load a module from a flake input or external path (modules must live inside the stack source tree) -- No way for users to register modules from their own `.stack/` config -- No isolation between module Alchemy resources (namespace collisions possible) - -**What I'd question building:** -- A package registry / discovery service (premature -- you'd need significant adoption first) -- Versioned module resolution (npm already handles the TypeScript dependency side) -- A module testing framework (each module is just an Alchemy script -- standard testing applies) - -``` -Tool: question - -**Input:** -```json -{ - "questions": [ - { - "question": "Which direction do you want to take this? The simplest high-value change would be letting users register external infra modules from their own config or flake inputs, without touching the stack source tree.", - "header": "Module system scope", - "options": [ - { - "label": "External module loading", - "description": "Let users register infra modules from flake inputs or local paths in their .stack/config.nix, so community modules can be consumed without forking stack" - }, - { - "label": "Module scaffold + docs", - "description": "Create a scaffold command (stack infra:new-module) and contributor docs that make it easy to write and share modules using the existing system" - }, - { - "label": "Both", - "description": "External loading + scaffold tooling + docs" - } - ] - } - ] -} -``` -**Output:** -``` -User has answered your questions: "Which direction do you want to take this? The simplest high-value change would be letting users register external infra modules from their own config or flake inputs, without touching the stack source tree."="Module scaffold + docs". You can now continue with the user's answers in mind. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.1s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Implement stack infra:new-module CLI command or script", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Implement stack infra:new-module CLI command or script", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.4s) - -Let me study the existing modules more closely to nail down the canonical shape, especially the simplest vs most complex examples: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/aws-key-pairs/module.nix -00003| # -00004| # AWS Key Pair provisioning module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| cfg = config.stack.infra.aws-key-pairs; -00013| -00014| keyType = lib.types.submodule { -00015| options = { -00016| public-key = lib.mkOption { -00017| type = lib.types.str; -00018| description = "Public key material for the key pair."; -00019| }; -00020| -00021| tags = lib.mkOption { -00022| type = lib.types.attrsOf lib.types.str; -00023| default = { }; -00024| description = "Tags applied to the key pair."; -00025| }; -00026| -00027| destroy-on-delete = lib.mkOption { -00028| type = lib.types.bool; -00029| default = false; -00030| description = "Delete the key pair when running alchemy destroy."; -00031| }; -00032| }; -00033| }; -00034| in -00035| { -00036| options.stack.infra.aws-key-pairs = { -00037| enable = lib.mkOption { -00038| type = lib.types.bool; -00039| default = false; -00040| description = "Enable AWS key pair provisioning."; -00041| }; -00042| -00043| keys = lib.mkOption { -00044| type = lib.types.attrsOf keyType; -00045| default = { }; -00046| description = "Key pair definitions keyed by key name."; -00047| }; -00048| -00049| sync-outputs = lib.mkOption { -00050| type = lib.types.listOf lib.types.str; -00051| default = [ "keyNames" "keyPairIds" ]; -00052| description = "Which outputs to sync to the storage backend."; -00053| }; -00054| }; -00055| -00056| config = lib.mkIf cfg.enable { -00057| stack.infra.enable = lib.mkDefault true; -00058| -00059| stack.infra.modules.aws-key-pairs = { -00060| name = "AWS Key Pairs"; -00061| description = "Import or create EC2 key pairs"; -00062| path = ./index.ts; -00063| inputs = { -00064| keys = cfg.keys; -00065| }; -00066| dependencies = { -00067| "@aws-sdk/client-ec2" = "catalog:"; -00068| }; -00069| outputs = -00070| let -00071| mkOutput = key: desc: { -00072| description = desc; -00073| sensitive = false; -00074| sync = builtins.elem key cfg.sync-outputs; -00075| }; -00076| in -00077| { -00078| keyNames = mkOutput "keyNames" "Key pair names (JSON)"; -00079| keyPairIds = mkOutput "keyPairIds" "Key pair IDs (JSON)"; -00080| }; -00081| }; -00082| }; -00083| } -00084| - -(End of file - total 84 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/index.ts" -} -``` -**Output:** -``` - -00001| // ============================================================================== -00002| // AWS Key Pairs Module -00003| // ============================================================================== -00004| import Infra from "@stack/infra"; -00005| import { KeyPair } from "@stack/infra/resources/key-pair"; -00006| -00007| interface KeyPairInput { -00008| publicKey: string; -00009| tags?: Record; -00010| destroyOnDelete?: boolean; -00011| } -00012| -00013| interface AwsKeyPairsInputs { -00014| keys: Record; -00015| } -00016| -00017| const infra = new Infra("aws-key-pairs"); -00018| const inputs = infra.inputs( -00019| process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES, -00020| ); -00021| -00022| const keyNames: string[] = []; -00023| const keyPairIds: Record = {}; -00024| -00025| for (const [keyName, key] of Object.entries(inputs.keys ?? {})) { -00026| const result = await KeyPair(infra.id(keyName), { -00027| keyName, -00028| publicKey: key.publicKey, -00029| tags: key.tags, -00030| destroyOnDelete: key.destroyOnDelete, -00031| }); -00032| keyNames.push(result.keyName); -00033| if (result.keyPairId) { -00034| keyPairIds[result.keyName] = result.keyPairId; -00035| } -00036| } -00037| -00038| export default { -00039| keyNames: JSON.stringify(keyNames), -00040| keyPairIds: JSON.stringify(keyPairIds), -00041| }; -00042| - -(End of file - total 42 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/cache/module.nix -00003| # -00004| # Cache Infrastructure Module -00005| # -00006| # Provisions cache/Redis resources based on environment: -00007| # - Production/CI: Upstash Redis (serverless, REST API) -00008| # - Local (devenv): Devenv-managed Redis (read-only reference) -00009| # - Local (docker): Docker Valkey/Redis container (fallback) -00010| # -00011| # Usage in .stack/config.nix: -00012| # stack.infra.cache = { -00013| # enable = true; -00014| # provider = "auto"; -00015| # upstash = { -00016| # region = "us-east-1"; -00017| # api-key-ssm-path = "/common/upstash-api-key"; -00018| # email-ssm-path = "/common/upstash-email"; -00019| # }; -00020| # }; -00021| # ============================================================================== -00022| { -00023| lib, -00024| config, -00025| ... -00026| }: -00027| let -00028| cfg = config.stack.infra.cache; -00029| projectName = config.stack.name or "my-project"; -00030| infraCfg = config.stack.infra; -00031| in -00032| { -00033| # ============================================================================ -00034| # Options -00035| # ============================================================================ -00036| options.stack.infra.cache = { -00037| enable = lib.mkOption { -00038| type = lib.types.bool; -00039| default = false; -00040| description = "Enable cache infrastructure provisioning"; -00041| }; -00042| -00043| provider = lib.mkOption { -00044| type = lib.types.enum [ -00045| "auto" -00046| "upstash" -00047| "devenv" -00048| "docker" -00049| ]; -00050| default = "auto"; -00051| description = '' -00052| Cache provider. -00053| -00054| - auto: Detect environment at runtime. Uses devenv if IN_NIX_SHELL is set, -00055| docker if USE_DOCKER=true, otherwise Upstash. -00056| - upstash: Always use Upstash Redis. -00057| - devenv: Always reference devenv-managed Redis. -00058| - docker: Always use Docker Valkey container. -00059| ''; -00060| }; -00061| -00062| # -------------------------------------------------------------------------- -00063| # Upstash configuration -00064| # -------------------------------------------------------------------------- -00065| upstash = { -00066| region = lib.mkOption { -00067| type = lib.types.str; -00068| default = "us-east-1"; -00069| description = "Upstash Redis region"; -00070| }; -00071| -00072| api-key-ssm-path = lib.mkOption { -00073| type = lib.types.str; -00074| default = "/common/upstash-api-key"; -00075| description = "SSM parameter path for the Upstash API key"; -00076| }; -00077| -00078| email-ssm-path = lib.mkOption { -00079| type = lib.types.str; -00080| default = "/common/upstash-email"; -00081| description = "SSM parameter path for the Upstash account email"; -00082| }; -00083| }; -00084| -00085| # -------------------------------------------------------------------------- -00086| # Devenv configuration -00087| # -------------------------------------------------------------------------- -00088| devenv = { -00089| host = lib.mkOption { -00090| type = lib.types.str; -00091| default = "localhost"; -00092| description = "Redis host in devenv"; -00093| }; -00094| -00095| port = lib.mkOption { -00096| type = lib.types.int; -00097| default = 6379; -00098| description = "Redis port in devenv"; -00099| }; -00100| }; -00101| -00102| # -------------------------------------------------------------------------- -00103| # Docker configuration -00104| # -------------------------------------------------------------------------- -00105| docker = { -00106| image = lib.mkOption { -00107| type = lib.types.str; -00108| default = "valkey/valkey"; -00109| description = "Docker image for Redis/Valkey"; -00110| }; -00111| -00112| tag = lib.mkOption { -00113| type = lib.types.str; -00114| default = "latest"; -00115| description = "Docker image tag"; -00116| }; -00117| -00118| port = lib.mkOption { -00119| type = lib.types.int; -00120| default = 6379; -00121| description = "Host port mapping for Docker Redis"; -00122| }; -00123| -00124| network = lib.mkOption { -00125| type = lib.types.str; -00126| default = "${projectName}_network"; -00127| description = "Docker network name"; -00128| }; -00129| }; -00130| -00131| # -------------------------------------------------------------------------- -00132| # Output configuration -00133| # -------------------------------------------------------------------------- -00134| ssm = { -00135| enable = lib.mkOption { -00136| type = lib.types.bool; -00137| default = true; -00138| description = "Write Redis URL and token to SSM Parameter Store"; -00139| }; -00140| -00141| path-prefix = lib.mkOption { -00142| type = lib.types.str; -00143| default = "/${projectName}"; -00144| description = "SSM path prefix (stage is appended)"; -00145| }; -00146| }; -00147| }; -00148| -00149| # ============================================================================ -00150| # Config: register in infra module system -00151| # ============================================================================ -00152| config = lib.mkIf (infraCfg.enable && cfg.enable) { -00153| stack.infra.modules.cache = { -00154| name = "Cache"; -00155| description = "Redis/Valkey cache provisioning (Upstash / devenv / Docker)"; -00156| path = ./index.ts; -00157| inputs = { -00158| inherit projectName; -00159| inherit (cfg) provider; -00160| upstash = { -00161| inherit (cfg.upstash) region; -00162| apiKeySsmPath = cfg.upstash.api-key-ssm-path; -00163| emailSsmPath = cfg.upstash.email-ssm-path; -00164| }; -00165| devenv = { -00166| inherit (cfg.devenv) host port; -00167| }; -00168| docker = { -00169| inherit (cfg.docker) image tag port network; -00170| }; -00171| ssm = { -00172| inherit (cfg.ssm) enable; -00173| pathPrefix = cfg.ssm.path-prefix; -00174| }; -00175| }; -00176| dependencies = { -00177| "alchemy" = "catalog:"; -00178| } -00179| // lib.optionalAttrs (config.stack.alchemy.enable) { -00180| ${config.stack.alchemy.package.name} = "workspace:*"; -00181| }; -00182| outputs = { -00183| redisUrl = { -00184| description = "Redis connection URL (endpoint)"; -00185| sync = true; -00186| }; -00187| redisToken = { -00188| description = "Redis REST token (Upstash only)"; -00189| sensitive = true; -00190| sync = true; -00191| }; -00192| provider = { -00193| description = "Active cache provider (upstash, devenv, docker)"; -00194| sync = true; -00195| }; -00196| }; -00197| }; -00198| }; -00199| } -00200| - -(End of file - total 200 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.2s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix", - "offset": 1, - "limit": 120 -} -``` -**Output:** -``` - -00002| # infra/options.nix -00003| # -00004| # Core options for the infrastructure module system. -00005| # -00006| # Defines: -00007| # - stack.infra.enable, framework, output-dir, key-format -00008| # - stack.infra.storage-backend (chamber, sops, ssm, none) -00009| # - stack.infra.modules (internal registry) -00010| # - stack.infra.package (generated package.json config) -00011| # - stack.infra.outputs (stub for cross-resource references) -00012| # ============================================================================== -00013| { -00014| lib, -00015| config, -00016| ... -00017| }: -00018| let -00019| cfg = config.stack.infra; -00020| projectName = config.stack.name or "my-project"; -00021| -00022| # ============================================================================ -00023| # Alchemy peer dependency mapping -00024| # -00025| # Maps alchemy resource imports to their required npm peer dependencies. -00026| # Used for validation to ensure all required dependencies are declared. -00027| # ============================================================================ -00028| -00029| # ============================================================================ -00030| # Submodule: output declaration (used in module registry) -00031| # ============================================================================ -00032| outputDeclType = lib.types.submodule { -00033| options = { -00034| description = lib.mkOption { -00035| type = lib.types.str; -00036| default = ""; -00037| description = "Human-readable description of this output"; -00038| }; -00039| -00040| sensitive = lib.mkOption { -00041| type = lib.types.bool; -00042| default = false; -00043| description = "Whether this output contains sensitive data"; -00044| }; -00045| -00046| sync = lib.mkOption { -00047| type = lib.types.bool; -00048| default = false; -00049| description = "Whether to sync this output to the storage backend"; -00050| }; -00051| }; -00052| }; -00053| -00054| # ============================================================================ -00055| # Submodule: infra module registry entry -00056| # ============================================================================ -00057| infraModuleType = lib.types.submodule { -00058| options = { -00059| name = lib.mkOption { -00060| type = lib.types.str; -00061| description = "Human-readable name of this infra module"; -00062| }; -00063| -00064| description = lib.mkOption { -00065| type = lib.types.str; -00066| default = ""; -00067| description = "Description of what this module provisions"; -00068| }; -00069| -00070| path = lib.mkOption { -00071| type = lib.types.path; -00072| description = '' -00073| Path to the TypeScript module file. -00074| Must default-export a Record of outputs. -00075| Use `import Infra from "@stack/infra"` for the library. -00076| ''; -00077| }; -00078| -00079| inputs = lib.mkOption { -00080| type = lib.types.attrsOf lib.types.anything; -00081| default = { }; -00082| description = '' -00083| Configuration values passed to the module at runtime. -00084| Serialized to JSON in .stack/state/infra-inputs.json. -00085| Values matching ENC[age,...] are decrypted at runtime. -00086| ''; -00087| }; -00088| -00089| dependencies = lib.mkOption { -00090| type = lib.types.attrsOf lib.types.str; -00091| default = { }; -00092| description = '' -00093| NPM dependencies this module requires. -00094| Merged into the infra package.json dependencies. -00095| -00096| For external flake modules, prefer providing bunDeps instead -00097| for pre-validated, reproducible dependencies. -00098| ''; -00099| }; -00100| -00101| bunDeps = lib.mkOption { -00102| type = lib.types.nullOr lib.types.package; -00103| default = null; -00104| description = '' -00105| Pre-fetched Bun dependency cache from bun2nix.fetchBunDeps. -00106| -00107| External flakes should provide this for reproducible builds. -00108| When provided, these deps are validated at Nix eval time and -00109| merged with other modules' deps. -00110| -00111| Example (in a flake providing an infra module): -00112| bunDeps = bun2nix.fetchBunDeps { -00113| bunNix = ./bun.nix; -00114| }; -00115| -00116| The flake should also ship package.json, bun.lock, and bun.nix -00117| alongside the module. -00118| ''; -00119| }; -00120| -00121| outputs = lib.mkOption { - -(File has more lines. Use 'offset' parameter to read beyond line 121) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix", - "offset": 121, - "limit": 100 -} -``` -**Output:** -``` - -00122| type = lib.types.attrsOf outputDeclType; -00123| default = { }; -00124| description = '' -00125| Output declarations for this module. -00126| Keys must match the keys of the default export from the TypeScript file. -00127| Only outputs with sync=true are written to the storage backend. -00128| ''; -00129| }; -00130| }; -00131| }; -00132| -00133| in -00134| { -00135| # ============================================================================ -00136| # Options -00137| # ============================================================================ -00138| options.stack.infra = { -00139| enable = lib.mkOption { -00140| type = lib.types.bool; -00141| default = false; -00142| description = "Enable the infrastructure module system"; -00143| }; -00144| -00145| framework = lib.mkOption { -00146| type = lib.types.enum [ "alchemy" ]; -00147| default = "alchemy"; -00148| description = '' -00149| IaC framework to use for infrastructure provisioning. -00150| Currently only "alchemy" is supported. The alchemy module at -00151| config.stack.alchemy provides the shared SDK configuration -00152| (version, state store, helpers) that this module consumes. -00153| ''; -00154| }; -00155| -00156| output-dir = lib.mkOption { -00157| type = lib.types.str; -00158| default = "packages/infra"; -00159| description = "Directory for generated infrastructure files (relative to project root)"; -00160| }; -00161| -00162| key-format = lib.mkOption { -00163| type = lib.types.str; -00164| default = "$module-$key"; -00165| description = '' -00166| Template for output storage keys. -00167| Variables: $module (module ID), $key (output key name). -00168| Example: "$module-$key" -> "aws-secrets-roleArn" -00169| ''; -00170| }; -00171| -00172| # ========================================================================== -00173| # Storage backend for persisting outputs -00174| # ========================================================================== -00175| storage-backend = { -00176| type = lib.mkOption { -00177| type = lib.types.enum [ -00178| "chamber" -00179| "sops" -00180| "ssm" -00181| "none" -00182| ]; -00183| default = "none"; -00184| description = "Storage backend for persisting infrastructure outputs"; -00185| }; -00186| -00187| chamber = { -00188| service = lib.mkOption { -00189| type = lib.types.str; -00190| default = ""; -00191| description = '' -00192| Chamber service name for output storage. -00193| Outputs are written as: chamber write -- -00194| ''; -00195| }; -00196| }; -00197| -00198| sops = { -00199| file-path = lib.mkOption { -00200| type = lib.types.str; -00201| default = ".stack/secrets/vars/dev.sops.yaml"; -00202| description = '' -00203| Path to SOPS-encrypted YAML file for infra outputs. -00204| Defaults to the dev group file. Uses `sops set` for non-destructive -00205| per-key updates, preserving existing secrets in the file. -00206| ''; -00207| }; -00208| -00209| group = lib.mkOption { -00210| type = lib.types.str; -00211| default = "dev"; -00212| description = '' -00213| Secrets group to write outputs to (e.g., "dev", "prod", "common"). -00214| Used to resolve the SOPS file path from the secrets directory: -00215| /vars/.sops.yaml -00216| When set, overrides file-path. -00217| ''; -00218| }; -00219| }; -00220| -00221| ssm = { - -(File has more lines. Use 'offset' parameter to read beyond line 221) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.7s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -Now I have a clear picture of the canonical module shape. Let me check where the scripts are registered to understand where to add the scaffold script: - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "infra:deploy|infra:destroy|infra:dev|infra:pull", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix" -} -``` -**Output:** -``` -Found 7 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 18: # - Registers scripts (infra:deploy, infra:destroy, etc.) - Line 691: "infra:deploy" = { - Line 708: "infra:destroy" = { - Line 725: "infra:dev" = { - Line 738: "infra:pull-outputs" = { - Line 925: name = "infra:deploy"; - Line 929: name = "infra:pull-outputs"; -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 691, - "limit": 80 -} -``` -**Output:** -``` - -00692| exec = '' -00693| cd "${outputDir}" && bunx alchemy deploy "$@" -00694| ''; -00695| description = "Deploy infrastructure via alchemy"; -00696| args = [ -00697| { -00698| name = "--stage"; -00699| description = "Deployment stage (e.g., dev, prod)"; -00700| } -00701| { -00702| name = "..."; -00703| description = "Additional alchemy deploy arguments"; -00704| } -00705| ]; -00706| }; -00707| -00708| "infra:destroy" = { -00709| exec = '' -00710| cd "${outputDir}" && bunx alchemy destroy "$@" -00711| ''; -00712| description = "Destroy infrastructure via alchemy"; -00713| args = [ -00714| { -00715| name = "--stage"; -00716| description = "Deployment stage to destroy"; -00717| } -00718| { -00719| name = "..."; -00720| description = "Additional alchemy destroy arguments"; -00721| } -00722| ]; -00723| }; -00724| -00725| "infra:dev" = { -00726| exec = '' -00727| cd "${outputDir}" && bunx alchemy dev "$@" -00728| ''; -00729| description = "Start infrastructure dev mode"; -00730| args = [ -00731| { -00732| name = "..."; -00733| description = "Additional alchemy dev arguments"; -00734| } -00735| ]; -00736| }; -00737| -00738| "infra:pull-outputs" = { -00739| exec = -00740| let -00741| storageType = cfg.storage-backend.type; -00742| dataDir = config.stack.dirs.data; -00743| outputsFile = "${dataDir}/infra-outputs.nix"; -00744| in -00745| if storageType == "chamber" then -00746| '' -00747| echo "Pulling outputs from chamber..." -00748| SERVICE="${cfg.storage-backend.chamber.service}" -00749| OUTPUT_FILE="${outputsFile}" -00750| -00751| echo "{" > "$OUTPUT_FILE" -00752| ${lib.concatMapStringsSep "\n" ( -00753| id: -00754| let -00755| mod = cfg.modules.${id}; -00756| syncKeys = builtins.filter (k: (mod.outputs.${k}.sync or false)) (builtins.attrNames mod.outputs); -00757| in -00758| '' -00759| echo ' ${id} = {' >> "$OUTPUT_FILE" -00760| ${lib.concatMapStringsSep "\n" ( -00761| key: -00762| let -00763| formattedKey = builtins.replaceStrings [ "$module" "$key" ] [ id key ] cfg.key-format; -00764| in -00765| '' -00766| VALUE=$(chamber read -q "$SERVICE" "${formattedKey}" 2>/dev/null || echo "") -00767| if [ -n "$VALUE" ]; then -00768| echo ' ${key} = "'"$VALUE"'";' >> "$OUTPUT_FILE" -00769| fi -00770| '' -00771| ) syncKeys} - -(File has more lines. Use 'offset' parameter to read beyond line 771) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -Good. Now let me create the scaffold script and template files. The scaffold will create the two files a module needs -- `module.nix` and `index.ts` -- with a working skeleton: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 685, - "limit": 10 -} -``` -**Output:** -``` - -00686| -00687| # ========================================================================== -00688| # Shell scripts -00689| # ========================================================================== -00690| stack.scripts = { -00691| "infra:deploy" = { -00692| exec = '' -00693| cd "${outputDir}" && bunx alchemy deploy "$@" -00694| ''; -00695| description = "Deploy infrastructure via alchemy"; - -(File has more lines. Use 'offset' parameter to read beyond line 695) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 33.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "oldString": " \"infra:deploy\" = {\n exec = ''\n cd \"${outputDir}\" && bunx alchemy deploy \"$@\"\n '';\n description = \"Deploy infrastructure via alchemy\";", - "newString": " \"infra:new-module\" = {\n exec = ''\n MODULE_ID=\"''${1:?Usage: infra:new-module }\"\n\n # Validate module ID\n if ! echo \"$MODULE_ID\" | grep -qE '^[a-z][a-z0-9-]*$'; then\n echo \"Error: Module ID must be lowercase alphanumeric with hyphens (e.g., my-module)\"\n exit 1\n fi\n\n MODULE_DIR=\"nix/stack/infra/modules/$MODULE_ID\"\n if [ -d \"$MODULE_DIR\" ]; then\n echo \"Error: Module directory $MODULE_DIR already exists\"\n exit 1\n fi\n\n MODULE_NAME=$(echo \"$MODULE_ID\" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)} 1')\n\n echo \"Creating infra module: $MODULE_ID ($MODULE_NAME)\"\n mkdir -p \"$MODULE_DIR\"\n\n # Generate module.nix\n cat > \"$MODULE_DIR/module.nix\" << 'NIXEOF'\n # ==============================================================================\n # infra/modules/MODULE_ID/module.nix\n # ==============================================================================\n {\n lib,\n config,\n ...\n }:\n let\n cfg = config.stack.infra.MODULE_ID;\n in\n {\n options.stack.infra.MODULE_ID = {\n enable = lib.mkOption {\n type = lib.types.bool;\n default = false;\n description = \"Enable MODULE_NAME provisioning.\";\n };\n\n # Add your module options here. Example:\n # region = lib.mkOption {\n # type = lib.types.str;\n # default = \"us-east-1\";\n # description = \"AWS region.\";\n # };\n };\n\n config = lib.mkIf cfg.enable {\n stack.infra.enable = lib.mkDefault true;\n\n stack.infra.modules.MODULE_ID = {\n name = \"MODULE_NAME\";\n description = \"MODULE_NAME infrastructure provisioning\";\n path = ./index.ts;\n\n # Nix values serialized to JSON and available at runtime\n # via infra.inputs() in the TypeScript file.\n inputs = {\n # Add your input values here. Example:\n # region = cfg.region;\n };\n\n # NPM packages required by the TypeScript implementation.\n dependencies = {\n # \"@aws-sdk/client-s3\" = \"catalog:\";\n };\n\n # Outputs the TypeScript module's default export must provide.\n # Keys with sync=true are written to the storage backend.\n outputs = {\n # exampleArn = {\n # description = \"ARN of the provisioned resource\";\n # sensitive = false;\n # sync = true;\n # };\n };\n };\n };\n }\n NIXEOF\n\n # Replace placeholders\n sed -i \"\" \"s/MODULE_ID/$MODULE_ID/g\" \"$MODULE_DIR/module.nix\"\n sed -i \"\" \"s/MODULE_NAME/$MODULE_NAME/g\" \"$MODULE_DIR/module.nix\"\n\n # Generate index.ts\n cat > \"$MODULE_DIR/index.ts\" << 'TSEOF'\n // ==============================================================================\n // MODULE_NAME Infra Module\n //\n // This module is executed by `infra:deploy` via Alchemy.\n //\n // The default export must be a Record matching the output\n // keys declared in module.nix.\n // ==============================================================================\n import Infra from \"@stack/infra\";\n\n // Typed inputs — define an interface matching your module.nix inputs.\n interface Inputs {\n // region: string;\n }\n\n const infra = new Infra(\"MODULE_ID\");\n const inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n );\n\n // ---------------------------------------------------------------------------\n // Provision resources here.\n //\n // Use Alchemy resources or AWS SDK clients:\n //\n // import { SomeResource } from \"@stack/infra/resources/some-resource\";\n // const resource = await SomeResource(infra.id(\"my-resource\"), { ... });\n //\n // Or use alchemy built-in resources:\n //\n // import { Role } from \"alchemy/aws\";\n // const role = await Role(infra.id(\"role\"), { roleName: \"...\", ... });\n //\n // ---------------------------------------------------------------------------\n\n // Export outputs matching the keys in module.nix outputs.\n export default {\n // exampleArn: resource.arn,\n };\n TSEOF\n\n sed -i \"\" \"s/MODULE_ID/$MODULE_ID/g\" \"$MODULE_DIR/index.ts\"\n sed -i \"\" \"s/MODULE_NAME/$MODULE_NAME/g\" \"$MODULE_DIR/index.ts\"\n\n # Trim leading whitespace from heredoc indentation\n sed -i \"\" 's/^ //' \"$MODULE_DIR/module.nix\"\n sed -i \"\" 's/^ //' \"$MODULE_DIR/index.ts\"\n\n echo \"\"\n echo \"Created:\"\n echo \" $MODULE_DIR/module.nix — Nix options and module registration\"\n echo \" $MODULE_DIR/index.ts — TypeScript Alchemy implementation\"\n echo \"\"\n echo \"Next steps:\"\n echo \" 1. Add your options to module.nix\"\n echo \" 2. Implement provisioning logic in index.ts\"\n echo \" 3. Import the module in nix/stack/infra/default.nix:\"\n echo \" ./modules/$MODULE_ID/module.nix\"\n echo \" 4. Enable in .stack/config.nix:\"\n echo \" stack.infra.$MODULE_ID.enable = true;\"\n echo \" 5. Run: infra:deploy\"\n '';\n description = \"Scaffold a new infra module\";\n args = [\n {\n name = \"\";\n description = \"Module identifier (lowercase, hyphens, e.g., my-s3-buckets)\";\n }\n ];\n };\n\n \"infra:deploy\" = {\n exec = ''\n cd \"${outputDir}\" && bunx alchemy deploy \"$@\"\n '';\n description = \"Deploy infrastructure via alchemy\";" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 57.7s) - -Now write the docs: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/infra-modules.mdx", - "content": "---\ntitle: Writing Infra Modules\ndescription: Create custom infrastructure modules for the Alchemy-based provisioning system\nicon: puzzle\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel's infrastructure system is modular. Each infra module is a pair of files -- a Nix module that declares options and a TypeScript file that provisions resources via Alchemy. You can write your own modules to provision any cloud resource.\n\n## Module Anatomy\n\nEvery infra module has exactly two files:\n\n```\nnix/stack/infra/modules//\n module.nix # Nix options + module registration\n index.ts # TypeScript Alchemy implementation\n```\n\n**`module.nix`** declares:\n- Options under `stack.infra.` (what users configure)\n- Registration in `stack.infra.modules.` with:\n - `name` / `description` -- metadata\n - `path` -- pointer to the TypeScript file\n - `inputs` -- Nix values serialized to JSON for the TypeScript runtime\n - `dependencies` -- NPM packages the TypeScript file needs\n - `outputs` -- what the module produces (ARNs, URLs, etc.)\n\n**`index.ts`** implements:\n- Reads inputs via `infra.inputs()`\n- Provisions resources using Alchemy resources or AWS SDK clients\n- Default-exports a `Record` matching the output keys declared in `module.nix`\n\n## Scaffold a Module\n\nThe fastest way to start:\n\n```bash\ninfra:new-module my-s3-buckets\n```\n\nThis creates `nix/stack/infra/modules/my-s3-buckets/` with a working `module.nix` and `index.ts` skeleton. Then:\n\n\n\n\n### Add options\n\nEdit `module.nix` to declare what users configure:\n\n```nix\noptions.stack.infra.my-s3-buckets = {\n enable = lib.mkOption {\n type = lib.types.bool;\n default = false;\n description = \"Enable S3 bucket provisioning.\";\n };\n\n buckets = lib.mkOption {\n type = lib.types.attrsOf (lib.types.submodule {\n options = {\n region = lib.mkOption {\n type = lib.types.str;\n default = \"us-east-1\";\n };\n versioning = lib.mkOption {\n type = lib.types.bool;\n default = true;\n };\n };\n });\n default = {};\n description = \"S3 buckets to create.\";\n };\n};\n```\n\n\n\n\n### Wire inputs and outputs\n\nIn the `config` block of `module.nix`, pass Nix values to the TypeScript runtime and declare what the module produces:\n\n```nix\nconfig = lib.mkIf cfg.enable {\n stack.infra.enable = lib.mkDefault true;\n\n stack.infra.modules.my-s3-buckets = {\n name = \"S3 Buckets\";\n description = \"Provision S3 buckets with versioning\";\n path = ./index.ts;\n\n inputs = {\n buckets = cfg.buckets;\n };\n\n dependencies = {\n \"@aws-sdk/client-s3\" = \"catalog:\";\n };\n\n outputs = {\n bucketArns = {\n description = \"Bucket ARNs (JSON)\";\n sync = true;\n };\n };\n };\n};\n```\n\n\n\n\n### Implement provisioning\n\nEdit `index.ts` to provision resources:\n\n```typescript\nimport Infra from \"@stack/infra\";\n\ninterface BucketInput {\n region: string;\n versioning: boolean;\n}\n\ninterface Inputs {\n buckets: Record;\n}\n\nconst infra = new Infra(\"my-s3-buckets\");\nconst inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n);\n\nconst {\n S3Client,\n CreateBucketCommand,\n PutBucketVersioningCommand,\n} = await import(\"@aws-sdk/client-s3\");\n\nconst bucketArns: Record = {};\n\nfor (const [name, bucket] of Object.entries(inputs.buckets)) {\n const client = new S3Client({ region: bucket.region });\n\n try {\n await client.send(new CreateBucketCommand({ Bucket: name }));\n } catch (err: any) {\n if (err.name !== \"BucketAlreadyOwnedByYou\") throw err;\n }\n\n if (bucket.versioning) {\n await client.send(new PutBucketVersioningCommand({\n Bucket: name,\n VersioningConfiguration: { Status: \"Enabled\" },\n }));\n }\n\n bucketArns[name] = `arn:aws:s3:::${name}`;\n}\n\nexport default {\n bucketArns: JSON.stringify(bucketArns),\n};\n```\n\n\n\n\n### Register the module\n\nAdd the import to `nix/stack/infra/default.nix`:\n\n```nix\nimports = [\n # ... existing modules\n ./modules/my-s3-buckets/module.nix\n];\n```\n\n\n\n\n### Enable and deploy\n\nIn `.stack/config.nix`:\n\n```nix\nstackpanel.infra.my-s3-buckets = {\n enable = true;\n buckets = {\n \"myapp-uploads\" = {\n region = \"us-west-2\";\n versioning = true;\n };\n \"myapp-backups\" = {\n region = \"us-west-2\";\n versioning = false;\n };\n };\n};\n```\n\nThen deploy:\n\n```bash\ninfra:deploy\n```\n\n\n\n\n## Module Contract\n\n### Inputs\n\nInputs are Nix values that get serialized to JSON. The TypeScript file reads them at runtime:\n\n```typescript\nconst infra = new Infra(\"my-module\");\nconst inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n);\n```\n\n**Key-case transformation**: Nix uses `kebab-case`, TypeScript receives `camelCase`. The codegen handles the conversion automatically. So `api-key-ssm-path` in Nix becomes `apiKeySsmPath` in TypeScript.\n\n### Outputs\n\nThe TypeScript file's default export must be a `Record` where keys match the `outputs` declared in `module.nix`:\n\n```typescript\n// module.nix declares: outputs.bucketArns, outputs.roleArn\nexport default {\n bucketArns: JSON.stringify(arns), // complex values as JSON strings\n roleArn: role.arn, // simple strings directly\n};\n```\n\nOutputs with `sync = true` are written to the storage backend (SOPS, SSM, or Chamber) by the Alchemy orchestrator after all modules complete.\n\nOutputs with `sensitive = true` are marked for encryption in the storage backend.\n\n### Dependencies\n\nNPM dependencies are declared in `module.nix` and merged into the generated `packages/infra/package.json`:\n\n```nix\ndependencies = {\n \"@aws-sdk/client-s3\" = \"catalog:\"; # from bun catalog\n \"some-package\" = \"^1.2.3\"; # pinned version\n};\n```\n\n### Using Alchemy Resources\n\nFor common AWS resources, use the custom Alchemy resources in `@stack/infra/resources/`:\n\n```typescript\nimport { IamRole } from \"@stack/infra/resources/iam-role\";\nimport { KmsKey } from \"@stack/infra/resources/kms-key\";\nimport { Ec2Instance } from \"@stack/infra/resources/ec2-instance\";\nimport { SecurityGroup } from \"@stack/infra/resources/security-group\";\n```\n\nThese resources are adopt-safe (they detect and import existing resources rather than failing).\n\nYou can also use Alchemy's built-in resources:\n\n```typescript\nimport { Role } from \"alchemy/aws\";\nimport { GitHubOIDCProvider } from \"alchemy/aws/oidc\";\n```\n\n### Resource IDs\n\nUse `infra.id()` to generate namespaced resource IDs that avoid collisions between modules:\n\n```typescript\nconst role = await IamRole(infra.id(\"role\"), { ... });\nconst key = await KmsKey(infra.id(\"kms\"), { ... });\n```\n\nThis produces IDs like `my-module/role` and `my-module/kms`.\n\n## Directory Modules\n\nFor complex modules, split the TypeScript into multiple files by using a directory:\n\n```\nnix/stack/infra/modules/my-module/\n module.nix # set path = ./impl;\n impl/\n index.ts # main entry point (default export)\n policies.ts # helper functions\n constants.ts # shared constants\n```\n\nIn `module.nix`, set `path = ./impl;` (pointing to the directory). The codegen copies all `*.ts` files from the directory to `packages/infra/modules/my-module/`.\n\nImport between files uses standard relative imports:\n\n```typescript\n// index.ts\nimport { buildPolicy } from \"./policies\";\n```\n\n## Existing Modules Reference\n\nStudy these modules as examples:\n\n| Module | Complexity | Good example of |\n|--------|-----------|-----------------|\n| `aws-key-pairs` | Simple (84 lines Nix, 42 lines TS) | Minimal module, loop over inputs |\n| `aws-security-groups` | Simple (145 lines Nix, 69 lines TS) | Nested submodule types |\n| `aws-iam` | Medium (128 lines Nix, 71 lines TS) | IAM role + instance profile |\n| `cache` | Medium (200 lines Nix, variable TS) | Multi-provider pattern |\n| `aws-secrets` | Medium (directory module) | Split into policies.ts + index.ts |\n| `aws-ec2-app` | Complex (918 lines Nix, 962 lines TS) | Per-app loop, auto-discovery, multiple resource types |\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `infra:new-module ` | Scaffold a new module with `module.nix` + `index.ts` |\n| `infra:deploy` | Deploy all enabled modules |\n| `infra:destroy` | Remove provisioned resources |\n| `infra:pull-outputs` | Pull outputs from storage backend |\n\n## Tips\n\n- **Start small**: Begin with a single resource type and add complexity later\n- **Use adopt patterns**: Check if a resource exists before creating it, especially for IAM roles and KMS keys\n- **JSON-encode complex outputs**: Outputs must be strings. Use `JSON.stringify()` for arrays and objects\n- **Test locally**: Run `infra:deploy --stage dev-$USER` to test without affecting shared infrastructure\n- **Check the generated code**: Look at `packages/infra/modules/.ts` to verify the codegen output matches your source\n\n## Reference\n\n- [Infrastructure](/docs/deployment/infrastructure) for the infra system overview\n- [Machines](/docs/deployment/machines) for machine inventory\n- [Deployment Overview](/docs/deployment) for all deployment options\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.3s) - -Now update the meta.json and deployment index to include the new page: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}", - "newString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"infra-modules\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}" -} -``` -**Error:** -``` -Error: File /Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json has been modified since it was last read. -Last modification: 2026-03-01T13:40:54.298Z -Last read: 2026-03-01T13:09:36.853Z - -Please read the file again before modifying it. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.1s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json" -} -``` -**Output:** -``` - -00001| { -00002| "title": "Containers & Deployment", -00003| "description": "Build containers and deploy to production", -00004| "icon": "cloud-upload", -00005| "pages": ["index", "infrastructure", "machines", "cloudflare", "fly", "containers", "sst"] -00006| } -00007| - -(End of file - total 7 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "\"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]", - "newString": "\"pages\": [\"index\", \"infrastructure\", \"machines\", \"infra-modules\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 22, - "limit": 22 -} -``` -**Output:** -``` - -00023| -00024| ## What Runs Where -00025| -00026| | Host | Provisioning path | Notes | -00027| |------|-------------------|-------| -00028| | `cloudflare` | `infra:deploy` (Alchemy infra module) | Framework × host maps to a Cloudflare Alchemy resource | -00029| | `fly` | Fly module workflow (`flyctl`, generated `fly.toml`) | Container-based deploy flow; not managed by infra deployment module | -00030| -00031| -00032| The infra deployment module currently provisions Cloudflare apps only. Fly apps are handled by the Fly deployment module. -00033| -00034| -00035| ## Minimal Example -00036| -00037| ```nix -00038| { -00039| stack.deployment.defaultHost = "cloudflare"; -00040| -00041| stack.apps.web = { -00042| path = "apps/web"; -00043| framework.tanstack-start.enable = true; -00044| deployment = { - -(File has more lines. Use 'offset' parameter to read beyond line 44) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 1, - "limit": 22 -} -``` -**Output:** -``` - -00002| title: Containers & Deployment -00003| description: Host-aware deployment for Cloudflare and Fly.io -00004| icon: cloud -00005| --- -00006| -00007| import { Cards, Card } from "fumadocs-ui/components/card"; -00008| import { Callout } from "fumadocs-ui/components/callout"; -00009| -00010| Stack deployment is app-first: each app opts into deployment, chooses a host, and then host-specific modules generate the files/scripts needed for that host. -00011| -00012| ## Routing Model -00013| -00014| Deployment selection is resolved in this order: -00015| -00016| 1. `stack.apps..deployment.host` (explicit per app) -00017| 2. `stack.deployment.defaultHost` (global fallback, default: `"cloudflare"`) -00018| -00019| An app is deployable when: -00020| -00021| - `stack.apps..deployment.enable = true` -00022| - one framework flag is enabled (for example `framework.tanstack-start.enable = true`) -00023| - -(File has more lines. Use 'offset' parameter to read beyond line 23) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.0s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.1s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "Cards\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.3s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "Cards|Card title\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -Found 3 matches -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx: - Line 7: import { Cards, Card } from "fumadocs-ui/components/card"; - Line 71: - Line 84: -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 68, - "limit": 20 -} -``` -**Output:** -``` - -00069| ## Provider Guides -00070| -00071| -00072| -00073| Configure per-app Cloudflare options and deploy with `infra:deploy` -00074| -00075| -00076| Generate `fly.toml`, use wrapped `fly-` commands, and deploy containers -00077| -00078| -00079| Understand infra modules, Alchemy generation, stages, and output sync -00080| -00081| -00082| Define machine inventory for Colmena and infrastructure-aware workflows -00083| -00084| -00085| - -(End of file - total 85 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.2s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": " \n Define machine inventory for Colmena and infrastructure-aware workflows\n \n", - "newString": " \n Define machine inventory for Colmena and infrastructure-aware workflows\n \n \n Scaffold and develop custom Alchemy modules for the infra system\n \n" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 78.5s) - -Now let me verify the scaffold script works: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c bash -lc 'infra:new-module test-example'", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 120000, - "description": "Test scaffold command generates module files" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/jlmnkmhk3xg3vngj90npf497qn5cq7f0-vendor-env.drv'... -building '/nix/store/fim5b2b3kas7a2b4msa7z5yl8j3pmxdn-go-cache-env.drv'... -building '/nix/store/0c542jravs13kkyiial58nwpcpgsp0dx-stack-0.1.0.drv'... -building '/nix/store/0lg9wabl1716sdzdvw95gawh8qi5ksx6-infra-new-module.drv'... -building '/nix/store/2rad2l3c9wlw17mzz68ifbiaawqkyr1q-stack-scripts.drv'... -building '/nix/store/9j9dkjgyfjr5n89vd74g8l46qs3hrr0l-stack-profile.drv'... -building '/nix/store/hdylk86pwhx3pnndq2gx52vgr1n4jyba-stack-generate-bin.drv'... -building '/nix/store/38j99v4bji5kmvx3vp9ydm0m45v6425l-stack-gc-update.drv'... -building '/nix/store/g25yfzmjlgs6ywwj1gcb4xldp1b64nzb-stack-shellhook.drv'... -building '/nix/store/yq8ad3cgkcqkqyd9xb5v0pr1sp97vsqb-stack-stack-env.drv'... -Config validated for stack -✓ Stack initialized -warning: unknown experimental feature 'build-time-fetch-tree' -warning: unknown experimental feature 'parallel-eval' -warning: unknown experimental feature 'external-builders' -warning: unknown setting 'eval-cores' -warning: unknown setting 'external-builders' -warning: unknown setting 'lazy-trees' -Checking if CA is reachable... OK -Checking if root cert is installed... OK -Checking if fingerprint matches... OK -Checking if device certificate exists... OK -Checking if device certificate is valid... OK - -All checks passed! -📜 stack scripts loaded - remove packages/infra/modules/deployment.ts (stale) - write .gitignore (block, updated) - write .stack/state/infra-inputs.json - write .vscode/settings.json - write .zed/settings.json - write packages/infra/README.md - write packages/infra/alchemy.run.ts - write packages/infra/modules/aws-ec2-app.ts - write packages/infra/package.json - write packages/infra/src/types.ts -files: 9 written, 69 unchanged, 1 removed -Stack development environment - -📦 Stack Ports (project: stack) - Stable port: 6400 - - Apps: - docs: 5738 -> https://docs.stack.lan - stack-go: 5797 - web: 5775 -> https://stack.stack.lan - - Services: - Minio: 6498 - Minio Console: 6436 - PostgreSQL: 6404 - Redis: 6494 - - Tip: Set STACKPANEL_QUIET=1 to hide this message - - → .stack/bin: 220 binaries -Caddy config directories created at /Users/cm/.config/caddy -Site configuration saved to /Users/cm/.config/caddy/sites.d/stackpanel_docs_stackpanel_lan.caddy - -Run 'caddy-start' or 'caddy-restart' to apply changes -Caddy config directories created at /Users/cm/.config/caddy -Site configuration saved to /Users/cm/.config/caddy/sites.d/stackpanel_stackpanel_stackpanel_lan.caddy - -Run 'caddy-start' or 'caddy-restart' to apply changes -✅ Stack Nix module system initialized -stack core initialized - | | | - __| __| _' | __| | / __ \ _' | __ \ _ \ | - \__ \ | ( | ( < | | ( | | | __/ | - ____/ \__| \__,_| \___| _|\_\ .__/ \__,_| _| _| \___| _| - _| - -╭────────────────────────────────────────────────────────────────────────╮ -│ │ -│ stackpanel Shell │ -│ Your reproducible development environment is ready │ -│ │ -│ ───��──────────────────────────────────────────────────────────────── │ -│ Status │ -│ Agent ● localhost:9876 │ -│ Services ○ minio ○ minio_console ○ postgres ○ redis │ -│ Health ███████░░░ 22/28 │ -│ ● aws-roles-anywhere 3/3 │ -│ ● deployment-fly 2/2 │ -│ ● docker 1/1 │ -│ ● env-codegen 2/2 │ -│ ● go 3/3 │ -│ ● oxlint 2/3 │ -│ ● secrets 5/10 │ -│ ● step-ca 4/4 │ -│ Shell ⚠ stale (config changed, reload shell) │ -│ Studio http://localhost:3000/studio?project=stackpanel │ -│ │ -│ Environment │ -│ Node 22.22.0 • Bun 1.3.3 • Go 1.25.6 • Python 3.12.12 │ -│ PostgreSQL 17.7 • Redis 8.2.3 • Docker 28.5.2 │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Quick Start │ -│ dev  Start all development services │ -│ dev stop  Stop all services │ -│ sp status  Open interactive dashboard │ -│ sp commands  List all available commands │ -│ │ -│ Shortcuts │ -│ sp  = stackpanel │ -│ spx = run devshell commands │ -│ x  = same as spx │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ ⚠ Action Required │ -│ ! oxlint: 1/3 checks failing → sp status │ -│ ✗ secrets: 5/10 checks failing → sp status │ -│ ! Shell is stale (config changed) → exit && direnv allow │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Resources │ -│ Docs https://stackpanel.dev/docs │ -│ │ -╰────────────────────────────────────────────────────────────────────────╯ - -═══════════════════════════════════════════════════════════════ -Shell hook completed at 2026-03-01 07:17:51 -Log saved to: /Users/cm/git/darkmatter/stack/.stack/state/shell.log -═══════════════════════════════════════════════════════════════ -Creating infra module: test-example (Test Example) - -Created: - nix/stack/infra/modules/test-example/module.nix — Nix options and module registration - nix/stack/infra/modules/test-example/index.ts — TypeScript Alchemy implementation - -Next steps: - 1. Add your options to module.nix - 2. Implement provisioning logic in index.ts - 3. Import the module in nix/stack/infra/default.nix: - ./modules/test-example/module.nix - 4. Enable in .stack/config.nix: - stack.infra.test-example.enable = true; - 5. Run: infra:deploy - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.1s) - -Let me check the generated files look clean: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/test-example/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/test-example/module.nix -00003| # ============================================================================== -00004| { -00005| lib, -00006| config, -00007| ... -00008| }: -00009| let -00010| cfg = config.stack.infra.test-example; -00011| in -00012| { -00013| options.stack.infra.test-example = { -00014| enable = lib.mkOption { -00015| type = lib.types.bool; -00016| default = false; -00017| description = "Enable Test Example provisioning."; -00018| }; -00019| -00020| # Add your module options here. Example: -00021| # region = lib.mkOption { -00022| # type = lib.types.str; -00023| # default = "us-east-1"; -00024| # description = "AWS region."; -00025| # }; -00026| }; -00027| -00028| config = lib.mkIf cfg.enable { -00029| stack.infra.enable = lib.mkDefault true; -00030| -00031| stack.infra.modules.test-example = { -00032| name = "Test Example"; -00033| description = "Test Example infrastructure provisioning"; -00034| path = ./index.ts; -00035| -00036| # Nix values serialized to JSON and available at runtime -00037| # via infra.inputs() in the TypeScript file. -00038| inputs = { -00039| # Add your input values here. Example: -00040| # region = cfg.region; -00041| }; -00042| -00043| # NPM packages required by the TypeScript implementation. -00044| dependencies = { -00045| # "@aws-sdk/client-s3" = "catalog:"; -00046| }; -00047| -00048| # Outputs the TypeScript module's default export must provide. -00049| # Keys with sync=true are written to the storage backend. -00050| outputs = { -00051| # exampleArn = { -00052| # description = "ARN of the provisioned resource"; -00053| # sensitive = false; -00054| # sync = true; -00055| # }; -00056| }; -00057| }; -00058| }; -00059| } -00060| - -(End of file - total 60 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/test-example/index.ts" -} -``` -**Output:** -``` - -00001| // ============================================================================== -00002| // Test Example Infra Module -00003| // -00004| // This module is executed by `infra:deploy` via Alchemy. -00005| // -00006| // The default export must be a Record matching the output -00007| // keys declared in module.nix. -00008| // ============================================================================== -00009| import Infra from "@stack/infra"; -00010| -00011| // Typed inputs — define an interface matching your module.nix inputs. -00012| interface Inputs { -00013| // region: string; -00014| } -00015| -00016| const infra = new Infra("test-example"); -00017| const inputs = infra.inputs( -00018| process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES, -00019| ); -00020| -00021| // --------------------------------------------------------------------------- -00022| // Provision resources here. -00023| // -00024| // Use Alchemy resources or AWS SDK clients: -00025| // -00026| // import { SomeResource } from "@stack/infra/resources/some-resource"; -00027| // const resource = await SomeResource(infra.id("my-resource"), { ... }); -00028| // -00029| // Or use alchemy built-in resources: -00030| // -00031| // import { Role } from "alchemy/aws"; -00032| // const role = await Role(infra.id("role"), { roleName: "...", ... }); -00033| // -00034| // --------------------------------------------------------------------------- -00035| -00036| // Export outputs matching the keys in module.nix outputs. -00037| export default { -00038| // exampleArn: resource.arn, -00039| }; -00040| - -(End of file - total 40 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.6s) - -Clean scaffold output. Now clean up the test module: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "rm -rf nix/stack/infra/modules/test-example", - "workdir": "/Users/cm/git/darkmatter/stack", - "description": "Remove test scaffold module" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.0s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.4s) - -Here's a summary of what was built: - -## `infra:new-module` Scaffold Command - -Registered in `nix/stack/infra/codegen.nix` as a devshell script. Usage: - -```bash -infra:new-module my-s3-buckets -``` - -Creates: -``` -nix/stack/infra/modules/my-s3-buckets/ - module.nix # Nix options + module registration (with commented examples) - index.ts # TypeScript Alchemy implementation (with commented examples) -``` - -Validated: module ID must be lowercase alphanumeric with hyphens, directory must not already exist. The generated files have the module ID and name substituted throughout, and include commented examples for options, inputs, dependencies, outputs, and resource provisioning. - -The output includes clear next steps: -1. Add options to `module.nix` -2. Implement logic in `index.ts` -3. Import in `nix/stack/infra/default.nix` -4. Enable in `.stack/config.nix` -5. Run `infra:deploy` - -## Documentation - -`apps/docs/content/docs/deployment/infra-modules.mdx` -- comprehensive guide covering: - -- **Module anatomy**: the two-file contract (module.nix + index.ts) -- **Step-by-step walkthrough**: building an S3 bucket module from scaffold to deploy -- **Module contract**: inputs (key-case transformation), outputs (JSON strings), dependencies, Alchemy resources, resource IDs (`infra.id()`) -- **Directory modules**: splitting complex modules into multiple TypeScript files -- **Existing modules reference table**: ranked by complexity with what each is a good example of -- **Tips**: adopt patterns, testing with stages, checking generated code - ---- - diff --git a/docs/infradeploy.md b/docs/infradeploy.md deleted file mode 100644 index df58c9d0..00000000 --- a/docs/infradeploy.md +++ /dev/null @@ -1,9566 +0,0 @@ -# Infra module deploy --adopt failure despite help showing option - -**Session ID:** ses_35892eb07ffevEFiAHEU4zQAjT -**Created:** 2/28/2026, 7:24:29 PM -**Updated:** 3/1/2026, 7:19:05 AM - ---- - -## Assistant (Build · gpt-5.3-codex · 9.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix show-config | rg \"^builders =|^max-jobs =|^builders-use-substitutes =|^fallback =\"", - "description": "Check nix builder configuration values" -} -``` -**Output:** -``` -warning: 'show-config' is a deprecated alias for 'config show' -builders = ssh://root@100.102.113.26 x86_64-linux /etc/nix/builder_ed25519 16 1 big-parallel,benchmark,kvm,nixos-test -builders-use-substitutes = true -fallback = false -max-jobs = 24 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.9s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ssh -o ConnectTimeout=5 -i /etc/nix/builder_ed25519 root@100.102.113.26 'echo ok'", - "description": "Test remote nix builder SSH connectivity" -} -``` -**Output:** -``` -ok - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.1s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix store ping --store \"ssh://root@100.102.113.26\"", - "description": "Ping remote Nix store over SSH" -} -``` -**Output:** -``` -warning: 'ping' is a deprecated alias for 'info' -Store URL: ssh://root@100.102.113.26 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 35.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix build --impure .#devShells.aarch64-darwin.default -L", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 30000, - "description": "Check if devshell build itself stalls" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 30000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 96.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure --option builders '' --option fallback true -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 90000, - "description": "Test develop without remote builder configuration" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 90000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix eval --raw --impure --expr '\"ok\"'", - "description": "Test basic nix daemon responsiveness" -} -``` -**Output:** -``` -ok -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 28.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "pgrep -af \"nix develop|bash --rcfile /tmp/nix-shell|nix-daemon __build-remote\"", - "description": "List hanging nix develop and nix-shell processes" -} -``` -**Output:** -``` -12984 -22953 -45950 -59070 -75186 -85568 -90006 -99002 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 12984,22953,45950,59070,75186,85568,90006,99002 -o pid,ppid,stat,etime,command", - "description": "Show details for lingering nix-related process IDs" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND -90006 85629 Ss 08:25 nix-daemon __build-remote 6 -12984 72659 S 02-21:38:19 bash --rcfile /tmp/nix-shell.ifHEBl -85568 5179 S+ 11:09 nix develop --verbose --debug --log-format bar-with-logs -L -22953 23851 S 10:06:45 bash --rcfile /tmp/nix-shell.RkSZvV -59070 22953 S+ 08:00:40 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O -75186 52085 S+ 01:08:59 bash --rcfile /tmp/nix-shell.5v7q3p -45950 11608 S 01-01:52:27 bash --rcfile /tmp/nix-shell.n5blbc -99002 90569 S 07:43:19 bash --rcfile /tmp/nix-shell.qZDJVz - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "kill 85568 90006", - "description": "Terminate stuck nix develop and remote build helper" -} -``` -**Output:** -``` -zsh:kill:1: kill 90006 failed: operation not permitted - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 85568,90006 -o pid,ppid,stat,etime,command", - "description": "Check whether targeted stuck processes remain" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 64.5s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Retest develop after killing stuck process" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/p67312d0vwhswgdzf38d8ffa12ky2pyf-stack-0.1.0.drv'... -building '/nix/store/26h81d1lqmd7w1qznqjmmd1n0ql7nzrh-check-files-drift.drv'... -building '/nix/store/qrd6rbkpvy6dpf4hzz9pwpwayzqs4hkr-write-files.drv'... -building '/nix/store/mq0vqf71y6cngd22sdqaqqrfz0prdv2k-check-files.drv'... -building '/nix/store/1spcb854akafmycs84cqyvnjcix3a0vl-write-files.drv'... -building '/nix/store/0hzvw2kw65bjj3mvc6xm22jagpj3l6kb-stack-scripts.drv'... -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 60000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.9s) - -_Thinking:_ - - - -Yep — I found the likely cause. - -`nix develop` wasn’t truly deadlocked in eval; it was blocked behind an already-running stuck `nix develop`/remote build worker. - -I found these long-lived processes on your machine: - -- `nix develop --verbose --debug --log-format bar-with-logs -L` (running ~11 min) -- `nix-daemon __build-remote` worker tied to it -- several old `bash --rcfile /tmp/nix-shell...` sessions - -After terminating the stuck `nix develop` process, a fresh `nix develop --impure -c true` immediately started progressing again (building derivations), instead of sitting at daemon op logs. - -**What to do now** -1. In your terminal, kill stale `nix develop` jobs: - - `pkill -f "nix develop --verbose --debug --log-format bar-with-logs -L"` - - or manually: `ps -axo pid,etime,command | rg "nix develop|nix-shell"` then `kill ` -2. If it still stalls, restart Determinate Nix daemon: - - `sudo launchctl kickstart -k system/systems.determinate.nix-daemon` -3. Retry: - - `nix develop --impure` - -If you want, I can also give you a small `bin/nix-unstick` script for this repo to automate those checks/cleanup. - ---- - -## User - -how do i setup machines in the studio UI? lets also make sure the docs site is updated and has clear, detailed docs for this deploy feature - ---- - -## Assistant (Build · claude-opus-4-6 · 174.7s) - -_Thinking:_ - -The user is asking about two things: -1. How to set up machines in the studio UI (likely referring to Fly.io machines or some deployment feature) -2. Making sure the docs site has clear, detailed documentation for the deploy feature - -Let me first understand what "machines" means in this context - it's likely related to the Fly.io deployment module that was throwing "Unsupported host fly" earlier. Let me explore the codebase to understand the deployment/machines setup in the studio UI and the docs. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore deployment/machines UI and docs", - "prompt": "Search the codebase thoroughly for:\n\n1. How \"machines\" or deployment targets are configured in the studio UI (apps/web). Look for components related to deployment, machines, infra setup, infrastructure-step, etc.\n2. The deployment module in nix/stack/infra/modules/deployment/ - understand what hosts/frameworks are supported and how apps get registered\n3. The current docs content for deployment/infra in apps/docs/content/docs/ - find all relevant deployment and infra docs\n4. How the infra panel works in the studio UI (apps/web/src/components/studio/panels/infra-panel.tsx, setup steps, etc.)\n5. The .stack/config.nix to understand how deployment is currently configured\n\nReturn:\n- File paths and key line numbers for all deployment-related UI components\n- File paths for all deployment docs\n- The current deployment config from .stack/config.nix (the deployment/infra section)\n- What hosts/frameworks the deployment module supports\n- How the studio UI handles infrastructure/deployment setup", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have a thorough understanding of the entire deployment/infra system. Here is my complete report. - ---- - -## Comprehensive Deployment & Infrastructure Search Results - -### 1. Deployment-Related UI Components (apps/web) - -#### Route Files -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/deploy.tsx` | L1-11 | Route at `/studio/deploy`, renders `DeployPanel` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/infra.tsx` | L1-11 | Route at `/studio/infra`, renders `InfraPanel` | - -#### Deploy Panel (Colmena-based NixOS Deployment) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` | L1-547 | **Primary deploy panel**. Colmena-centric deployment management. Shows machine inventory cards, app-to-machine mapping, and deploy actions (eval/build/apply). Tabs: Machines, App Targets, Actions, Settings. | -| ^^ | L48-65 | `MachineInfo` type: id, name, host, ssh, tags, roles, provider, arch, publicIp, privateIp, targetEnv, labels | -| ^^ | L67-74 | `AppDeployMapping` type: enable, targets, resolvedTargets, role, nixosModules, system | -| ^^ | L89-125 | `useColmenaData()` hook: reads `nixConfig.serializable.colmena` for machine inventory and iterates `apps` for deploy-enabled entries | -| ^^ | L131-201 | `MachineCard` component: renders individual machine with SSH info, architecture, provider, tags, roles | -| ^^ | L250-546 | `DeployPanel` main component: status overview (machines/reachable/unreachable/deploy-enabled apps), tabbed machine list, app target rows, Colmena action buttons (eval/build/apply), settings display | - -#### Infra Panel (SST/AWS Infrastructure) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra-panel.tsx` | L1-808 | **Infrastructure panel**. AWS infrastructure provisioning with SST. Tabs: Status, Deploy, Outputs, Resources, Configure | -| ^^ | L50-248 | `InfraPanel` main component: status cards (Enabled/Deployed/KMS), tabbed layout | -| ^^ | L257-363 | `StatusTabContent`: shows SST project name, region, OIDC provider, IAM role, KMS key, config path | -| ^^ | L366-451 | `DeployTabContent`: stage selector (dev/staging/production), CommandRunner for `sst deploy` and `sst remove` | -| ^^ | L453-496 | `OutputsTabContent`: displays deployed stack outputs (ARNs, etc.) | -| ^^ | L498-540 | `ResourcesTabContent`: lists deployed AWS resources by type/URN | -| ^^ | L542-807 | `ConfigureTabContent`: full SST config form -- enable toggle, project name, region, account ID, config path, KMS settings, OIDC provider (GitHub Actions/Fly.io/Roles Anywhere), IAM role name | - -#### Infra Shared Components -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/index.ts` | L1-9 | Barrel export for infra module | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/types.ts` | L1-54 | Type definitions: `SSTData`, `SSTStatus`, `SSTResource` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/constants.ts` | L1-73 | Constants: `OIDC_PROVIDERS`, `AWS_REGIONS`, `DEFAULT_SST_DATA`, `mergeWithDefaults()` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/components.tsx` | L1-114 | Shared UI: `StatusCard`, `ResourceRow`, `OutputRow` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` | L1-327 | `useSSTConfig()` hook: manages SST form state, reads `useNixData("sst")`, calls agent for status/outputs/resources, handles deploy/remove via `agentClient.deploySSTInfra()` | - -#### Setup Wizard Infrastructure Step -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/infrastructure-step.tsx` | L1-340 | **Setup wizard step** for AWS infrastructure. Guided config for IAM Role, KMS Key, SST config. Toggle enable, AWS region/account, OIDC provider (GitHub Actions specific fields), KMS key alias. Links to infra panel for deployment. | - -#### Per-App Deployment Tab -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/app-expanded-content.tsx` | L52 | Nav item `{ id: "deployment", label: "Deployment", icon: Rocket }` | -| ^^ | L85-86 | Props: `deploymentPanels?: AppModulePanel[]` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps-panel-alt.tsx` | L88-101 | Filters module panels: deployment panels are `module === "deployment-fly"` or `module === "deployment-cloudflare"`, routed to app Deployment tab | - -#### Sidebar Navigation -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-sidebar.tsx` | L91-92 | Panel IDs include `"infra"` and `"deploy"` | -| ^^ | L122 | Main nav: `{ id: "deploy", label: "Deploy", icon: Rocket }` | -| ^^ | L129 | Tools nav: `{ id: "infra", label: "Infrastructure", icon: Cloud }` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-header.tsx` | L44 | Label mapping: `infra: "Infrastructure"` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/landing/infrastructure-section.tsx` | -- | Landing page infrastructure section | - ---- - -### 2. Deployment Module in Nix (nix/stack/) - -#### Top-Level Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/default.nix` | L1-39 | **Aggregator** -- imports `./fly` and `./cloudflare` | - -#### Infra Modules Deployment (Alchemy-Based) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix` | L1-157 | **Infra deployment module** -- reads `framework` x `deployment.host` from each app and creates Alchemy resources | - -**Supported Framework x Host Matrix** (from lines 12-19): - -| Framework | Host | Alchemy Resource | -|---|---|---| -| `tanstack-start` | `cloudflare` | `TanStackStart` | -| `nextjs` | `cloudflare` | `Nextjs` | -| `vite` | `cloudflare` | `cloudflare.Vite` | -| `hono` | `cloudflare` | `cloudflare.Worker` | -| `astro` | `cloudflare` | `Astro` | -| `remix` | `cloudflare` | `Remix` (Worker-based) | -| `nuxt` | `cloudflare` | (listed in frameworkNames, line 51) | -| `*` | `fly` | Fly container (separate module) | -| `*` | `vercel` | (planned, checked on line 80) | -| `*` | `aws` | (planned, checked on line 81) | - -**How apps get registered** (lines 64-69): `deployableApps` = apps where `deployment.enable == true` AND a framework is detected AND `deployment.host != null`. The module auto-enables `stack.infra.enable` and registers itself as `stack.infra.modules.deployment` with pure-data inputs per app (framework, host, path, bindings, secrets + framework-specific options). - -#### Fly.io Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix` | L1-603 | **Full Fly.io deployment module**. Container-based deployment using nix2container. Generates fly.toml, deploy scripts, wrapped `fly-` CLI commands, turbo tasks, health checks | -| ^^ | L59-135 | Per-app options added via `deploymentAppModule`: fly.appName, fly.region, fly.memory, fly.cpus, fly.cpuKind (shared/performance), fly.autoStop (off/stop/suspend), fly.autoStart, fly.minMachines, fly.forceHttps, fly.env; container.type (bun/node/go/static/custom), container.port, container.entrypoint | -| ^^ | L380-415 | **Global options**: `stack.deployment.defaultHost` (enum: cloudflare/fly/vercel/aws), `stack.deployment.fly.organization`, `stack.deployment.fly.defaultRegion` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/meta.nix` | L1-64 | Metadata: id=`deployment-fly`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/schema.nix` | -- | SpField schema definitions for Fly options | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/container.nix` | -- | Container configuration | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/reference.nix` | -- | Reference docs | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix` | -- | UI panel definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/lib/fly-oidc.nix` | -- | Fly.io OIDC integration | - -#### Cloudflare Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/module.nix` | L1-110 | **Cloudflare Workers per-app options**. Adds `deployment.cloudflare.*` to each app via appModules: workerName, route, compatibility (node/browser), kvNamespaces, d1Databases, r2Buckets. Global options: accountId, compatibilityDate, defaultRoute | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/meta.nix` | L1-43 | Metadata: id=`deployment-cloudflare`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/schema.nix` | -- | SpField schema definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix` | -- | UI panel definitions | - -#### Machine Inventory Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` | L1-307 | **Machine inventory** for Colmena deployments. Sources: `static` or `aws-ec2`. Machine type: id, name, host, ssh config, tags, roles, provider, arch, publicIp, privateIp, labels, nixosProfile, nixosModules, targetEnv, env, metadata. AWS EC2 discovery via filters, tag-based role/tag/env resolution, host preference order | - -#### Infrastructure Module System -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` | L1-38 | **Aggregator** -- imports options.nix, codegen.nix, and all 11 infra sub-modules | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix` | L1-335 | **Core options**: enable, framework (alchemy only), output-dir, key-format, storage-backend (chamber/sops/ssm/none), modules registry (infraModuleType), package config, outputs stub | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix` | L1-968 | **Code generation engine**: generates `src/index.ts` (Infra class), `src/types.ts`, custom alchemy resources (KMS, IAM, EC2, ALB, etc.), `alchemy.run.ts` orchestrator, `package.json`, `tsconfig.json`, `infra-inputs.json`. Registers scripts: `infra:deploy`, `infra:destroy`, `infra:dev`, `infra:pull-outputs`. Registers as module `infra` in category `infrastructure` | - -All 11 infra sub-modules: -- `aws-secrets` -- KMS + IAM roles -- `deployment` -- App hosting via Alchemy -- `database` -- Neon Postgres / devenv / Docker -- `cache` -- Upstash Redis / devenv / Docker -- `machines` -- Machine inventory (static/AWS EC2) -- `aws-network` -- VPC, subnets -- `aws-security-groups` -- Security groups -- `aws-key-pairs` -- SSH key pairs -- `aws-iam` -- IAM roles/policies -- `aws-ec2` -- EC2 instances -- `aws-ec2-app` -- Per-app EC2 resources (ALB, target groups, etc.) - ---- - -### 3. Deployment Documentation - -#### Public Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx` | Deployment overview | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx` | Fly.io deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx` | Cloudflare deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx` | Container deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx` | SST infrastructure guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json` | Section metadata | - -#### Internal/Reference Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment.mdx` | Deployment module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra.mdx` | Infra module system reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/options.mdx` | Infra options reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/codegen.mdx` | Infra codegen reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/templates.mdx` | Infra templates reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/deployment/module.mdx` | Deployment infra module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-secrets/module.mdx` | AWS secrets module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/database/module.mdx` | Database module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/cache/module.mdx` | Cache module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/machines/module.mdx` | Machines module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-network/module.mdx` | AWS network module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/module.mdx` | Fly module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/container.mdx` | Fly container reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/reference.mdx` | Fly reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/schema.mdx` | Fly schema reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/lib/fly-oidc.mdx` | Fly OIDC reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/module.mdx` | Cloudflare module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/schema.mdx` | Cloudflare schema reference | - ---- - -### 4. Current Deployment Config from .stack/config.nix - -From `/Users/cm/git/darkmatter/stack/.stack/config.nix`: - -**Deployment section** (lines 218-223): -```nix -deployment = { - fly = { - organization = "darkmatter"; - }; -}; -``` - -**Per-app deployment** -- `web` app (lines 76-96): -```nix -web = { - deployment = { - bindings = [ "DATABASE_URL" "CORS_ORIGIN" "BETTER_AUTH_SECRET" "BETTER_AUTH_URL" "POLAR_ACCESS_TOKEN" "POLAR_SUCCESS_URL" ]; - enable = true; - host = "fly"; - secrets = [ "DATABASE_URL" "BETTER_AUTH_SECRET" "POLAR_ACCESS_TOKEN" ]; - fly = { - appName = "stack-web"; - region = "iad"; - }; - }; - framework = { tanstack-start = { enable = true; }; }; - container = { enable = true; type = "bun"; }; -}; -``` - -**Infra section** (lines 447-475): -```nix -infra = { - enable = true; - storage-backend = { - type = "sops"; - sops.group = "dev"; - }; - database = { - enable = true; - provider = "neon"; - neon = { region = "aws-us-east-1"; api-key-ssm-path = "/common/neon-api-key"; }; - }; - machines = { - enable = true; - source = "aws-ec2"; - aws = { - region = "us-west-2"; - filters = [{ name = "instance-state-name"; values = [ "running" ]; }]; - }; - machines = {}; - }; -}; -``` - -**SST section** (lines 480-509): -```nix -sst = { - account-id = "950224716579"; - config-path = "packages/infra/sst.config.ts"; - enable = true; - iam = { role-name = "stack-secrets-role"; }; - kms = { alias = "stack-secrets"; deletion-window-days = 30; enable = true; }; - oidc = { - flyio = { app-name = "*"; org-id = ""; }; - github-actions = { branch = "*"; org = "darkmatter"; repo = "stack"; }; - provider = "github-actions"; - roles-anywhere = { trust-anchor-arn = ""; }; - }; - project-name = "stack"; - region = "us-west-2"; -}; -``` - ---- - -### 5. How the Studio UI Handles Infrastructure/Deployment Setup - -The studio UI has **two separate panels** for deployment, plus a setup wizard step: - -**A. Deploy Panel (`/studio/deploy`)** -- Colmena-based NixOS deployment to machines: -- Reads machine inventory from `nixConfig.colmena.machinesComputed` and app deploy configs from `nixConfig.apps.*.deploy` -- Displays a **machine inventory grid** (MachineCard components showing hostname, SSH, arch, provider, tags, roles) -- Shows **app-to-machine mapping** (which apps deploy to which machines, resolved by targets/roles) -- Provides **Colmena actions**: eval, build, apply buttons -- Displays **settings** for Colmena config (machine source, hive config path, etc.) -- Data flow: Agent evaluates Nix config -> serializes colmena data -> SSE events -> React queries `useNixConfig()` - -**B. Infra Panel (`/studio/infra`)** -- SST/AWS infrastructure provisioning: -- Uses `useSSTConfig()` hook that reads from `useNixData("sst")` for persistent config and calls agent REST endpoints (`getSSTStatus`, `getSSTOutputs`, `getSSTResources`, `deploySSTInfra`, `removeSSTInfra`) -- **Status tab**: shows project overview, IAM role, KMS key, config path status -- **Deploy tab**: stage selector (dev/staging/production) + CommandRunner that executes `sst deploy --stage ` or `sst remove --stage ` via the agent -- **Outputs tab**: displays stack outputs (ARNs, URLs) with copy-to-clipboard -- **Resources tab**: lists deployed AWS resources by type/URN -- **Configure tab**: full SST config editor (enable, project name, region, account ID, KMS settings, OIDC provider, IAM role). Links to the Setup wizard for initial configuration. - -**C. Setup Wizard Infrastructure Step** (`infrastructure-step.tsx`): -- Part of the Project Setup wizard at `/studio/setup` -- Guided step for initial AWS infrastructure config -- Shows a table of what gets deployed (IAM Role, KMS Key, SST Config) -- Form: enable toggle, AWS region/account, OIDC provider (GitHub Actions with org/repo), KMS key toggle + alias -- Saves to `.stack/data/sst.nix` via the agent - -**D. Per-App Deployment Tab** (within the Apps panel): -- Each expanded app has a "Deployment" tab (nav item at line 52 of app-expanded-content.tsx) -- Deployment panels are filtered by module: `deployment-fly` and `deployment-cloudflare` panels are separated from generic module panels (apps-panel-alt.tsx lines 88-101) -- These panels render the per-app deployment config form via `AppConfigFormRenderer` using the schema defined in the Nix module's `ui.nix` - - -session_id: ses_35680e048ffea12FLMy7pJZVXe - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.6s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers & Deployment -00003| description: Build containers and deploy your applications to production -00004| icon: cloud -00005| --- -00006| -00007| import { Cards, Card } from "fumadocs-ui/components/card"; -00008| import { Callout } from "fumadocs-ui/components/callout"; -00009| -00010| Stack supports building containers and deploying to multiple platforms—all configured from the same Nix source of truth as your dev environment. Container definitions, deployment targets, and infrastructure provisioning are declared alongside your services and apps, so everything stays in sync. -00011| -00012| ## The Deployment Story -00013| -00014| Stack's approach to deployment follows the same philosophy as everything else: **declare it once, generate the artifacts your tools expect.** -00015| -00016| - Need a `Dockerfile`? Stack generates one optimized for your stack. -00017| - Need a Cloudflare Workers config? Declared in Nix, generated as standard config files. -00018| - Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up. -00019| -00020| The generated files are standard—`Dockerfile`, `fly.toml`, `sst.config.ts`—committed to Git and usable without Nix. Your CI pipeline reads them as normal config. If you eject from Stack, the files stay. -00021| -00022| ## What's Available -00023| -00024| -00025| -00026| Build OCI-compatible container images using Nix or Dockerfile generation -00027| -00028| -00029| Deploy to Cloudflare Workers and Pages -00030| -00031| -00032| Deploy to Fly.io with generated fly.toml and Dockerfile -00033| -00034| -00035| AWS infrastructure provisioning with KMS, IAM, and OIDC authentication -00036| -00037| -00038| -00039| ## Quick Example -00040| -00041| A typical deployment setup might combine containers with a deployment target: -00042| -00043| ```nix -00044| { -00045| # Define your apps -00046| stack.apps.api = { -00047| port = 1; -00048| root = "./apps/api"; -00049| build = "bun run build"; -00050| }; -00051| -00052| # Build a container for the API -00053| stack.containers.api = { -00054| enable = true; -00055| app = "api"; -00056| registry = "ghcr.io/my-org/myapp-api"; -00057| }; -00058| -00059| # Deploy to Fly.io -00060| stack.deployment.fly = { -00061| enable = true; -00062| apps.api = { -00063| region = "iad"; -00064| vm-size = "shared-cpu-1x"; -00065| }; -00066| }; -00067| } -00068| ``` -00069| -00070| From this, Stack generates a `Dockerfile` for the API, a `fly.toml` with the correct configuration, and optionally CI steps that build and deploy on merge to `main`. -00071| -00072| ## How Deployment Connects to CI -00073| -00074| Deployment configuration feeds into [CI generation](/docs/apps-ci/ci-generation). When you define a deployment target and enable the CI extension, the generated GitHub Actions workflow includes the appropriate deploy steps: -00075| -00076| ``` -00077| stack.apps.api.container -00078| → Generates container image -00079| → CI workflow gets docker build + push steps -00080| -00081| stack.apps.api.deployment.host = "fly" -00082| → Generates fly.toml -00083| → CI workflow gets flyctl deploy step -00084| -00085| stack.apps.api.deployment.host = "cloudflare" -00086| → Generates wrangler config -00087| → CI workflow gets cloudflare deploy step -00088| ``` -00089| -00090| Each deployment platform adds its own steps to the CI pipeline. Multiple platforms can coexist—deploy your frontend to Cloudflare and your API to Fly.io from the same workflow. -00091| -00092| -00093| All deployment configuration generates standard files that work without Nix. Your CI runners don't need Nix installed—they use Docker, flyctl, wrangler, or sst directly. -00094| -00095| -00096| ## Container Strategies -00097| -00098| Stack supports two approaches to building containers: -00099| -00100| | Strategy | How It Works | Best For | -00101| |----------|-------------|----------| -00102| | **Dockerfile generation** | Generates a standard `Dockerfile` optimized for your stack | Most projects, standard CI/CD pipelines | -00103| | **Nix-based containers** | Uses `nix2container` to build minimal OCI images directly from Nix | Minimal image size, reproducible builds | -00104| -00105| The Dockerfile approach is more portable—any Docker-compatible CI can build it. The Nix approach produces smaller, more reproducible images but requires Nix at build time. See [Containers](./containers) for details on both strategies. -00106| -00107| ## Platform-Specific Notes -00108| -00109| ### Cloudflare -00110| -00111| Cloudflare Workers and Pages deployments are lightweight—no containers involved. Stack generates the `wrangler.toml` configuration and can produce the correct build output format for your framework (static export for Pages, Worker bundles for Workers). -00112| -00113| ### Fly.io -00114| -00115| Fly.io deployments use containers. Stack generates both the `Dockerfile` and `fly.toml`, with the correct ports, health checks, and environment variables wired up from your Stack config. -00116| -00117| ### SST (AWS) -00118| -00119| SST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. -00120| -00121| ## Reference -00122| -00123| - [Options Reference → CI](/docs/reference/ci) for CI generation options -00124| - [Apps & CI](/docs/apps-ci) for how app definitions drive deployment -00125| - [Core Concepts → Building Source](/docs/concepts/building-source) for why deployment config is generated -00126| - [Core Concepts → File Generation](/docs/concepts/file-generation) for how generated files work - -(End of file - total 126 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Fly.io -00003| description: Deploy applications to Fly.io with generated configuration and OIDC authentication -00004| icon: plane -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can generate Fly.io deployment configuration from your project definition—`fly.toml` files, Dockerfiles, and OIDC-based authentication for passwordless deploys from CI. Instead of hand-maintaining Fly config that drifts from your actual app setup, the deployment extension derives everything from your Stack config. -00010| -00011| ## Enable Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Fly extension" - just configure your apps with `deployment.host = "fly"`. -00014| -00015| Stack will automatically add the `flyctl` CLI to your devshell when you have apps configured for Fly.io deployment. -00016| -00017| ## Defining Fly Apps -00018| -00019| Each app that should be deployed to Fly needs deployment configuration: -00020| -00021| ```nix -00022| stack.apps = { -00023| api = { -00024| port = 1; -00025| root = "./apps/api"; -00026| build = "bun run build"; -00027| -00028| deployment = { -00029| enable = true; -00030| host = "fly"; -00031| -00032| fly = { -00033| appName = "myapp-api"; -00034| region = "iad"; -00035| memory = "512mb"; -00036| cpus = 1; -00037| forceHttps = true; -00038| }; -00039| }; -00040| }; -00041| -00042| web = { -00043| port = 0; -00044| root = "./apps/web"; -00045| build = "bun run build"; -00046| -00047| deployment = { -00048| enable = true; -00049| host = "fly"; -00050| -00051| fly = { -00052| appName = "myapp-web"; -00053| region = "iad"; -00054| }; -00055| }; -00056| }; -00057| }; -00058| ``` -00059| -00060| Stack generates deployment configuration for each app, wiring up the correct ports, health checks, and resource settings. -00061| -00062| ## Generated Files -00063| -00064| When the Fly extension is enabled, Stack generates: -00065| -00066| | File | Description | -00067| |------|-------------| -00068| | `apps//fly.toml` | Fly.io app configuration | -00069| | `apps//Dockerfile` | Container image (if not already defined) | -00070| -00071| These are standard Fly.io files—`flyctl` reads them directly. Like all Stack-generated files, they're committed to Git so that deploys work without Nix. -00072| -00073| ```nix -00074| # The extension generates fly.toml from your config -00075| stack.files.entries."apps/api/fly.toml" = { -00076| # ... generated automatically from deployment.fly settings -00077| }; -00078| ``` -00079| -00080| -00081| You can inspect the generated `fly.toml` at any time. It's a standard file that `flyctl` understands—no proprietary format or wrapper. -00082| -00083| -00084| ## Container Builds -00085| -00086| Fly.io deploys container images. Configure containers alongside your app deployment: -00087| -00088| ```nix -00089| stack.apps.api = { -00090| port = 1; -00091| root = "./apps/api"; -00092| build = "bun run build"; -00093| -00094| deployment = { -00095| enable = true; -00096| host = "fly"; -00097| -00098| fly = { -00099| appName = "myapp-api"; -00100| region = "iad"; -00101| }; -00102| }; -00103| -00104| container = { -00105| enable = true; -00106| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00107| }; -00108| }; -00109| ``` -00110| -00111| Or define containers separately using the [containers module](/docs/deployment/containers) with matching names. -00112| -00113| ## OIDC Authentication -00114| -00115| For CI/CD deployments, configure OIDC authentication at the global deployment level: -00116| -00117| ```nix -00118| stack.deployment = { -00119| fly = { -00120| organization = "my-fly-org"; -00121| }; -00122| }; -00123| ``` -00124| -00125| When configured, the [CI generation](/docs/apps-ci/ci-generation) can produce GitHub Actions steps that authenticate with Fly using GitHub's OIDC token. This means: -00126| -00127| - No `FLY_API_TOKEN` secret to manage -00128| - Tokens are short-lived and scoped to each CI run -00129| - Authentication is tied to your GitHub org/repo identity -00130| - No token rotation or expiration to worry about -00131| -00132| ```yaml -00133| # Generated CI step (simplified) -00134| - name: Deploy to Fly -00135| uses: superfly/flyctl-actions/setup-flyctl@master -00136| - run: flyctl deploy --remote-only -00137| env: -00138| FLY_API_TOKEN: ${{ steps.fly-oidc.outputs.token }} -00139| ``` -00140| -00141| -00142| OIDC authentication requires your Fly.io organization to have OIDC configured with your GitHub repository. See the [Fly.io OIDC docs](https://fly.io/docs/reference/openid-connect/) for setup instructions. -00143| -00144| -00145| ## Multi-Region Deployment -00146| -00147| Deploy to multiple Fly.io regions for lower latency: -00148| -00149| ```nix -00150| stack.apps.api.deployment.fly = { -00151| appName = "myapp-api"; -00152| region = "iad"; -00153| -00154| # Note: Multi-region configuration is managed through Fly.io directly -00155| # Set your primary region here and use flyctl to scale to additional regions -00156| }; -00157| ``` -00158| -00159| After deploying to your primary region, use `flyctl scale` to add machines in additional regions. -00160| -00161| ## Environment and Secrets -00162| -00163| Configure environment variables and secrets for your Fly.io deployments: -00164| -00165| ```nix -00166| stack.apps.api.deployment.fly = { -00167| appName = "myapp-api"; -00168| -00169| # These get set as Fly secrets -00170| secrets = [ -00171| "DATABASE_URL" -00172| "API_KEY" -00173| "STRIPE_SECRET_KEY" -00174| ]; -00175| -00176| # Static environment variables (non-secret) -00177| env = { -00178| NODE_ENV = "production"; -00179| LOG_LEVEL = "info"; -00180| }; -00181| }; -00182| ``` -00183| -00184| Environment variables are included in deployment configuration. Secrets are managed through Stack's [secrets system](/docs/secrets/variables) and synced to Fly.io. -00185| -00186| ## Deployment Scripts -00187| -00188| The Fly extension registers scripts for common deployment tasks: -00189| -00190| ```bash -00191| # Deploy a specific app -00192| fly:deploy api -00193| -00194| # Deploy all Fly-configured apps -00195| fly:deploy-all -00196| -00197| # Check deployment status -00198| fly:status -00199| -00200| # Open the Fly dashboard -00201| fly:dashboard -00202| ``` -00203| -00204| These are standard Stack [scripts](/docs/dev-environment/packages-scripts) that wrap `flyctl` with the correct app names and configuration. -00205| -00206| ## Databases on Fly -00207| -00208| For managed databases, use Fly's CLI to create Postgres instances, then reference them in your app: -00209| -00210| ```bash -00211| # Create a Postgres cluster -00212| flyctl postgres create --name myapp-db --region iad -00213| -00214| # Attach it to your app -00215| flyctl postgres attach myapp-db --app myapp-api -00216| ``` -00217| -00218| Then reference the database in your deployment config: -00219| -00220| ```nix -00221| stack.apps.api.deployment.fly = { -00222| appName = "myapp-api"; -00223| -00224| secrets = [ -00225| "DATABASE_URL" # Automatically set by Fly when attached -00226| ]; -00227| }; -00228| ``` -00229| -00230| -00231| For local development, use Stack's built-in [PostgreSQL service](/docs/services/postgres) instead of Fly Postgres. -00232| -00233| -00234| ## Combining with Other Deployment Targets -00235| -00236| You can deploy different apps in your monorepo to different platforms: -00237| -00238| ```nix -00239| stack.apps = { -00240| web = { -00241| port = 0; -00242| # Deploy the frontend to Cloudflare -00243| deployment = { -00244| enable = true; -00245| host = "cloudflare"; -00246| }; -00247| }; -00248| -00249| api = { -00250| port = 1; -00251| # Deploy the API to Fly.io -00252| deployment = { -00253| enable = true; -00254| host = "fly"; -00255| fly.appName = "myapp-api"; -00256| }; -00257| }; -00258| }; -00259| ``` -00260| -00261| Each deployment target generates its own configuration independently. The [CI generation](/docs/apps-ci/ci-generation) produces workflow steps for all enabled deployment targets. -00262| -00263| ## CLI Commands -00264| -00265| When you have apps configured for Fly.io deployment, `flyctl` is automatically added to your devshell: -00266| -00267| ```bash -00268| # Direct flyctl access -00269| flyctl status --app myapp-api -00270| flyctl logs --app myapp-api -00271| flyctl ssh console --app myapp-api -00272| -00273| # Build containers for deployment -00274| nix build .#packages.x86_64-linux.container-api -00275| ``` -00276| -00277| ## Troubleshooting -00278| -00279| ### "App not found" errors -00280| -00281| Make sure the Fly app exists. On first deployment, you may need to create it: -00282| -00283| ```bash -00284| flyctl apps create myapp-api --org my-org -00285| ``` -00286| -00287| Or let `flyctl deploy` create it automatically on the first run. -00288| -00289| ### Build failures in CI -00290| -00291| Check that the generated Dockerfile is correct: -00292| -00293| ```bash -00294| cat apps/api/Dockerfile -00295| ``` -00296| -00297| If the auto-generated Dockerfile doesn't work for your app, you can provide a custom one and point the extension at it. -00298| -00299| ### OIDC token errors -00300| -00301| Verify that your Fly.io organization has OIDC configured for your GitHub repository. The OIDC subject must match what Fly expects—typically `repo:/:ref:refs/heads/main` or similar. -00302| -00303| ```bash -00304| # Test OIDC locally (requires flyctl >= 0.1.130) -00305| flyctl auth token --oidc -00306| ``` -00307| -00308| ### Secrets not available in the deployed app -00309| -00310| Fly secrets are set separately from `fly.toml` environment variables. Make sure secrets are actually set: -00311| -00312| ```bash -00313| flyctl secrets list --app myapp-api -00314| ``` -00315| -00316| If a secret is missing, set it manually or re-run the deployment script that syncs Stack variables to Fly secrets. -00317| -00318| ## Reference -00319| -00320| - [Options Reference → Deployment](/docs/reference) for Fly deployment options -00321| - [Containers](/docs/deployment/containers) for container image configuration -00322| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00323| - [Fly.io documentation](https://fly.io/docs/) for platform-specific details -00324| - [Fly.io OIDC](https://fly.io/docs/reference/openid-connect/) for passwordless CI authentication - -(End of file - total 324 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Cloudflare -00003| description: Deploy applications to Cloudflare Workers and Pages -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack includes a Cloudflare deployment module that generates the configuration needed to deploy your applications to [Cloudflare Workers](https://workers.cloudflare.com/) and [Cloudflare Pages](https://pages.cloudflare.com/). Like everything in Stack, the deployment config is derived from your project's source of truth—no hand-maintained `wrangler.toml` files that drift out of sync. -00010| -00011| ## Enable Cloudflare Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Cloudflare extension" - just configure your apps with `deployment.host = "cloudflare"`. -00014| -00015| Stack will automatically add the `wrangler` CLI to your devshell when you have apps configured for Cloudflare deployment. -00016| -00017| ## Configuration -00018| -00019| ### Global Settings -00020| -00021| Set your Cloudflare account ID at the global deployment level: -00022| -00023| ```nix -00024| stack.deployment = { -00025| cloudflare = { -00026| accountId = "your-cloudflare-account-id"; -00027| compatibilityDate = "2024-01-01"; -00028| }; -00029| }; -00030| ``` -00031| -00032| ### Per-App Deployment -00033| -00034| Configure each app with Cloudflare-specific deployment settings: -00035| -00036| ```nix -00037| stack.apps.web = { -00038| port = 0; -00039| framework = "tanstack-start"; -00040| root = "./apps/web"; -00041| build = "bun run build"; -00042| -00043| deployment = { -00044| enable = true; -00045| host = "cloudflare"; -00046| -00047| cloudflare = { -00048| workerName = "myapp-web"; -00049| type = "CLOUDFLARE_WORKER_TYPE_VITE"; -00050| route = "myapp.com/*"; -00051| bindings = { -00052| DATABASE_URL = "..."; -00053| }; -00054| }; -00055| }; -00056| }; -00057| ``` -00058| -00059| ### Environment Variables -00060| -00061| Pass environment variables to your Workers at deploy time: -00062| -00063| ```nix -00064| stack.apps.api = { -00065| port = 1; -00066| root = "./apps/api"; -00067| -00068| deployment = { -00069| enable = true; -00070| host = "cloudflare"; -00071| -00072| cloudflare = { -00073| workerName = "myapp-api"; -00074| bindings = { -00075| APP_ENV = "production"; -00076| API_VERSION = "v1"; -00077| }; -00078| }; -00079| }; -00080| }; -00081| ``` -00082| -00083| For sensitive values, use Cloudflare's secret bindings instead of plain vars. You can set these through the Wrangler CLI: -00084| -00085| ```bash -00086| wrangler secret put API_KEY --name myapp-api -00087| ``` -00088| -00089| -00090| Never put sensitive values in `vars`—they're visible in your generated config and committed to Git. Use Wrangler secrets or Stack's [secrets system](/docs/secrets) for anything confidential. -00091| -00092| -00093| ## Generated Files -00094| -00095| The Cloudflare module generates standard Wrangler configuration files: -00096| -00097| ``` -00098| wrangler.toml # or per-app: apps/web/wrangler.toml -00099| ``` -00100| -00101| These are normal Wrangler config files that the `wrangler` CLI reads directly. If you eject from Stack, the files stay and work as-is. -00102| -00103| Like all Stack-generated files, they're committed to Git so that team members and CI can deploy without Nix. -00104| -00105| ## Scripts -00106| -00107| When Cloudflare deployment is enabled, the following scripts are registered: -00108| -00109| | Script | Description | -00110| |--------|-------------| -00111| | `cloudflare:deploy` | Deploy all configured Workers | -00112| | `cloudflare:deploy:` | Deploy a specific Worker | -00113| | `cloudflare:dev` | Start Wrangler dev mode | -00114| | `cloudflare:tail` | Tail Worker logs in real time | -00115| -00116| ```bash -00117| # Deploy everything -00118| cloudflare:deploy -00119| -00120| # Deploy just the API -00121| cloudflare:deploy:api -00122| -00123| # Start local dev with Wrangler -00124| cloudflare:dev -00125| -00126| # Tail production logs -00127| cloudflare:tail -00128| ``` -00129| -00130| ## CI Integration -00131| -00132| When you have apps configured for Cloudflare deployment and [CI generation](/docs/apps-ci/ci-generation) is enabled, the generated GitHub Actions workflow includes Cloudflare deploy steps: -00133| -00134| ```nix -00135| { -00136| stack.deployment = { -00137| cloudflare = { -00138| accountId = "your-account-id"; -00139| }; -00140| }; -00141| -00142| stack.apps.api = { -00143| port = 1; -00144| deployment = { -00145| enable = true; -00146| host = "cloudflare"; -00147| cloudflare.workerName = "myapp-api"; -00148| }; -00149| }; -00150| -00151| stack.ci = { -00152| enable = true; -00153| secrets = { -00154| "CLOUDFLARE_API_TOKEN" = "CF_API_TOKEN"; # GitHub secret name -00155| }; -00156| }; -00157| } -00158| ``` -00159| -00160| The generated workflow uses `wrangler deploy` with proper authentication via the `CLOUDFLARE_API_TOKEN` GitHub Actions secret. -00161| -00162| -00163| You'll need to create a Cloudflare API token with the appropriate permissions and add it as a GitHub Actions secret. See the [Cloudflare docs](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) for token creation. -00164| -00165| -00166| ## Workers with Bindings -00167| -00168| Cloudflare Workers support various bindings—KV namespaces, R2 buckets, D1 databases, Durable Objects, and more. Configure them through the module: -00169| -00170| ```nix -00171| stack.apps.api = { -00172| port = 1; -00173| -00174| deployment = { -00175| enable = true; -00176| host = "cloudflare"; -00177| -00178| cloudflare = { -00179| workerName = "myapp-api"; -00180| kvNamespaces = [ "CACHE" ]; -00181| r2Buckets = [ "UPLOADS" ]; -00182| d1Databases = [ "DB" ]; -00183| bindings = { -00184| CACHE = "abc123"; -00185| DB = "def456"; -00186| }; -00187| }; -00188| }; -00189| }; -00190| ``` -00191| -00192| These bindings are written into the generated `wrangler.toml` and available as typed bindings in your Worker code. -00193| -00194| ## Pages Deployment -00195| -00196| For static sites or frameworks with static export (like Next.js static export or Astro), use Cloudflare Pages: -00197| -00198| ```nix -00199| stack.apps.docs = { -00200| port = 2; -00201| -00202| deployment = { -00203| enable = true; -00204| host = "cloudflare"; -00205| -00206| cloudflare = { -00207| workerName = "myapp-docs"; -00208| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00209| }; -00210| }; -00211| }; -00212| ``` -00213| -00214| Pages deployments use `wrangler pages deploy` under the hood. -00215| -00216| ## Local Development with Wrangler -00217| -00218| The Cloudflare module adds `wrangler` to your devshell, so you can use Wrangler's local development features alongside Stack's dev environment: -00219| -00220| ```bash -00221| # Start Wrangler dev server (uses miniflare locally) -00222| wrangler dev --config apps/api/wrangler.toml -00223| -00224| # Or use the registered script -00225| cloudflare:dev -00226| ``` -00227| -00228| Wrangler's dev mode emulates the Workers runtime locally, including KV, R2, D1, and other bindings. This complements Stack's [process-compose](/docs/services/process-compose) setup—you can run Wrangler alongside your other local services. -00229| -00230| ## Combining with Other Deployment Targets -00231| -00232| Not every app in your project needs to deploy to Cloudflare. You can mix deployment targets: -00233| -00234| ```nix -00235| { -00236| stack.apps = { -00237| web = { -00238| port = 0; -00239| framework = "nextjs"; -00240| deployment = { -00241| enable = true; -00242| host = "fly"; -00243| fly.appName = "myapp-web"; -00244| }; -00245| }; -00246| -00247| api = { -00248| port = 1; -00249| framework = "hono"; -00250| deployment = { -00251| enable = true; -00252| host = "cloudflare"; -00253| cloudflare.workerName = "myapp-api"; -00254| }; -00255| }; -00256| -00257| docs = { -00258| port = 2; -00259| framework = "nextjs"; -00260| deployment = { -00261| enable = true; -00262| host = "cloudflare"; -00263| cloudflare = { -00264| workerName = "myapp-docs"; -00265| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00266| }; -00267| }; -00268| }; -00269| }; -00270| } -00271| ``` -00272| -00273| Each deployment target generates its own config files and scripts independently. -00274| -00275| ## Troubleshooting -00276| -00277| ### "Authentication error" during deploy -00278| -00279| Make sure your Cloudflare API token is set. For local deploys: -00280| -00281| ```bash -00282| export CLOUDFLARE_API_TOKEN="your-token" -00283| cloudflare:deploy -00284| ``` -00285| -00286| Or log in interactively: -00287| -00288| ```bash -00289| wrangler login -00290| ``` -00291| -00292| ### "Worker not found" errors -00293| -00294| Verify the Worker name matches what's in your Cloudflare dashboard. Workers are created on first deploy if they don't exist, but the account ID must be correct. -00295| -00296| ### Generated `wrangler.toml` not updating -00297| -00298| If your Stack config changed but the generated file hasn't, trigger a rebuild: -00299| -00300| ```bash -00301| # The agent watches for changes, but you can force a rebuild -00302| stack status -00303| ``` -00304| -00305| Or check the file status in [Studio](/docs/studio) to see if it's marked as stale. -00306| -00307| ## Reference -00308| -00309| - [Options Reference → CI](/docs/reference/ci) for CI configuration that includes Cloudflare deploys -00310| - [Apps & CI → CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00311| - [Fly.io](./fly) for an alternative deployment target -00312| - [SST](./sst) for AWS infrastructure provisioning -00313| - [Cloudflare Workers docs](https://developers.cloudflare.com/workers/) for Workers-specific configuration - -(End of file - total 313 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: SST -00003| description: AWS infrastructure provisioning with SST and OIDC-based authentication -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| [SST](https://sst.dev/) is an infrastructure-as-code framework for deploying to AWS. Stack's SST extension generates configuration, manages IAM roles with OIDC authentication, and provisions KMS keys for secrets encryption—all from your Nix config. -00010| -00011| ## Enable SST -00012| -00013| ```nix -00014| stack.sst = { -00015| enable = true; -00016| project-name = "myapp"; -00017| region = "us-west-2"; -00018| }; -00019| ``` -00020| -00021| When enabled, the SST extension: -00022| -00023| - Adds the SST CLI to your `PATH` -00024| - Generates an `sst.config.ts` from your Nix configuration -00025| - Registers `sst:deploy`, `sst:dev`, and other scripts in your devshell -00026| - Optionally provisions KMS keys and IAM roles for secure deployments -00027| -00028| ## Configuration -00029| -00030| ### Basic Setup -00031| -00032| ```nix -00033| stack.sst = { -00034| enable = true; -00035| project-name = "myapp"; -00036| region = "us-west-2"; -00037| }; -00038| ``` -00039| -00040| This generates a minimal `sst.config.ts` that you can extend with your own infrastructure definitions. -00041| -00042| ### KMS for Secrets Encryption -00043| -00044| SST can provision a KMS key for encrypting secrets at rest: -00045| -00046| ```nix -00047| stack.sst = { -00048| enable = true; -00049| project-name = "myapp"; -00050| region = "us-west-2"; -00051| kms.enable = true; -00052| }; -00053| ``` -00054| -00055| The KMS key integrates with Stack's [secrets system](/docs/secrets)—secrets encrypted with this key can be decrypted in your deployed application using the IAM role's KMS permissions. -00056| -00057| ### OIDC Authentication -00058| -00059| Instead of long-lived AWS credentials, the SST extension supports OIDC-based authentication. This gives you short-lived, automatically rotated credentials for CI/CD and local development. -00060| -00061| #### GitHub Actions OIDC -00062| -00063| ```nix -00064| stack.sst = { -00065| enable = true; -00066| project-name = "myapp"; -00067| region = "us-west-2"; -00068| -00069| oidc = { -00070| provider = "github-actions"; -00071| github-actions = { -00072| org = "my-org"; -00073| repo = "my-repo"; -00074| }; -00075| }; -00076| }; -00077| ``` -00078| -00079| This provisions: -00080| -00081| - An IAM OIDC identity provider for GitHub Actions -00082| - An IAM role that GitHub Actions can assume via OIDC -00083| - Trust policies scoped to your specific org and repo -00084| -00085| Your [CI workflows](/docs/apps-ci/ci-generation) automatically get the correct role ARN and OIDC configuration—no manual secret management. -00086| -00087| #### Fly.io OIDC -00088| -00089| ```nix -00090| stack.sst = { -00091| enable = true; -00092| project-name = "myapp"; -00093| region = "us-west-2"; -00094| -00095| oidc = { -00096| provider = "fly"; -00097| fly = { -00098| org = "my-fly-org"; -00099| }; -00100| }; -00101| }; -00102| ``` -00103| -00104| #### AWS Roles Anywhere -00105| -00106| For environments that don't support OIDC natively, use [AWS Roles Anywhere](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html) with mTLS certificates from [Step CA](/docs/networking/certificates): -00107| -00108| ```nix -00109| stack.sst = { -00110| enable = true; -00111| project-name = "myapp"; -00112| region = "us-west-2"; -00113| -00114| oidc = { -00115| provider = "roles-anywhere"; -00116| }; -00117| }; -00118| -00119| stack.aws.roles-anywhere = { -00120| enable = true; -00121| region = "us-west-2"; -00122| account-id = "123456789012"; -00123| role-name = "dev-role"; -00124| trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:trust-anchor/abc123"; -00125| profile-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:profile/def456"; -00126| }; -00127| ``` -00128| -00129| This provisions certificate-based AWS authentication through your team's [Step CA](/docs/networking/certificates) instance, giving every developer short-lived AWS credentials without any shared secrets. -00130| -00131| -00132| Roles Anywhere is the most secure option for local development—each developer gets their own short-lived certificate, and all access is logged in CloudTrail. It requires a [Step CA](/docs/networking/certificates) instance but provides the strongest security guarantees. -00133| -00134| -00135| ## Generated Files -00136| -00137| The SST extension generates: -00138| -00139| | File | Purpose | -00140| |------|---------| -00141| | `sst.config.ts` | SST project configuration with app name, region, and settings | -00142| -00143| Like all Stack-generated files, `sst.config.ts` is committed to Git and works without Nix. If you eject from Stack, you have a standard SST project. -00144| -00145| -00146| Don't edit `sst.config.ts` by hand—your changes will be overwritten on the next build. Add custom infrastructure in separate files that SST imports, or configure everything through the Nix options. -00147| -00148| -00149| ## Scripts -00150| -00151| When SST is enabled, the following scripts are available in your devshell: -00152| -00153| | Script | Description | -00154| |--------|-------------| -00155| | `sst:dev` | Start SST in development mode with live Lambda reloading | -00156| | `sst:deploy` | Deploy infrastructure to AWS | -00157| | `sst:remove` | Remove all deployed resources | -00158| -00159| These scripts handle credential injection automatically—whether you're using OIDC, Roles Anywhere, or environment-based credentials. -00160| -00161| ## Using with CI -00162| -00163| The SST extension integrates with [CI generation](/docs/apps-ci/ci-generation). When both are enabled, the generated GitHub Actions workflow includes: -00164| -00165| - AWS OIDC credential setup (if GitHub Actions OIDC is configured) -00166| - SST deploy steps with the correct region and project name -00167| - Environment variable injection for secrets -00168| -00169| ```nix -00170| { -00171| stack.sst = { -00172| enable = true; -00173| project-name = "myapp"; -00174| region = "us-west-2"; -00175| oidc.provider = "github-actions"; -00176| oidc.github-actions = { -00177| org = "my-org"; -00178| repo = "my-repo"; -00179| }; -00180| }; -00181| -00182| stack.ci.enable = true; -00183| } -00184| ``` -00185| -00186| This generates a CI pipeline that authenticates to AWS via OIDC and deploys your SST infrastructure—no long-lived AWS credentials stored in GitHub Secrets. -00187| -00188| ## Full Example -00189| -00190| A complete SST setup with KMS, OIDC, and secrets: -00191| -00192| ```nix -00193| { -00194| # SST infrastructure -00195| stack.sst = { -00196| enable = true; -00197| project-name = "myapp"; -00198| region = "us-west-2"; -00199| -00200| # KMS key for secrets encryption -00201| kms.enable = true; -00202| -00203| # GitHub Actions OIDC for CI deployments -00204| oidc = { -00205| provider = "github-actions"; -00206| github-actions = { -00207| org = "my-org"; -00208| repo = "my-repo"; -00209| }; -00210| }; -00211| }; -00212| -00213| # Local AWS access via Roles Anywhere -00214| stack.aws.roles-anywhere = { -00215| enable = true; -00216| region = "us-west-2"; -00217| account-id = "123456789012"; -00218| role-name = "dev-role"; -00219| trust-anchor-arn = "arn:aws:rolesanywhere:..."; -00220| profile-arn = "arn:aws:rolesanywhere:..."; -00221| }; -00222| -00223| # Secrets encrypted with KMS -00224| stack.variables."/prod/api-key" = { -00225| key = "API_KEY"; -00226| type = "SECRET"; -00227| master-keys = [ "prod" ]; -00228| }; -00229| -00230| # CI pipeline with SST deploy -00231| stack.ci.enable = true; -00232| } -00233| ``` -00234| -00235| ## Troubleshooting -00236| -00237| ### "Unable to assume role" in CI -00238| -00239| Verify your OIDC configuration matches your GitHub repository: -00240| -00241| ```nix -00242| oidc.github-actions = { -00243| org = "my-org"; # Must match your GitHub org exactly -00244| repo = "my-repo"; # Must match your GitHub repo name exactly -00245| }; -00246| ``` -00247| -00248| Also confirm the IAM OIDC provider and role have been provisioned. The SST extension creates these resources on first deploy—you may need to run `sst deploy` locally with admin credentials once to bootstrap. -00249| -00250| ### "KMS key not found" -00251| -00252| If KMS is enabled but not yet provisioned, run the initial deployment: -00253| -00254| ```bash -00255| sst:deploy -00256| ``` -00257| -00258| The KMS key is created as part of the SST infrastructure stack. After the first deploy, it's available for encrypting secrets. -00259| -00260| ### SST dev mode not connecting -00261| -00262| Ensure your AWS credentials are working: -00263| -00264| ```bash -00265| aws sts get-caller-identity -00266| ``` -00267| -00268| If using Roles Anywhere, verify your [Step CA](/docs/networking/certificates) certificate is valid and the trust anchor is configured correctly. -00269| -00270| ## Reference -00271| -00272| - [Options Reference → AWS](/docs/reference/aws) for AWS Roles Anywhere configuration -00273| - [Secrets](/docs/secrets) for encrypted secrets management -00274| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00275| - [Certificates](/docs/networking/certificates) for Step CA and Roles Anywhere -00276| - [SST documentation](https://sst.dev/docs) for SST-specific configuration - -(End of file - total 276 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers -00003| description: Build OCI containers from your Nix configuration -00004| icon: box -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can build OCI-compatible container images directly from your Nix configuration—no Dockerfile required. Images are built with [nix2container](https://github.com/nlewo/nix2container), producing minimal, reproducible containers with only the dependencies your application actually needs. -00010| -00011| ## Why Nix-Built Containers? -00012| -00013| Traditional Dockerfiles are imperative scripts that install packages, copy files, and run commands. The result depends on the base image version, network availability during build, and the order of operations. Two builds of the same Dockerfile can produce different images. -00014| -00015| Nix-built containers are **declarative and reproducible**. You describe what goes in the image, and Nix computes the exact result. The same input always produces the same image, byte-for-byte. -00016| -00017| Additional benefits: -00018| -00019| - **Minimal images** — Only your application and its runtime dependencies. No package manager, no shell, no leftover build tools. -00020| - **Layer caching** — Nix automatically splits your image into layers based on the dependency graph. Dependencies that don't change produce identical layers, giving you efficient caching without manual multi-stage builds. -00021| - **No base image** — Images are built from scratch. No inherited vulnerabilities from a base image you don't control. -00022| - **Shared with your devshell** — The same Nix packages used in your dev environment end up in your container. No "works locally but not in Docker" surprises. -00023| -00024| ## Enable Containers -00025| -00026| Container definitions live under `stack.containers`: -00027| -00028| ```nix -00029| stack.containers.api = { -00030| enable = true; -00031| name = "myapp-api"; -00032| tag = "latest"; -00033| -00034| # What goes in the image -00035| config = { -00036| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00037| env = { -00038| NODE_ENV = "production"; -00039| }; -00040| exposedPorts = { -00041| "4201/tcp" = {}; -00042| }; -00043| }; -00044| -00045| # Files to include -00046| copyToRoot = [ -00047| ./apps/api/dist -00048| ./apps/api/package.json -00049| ]; -00050| }; -00051| ``` -00052| -00053| ## Building Images -00054| -00055| Build a container image from the command line: -00056| -00057| ```bash -00058| # Build a specific container -00059| nix build --impure .#packages.x86_64-linux.container-api -00060| -00061| # Load it into Docker -00062| docker load < result -00063| -00064| # Or build and load in one step -00065| nix build --impure .#packages.x86_64-linux.container-api && docker load < result -00066| ``` -00067| -00068| The output is a standard OCI image that works with Docker, Podman, containerd, or any OCI-compatible runtime. -00069| -00070| ## Per-App Containers -00071| -00072| When you define [apps](/docs/apps-ci/defining-apps), you can attach container definitions directly: -00073| -00074| ```nix -00075| stack.apps.api = { -00076| port = 1; -00077| root = "./apps/api"; -00078| build = "bun run build"; -00079| }; -00080| -00081| stack.containers.api = { -00082| enable = true; -00083| name = "ghcr.io/my-org/myapp-api"; -00084| -00085| config = { -00086| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00087| env = { -00088| NODE_ENV = "production"; -00089| PORT = "4201"; -00090| }; -00091| exposedPorts = { -00092| "4201/tcp" = {}; -00093| }; -00094| }; -00095| }; -00096| ``` -00097| -00098| ## Multi-Stage Patterns -00099| -00100| Nix replaces the need for multi-stage Docker builds. Instead of copying between build and runtime stages, you define separate derivations: -00101| -00102| ```nix -00103| { pkgs, ... }: -00104| let -00105| # Build stage — produces the compiled output -00106| apiBundle = pkgs.stdenv.mkDerivation { -00107| name = "api-bundle"; -00108| src = ./apps/api; -00109| buildInputs = [ pkgs.bun ]; -00110| buildPhase = '' -00111| bun install --frozen-lockfile -00112| bun run build -00113| ''; -00114| installPhase = '' -00115| mkdir -p $out -00116| cp -r dist $out/ -00117| cp package.json $out/ -00118| ''; -00119| }; -00120| in -00121| { -00122| stack.containers.api = { -00123| enable = true; -00124| name = "ghcr.io/my-org/myapp-api"; -00125| -00126| # Only the built output and runtime go in the image -00127| # Build tools (bun, node headers, etc.) are NOT included -00128| copyToRoot = [ apiBundle ]; -00129| -00130| config = { -00131| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00132| workingDir = "/"; -00133| env.NODE_ENV = "production"; -00134| }; -00135| }; -00136| } -00137| ``` -00138| -00139| The resulting image contains only Node.js and your compiled code—no build tools, no source files, no `node_modules` with devDependencies. -00140| -00141| ## Image Layers -00142| -00143| nix2container automatically creates efficient image layers based on the Nix store dependency graph. Each Nix store path becomes a potential layer boundary, so: -00144| -00145| - **Runtime dependencies** (Node.js, system libraries) rarely change → cached layers -00146| - **Application code** changes frequently → small top layer -00147| - **No manual layer optimization** — Nix computes the optimal layer split -00148| -00149| This gives you Docker-like layer caching without writing `COPY package.json` tricks or managing multi-stage builds. -00150| -00151| ## Registry Push -00152| -00153| Push built images to a container registry: -00154| -00155| ```bash -00156| # Build the container -00157| nix build --impure .#packages.x86_64-linux.container-api -00158| docker load < result -00159| -00160| # Push to registry -00161| docker tag myapp-api:latest ghcr.io/my-org/myapp-api:latest -00162| docker push ghcr.io/my-org/myapp-api:latest -00163| -00164| # Or use skopeo for direct copy -00165| nix run nixpkgs#skopeo -- copy \ -00166| docker-archive:./result \ -00167| docker://ghcr.io/my-org/myapp-api:latest -00168| ``` -00169| -00170| For CI, the [CI generation](/docs/apps-ci/ci-generation) system can produce Docker build and push steps in your GitHub Actions workflow automatically. -00171| -00172| ## Configuration Reference -00173| -00174| Each container supports these options: -00175| -00176| | Option | Type | Description | -00177| |--------|------|-------------| -00178| | `enable` | `bool` | Whether to build this container | -00179| | `name` | `string` | Image name (including registry prefix) | -00180| | `tag` | `string` | Image tag (default: `"latest"`) | -00181| | `config.entrypoint` | `list` | Container entrypoint command | -00182| | `config.cmd` | `list` | Default command arguments | -00183| | `config.env` | `attrset` | Environment variables baked into the image | -00184| | `config.exposedPorts` | `attrset` | Ports to expose | -00185| | `config.workingDir` | `string` | Working directory inside the container | -00186| | `config.user` | `string` | User to run as | -00187| | `copyToRoot` | `list` | Files and derivations to include in the image | -00188| | `layers` | `list` | Explicit layer definitions (advanced) | -00189| | `maxLayers` | `int` | Maximum number of layers (default: 127) | -00190| -00191| ## Comparing to Dockerfiles -00192| -00193| If you're already using Dockerfiles, you can continue to do so. Stack's container system is an alternative, not a replacement. Here's when each makes sense: -00194| -00195| | Use Nix containers when... | Use Dockerfiles when... | -00196| |---------------------------|------------------------| -00197| | You want reproducible builds | You need specific base images (e.g., CUDA) | -00198| | You want minimal image size | Your team is more comfortable with Docker | -00199| | You're already using Nix for your devshell | You need Docker-specific features (BuildKit secrets, etc.) | -00200| | You want shared dependencies with your dev environment | You're deploying to a platform that expects Dockerfiles | -00201| -00202| -00203| Stack can also generate Dockerfiles through the [Docker module](/docs/deployment/index), giving you a middle ground: Nix-generated Dockerfiles that are standard files on disk. -00204| -00205| -00206| ## Troubleshooting -00207| -00208| ### Image is larger than expected -00209| -00210| Check what's being pulled in as dependencies: -00211| -00212| ```bash -00213| nix path-info -rsSh .#packages.x86_64-linux.container-api -00214| ``` -00215| -00216| This shows every store path in the image closure and its size. Look for unexpected dependencies—a common issue is accidentally including build-time tools in the runtime closure. -00217| -00218| ### "No such file or directory" at runtime -00219| -00220| The container's filesystem only contains what you explicitly include via `copyToRoot` and what Nix determines as runtime dependencies. If a file is missing, add it to `copyToRoot` or ensure the derivation that produces it is in the dependency graph. -00221| -00222| ### Can't run shell commands in the container -00223| -00224| By default, Nix-built containers don't include a shell (`bash`, `sh`). If you need one for debugging: -00225| -00226| ```nix -00227| copyToRoot = [ -00228| apiBundle -00229| pkgs.bashInteractive # Adds bash for debugging -00230| pkgs.coreutils # Adds ls, cat, etc. -00231| ]; -00232| ``` -00233| -00234| Remove these before deploying to production to keep the image minimal. -00235| -00236| ## Reference -00237| -00238| - [Deployment Overview](/docs/deployment) for other deployment options -00239| - [Apps → Defining Apps](/docs/apps-ci/defining-apps) for the app definitions containers can reference -00240| - [CI Generation](/docs/apps-ci/ci-generation) for automated container builds in CI -00241| - [nix2container](https://github.com/nlewo/nix2container) for the underlying container builder - -(End of file - total 241 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json" -} -``` -**Output:** -``` - -00001| { -00002| "title": "Containers & Deployment", -00003| "description": "Build containers and deploy to production", -00004| "icon": "cloud-upload", -00005| "pages": ["index", "containers", "cloudflare", "fly", "sst"] -00006| } -00007| - -(End of file - total 7 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 5.7s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/machines/module.nix -00003| # -00004| # Machine inventory infra module. -00005| # -00006| # Accepts a machine inventory definition in Nix and emits a JSON string -00007| # output that can be stored via the infra output backend. Colmena consumes -00008| # this inventory from stack.infra.outputs.machines. -00009| # ============================================================================== -00010| { -00011| lib, -00012| config, -00013| ... -00014| }: -00015| let -00016| cfg = config.stack.infra.machines; -00017| -00018| sshConfigType = lib.types.submodule { -00019| options = { -00020| user = lib.mkOption { -00021| type = lib.types.str; -00022| default = "root"; -00023| description = "SSH user for connecting to the machine."; -00024| }; -00025| -00026| port = lib.mkOption { -00027| type = lib.types.int; -00028| default = 22; -00029| description = "SSH port for connecting to the machine."; -00030| }; -00031| -00032| keyPath = lib.mkOption { -00033| type = lib.types.nullOr lib.types.str; -00034| default = null; -00035| description = "Path to the SSH private key for this machine."; -00036| }; -00037| }; -00038| }; -00039| -00040| awsFilterType = lib.types.submodule { -00041| options = { -00042| name = lib.mkOption { -00043| type = lib.types.str; -00044| description = "EC2 filter name (e.g., instance-state-name, tag:Name)."; -00045| }; -00046| -00047| values = lib.mkOption { -00048| type = lib.types.listOf lib.types.str; -00049| default = [ ]; -00050| description = "Values for the EC2 filter."; -00051| }; -00052| }; -00053| }; -00054| -00055| machineType = lib.types.submodule { -00056| options = { -00057| id = lib.mkOption { -00058| type = lib.types.nullOr lib.types.str; -00059| default = null; -00060| description = "Optional machine identifier (defaults to the attrset key)."; -00061| }; -00062| -00063| name = lib.mkOption { -00064| type = lib.types.nullOr lib.types.str; -00065| default = null; -00066| description = "Human-friendly machine name."; -00067| }; -00068| -00069| host = lib.mkOption { -00070| type = lib.types.nullOr lib.types.str; -00071| default = null; -00072| description = "SSH host or hostname for the machine."; -00073| }; -00074| -00075| ssh = lib.mkOption { -00076| type = sshConfigType; -00077| default = { }; -00078| description = "SSH connection settings for the machine."; -00079| }; -00080| -00081| tags = lib.mkOption { -00082| type = lib.types.listOf lib.types.str; -00083| default = [ ]; -00084| description = "Tags used for grouping and target selection."; -00085| }; -00086| -00087| roles = lib.mkOption { -00088| type = lib.types.listOf lib.types.str; -00089| default = [ ]; -00090| description = "Roles associated with this machine."; -00091| }; -00092| -00093| provider = lib.mkOption { -00094| type = lib.types.nullOr lib.types.str; -00095| default = null; -00096| description = "Infrastructure provider name (aws, gcp, hetzner, etc.)."; -00097| }; -00098| -00099| arch = lib.mkOption { -00100| type = lib.types.nullOr lib.types.str; -00101| default = null; -00102| description = "Target system architecture (e.g., x86_64-linux)."; -00103| }; -00104| -00105| publicIp = lib.mkOption { -00106| type = lib.types.nullOr lib.types.str; -00107| default = null; -00108| description = "Public IPv4/IPv6 address for the machine."; -00109| }; -00110| -00111| privateIp = lib.mkOption { -00112| type = lib.types.nullOr lib.types.str; -00113| default = null; -00114| description = "Private IPv4/IPv6 address for the machine."; -00115| }; -00116| -00117| labels = lib.mkOption { -00118| type = lib.types.attrsOf lib.types.str; -00119| default = { }; -00120| description = "Arbitrary labels attached to the machine."; -00121| }; -00122| -00123| nixosProfile = lib.mkOption { -00124| type = lib.types.nullOr lib.types.str; -00125| default = null; -00126| description = "NixOS profile name to deploy on this machine."; -00127| }; -00128| -00129| nixosModules = lib.mkOption { -00130| type = lib.types.listOf lib.types.str; -00131| default = [ ]; -00132| description = "Extra NixOS modules to include for this machine."; -00133| }; -00134| -00135| targetEnv = lib.mkOption { -00136| type = lib.types.nullOr lib.types.str; -00137| default = null; -00138| description = "Deployment environment label for this machine."; -00139| }; -00140| -00141| env = lib.mkOption { -00142| type = lib.types.attrsOf lib.types.str; -00143| default = { }; -00144| description = "Environment variables applied to this machine."; -00145| }; -00146| -00147| metadata = lib.mkOption { -00148| type = lib.types.attrsOf lib.types.anything; -00149| default = { }; -00150| description = "Extra metadata for downstream tooling."; -00151| }; -00152| }; -00153| }; -00154| in -00155| { -00156| options.stack.infra.machines = { -00157| enable = lib.mkOption { -00158| type = lib.types.bool; -00159| default = false; -00160| description = "Enable machine inventory provisioning via infra."; -00161| }; -00162| -00163| source = lib.mkOption { -00164| type = lib.types.enum [ -00165| "static" -00166| "aws-ec2" -00167| ]; -00168| default = "static"; -00169| description = "Machine inventory source (static or AWS EC2)."; -00170| }; -00171| -00172| aws = { -00173| region = lib.mkOption { -00174| type = lib.types.nullOr lib.types.str; -00175| default = config.stack.aws.roles-anywhere.region or null; -00176| description = "AWS region for EC2 inventory (falls back to AWS env defaults)."; -00177| }; -00178| -00179| instance-ids = lib.mkOption { -00180| type = lib.types.listOf lib.types.str; -00181| default = [ ]; -00182| description = "Explicit EC2 instance IDs to include in inventory."; -00183| }; -00184| -00185| filters = lib.mkOption { -00186| type = lib.types.listOf awsFilterType; -00187| default = [ -00188| { -00189| name = "instance-state-name"; -00190| values = [ "running" ]; -00191| } -00192| ]; -00193| description = "EC2 filters for inventory discovery."; -00194| }; -00195| -00196| name-tag-keys = lib.mkOption { -00197| type = lib.types.listOf lib.types.str; -00198| default = [ "Name" ]; -00199| description = "Tag keys used to derive machine names."; -00200| }; -00201| -00202| role-tag-keys = lib.mkOption { -00203| type = lib.types.listOf lib.types.str; -00204| default = [ -00205| "stack:role" -00206| "role" -00207| ]; -00208| description = "Tag keys used to derive machine roles."; -00209| }; -00210| -00211| tag-keys = lib.mkOption { -00212| type = lib.types.listOf lib.types.str; -00213| default = [ -00214| "stack:tag" -00215| "tag" -00216| ]; -00217| description = "Tag keys used to derive machine tags."; -00218| }; -00219| -00220| env-tag-keys = lib.mkOption { -00221| type = lib.types.listOf lib.types.str; -00222| default = [ -00223| "stack:env" -00224| "env" -00225| "stage" -00226| ]; -00227| description = "Tag keys used to derive machine target environments."; -00228| }; -00229| -00230| host-preference = lib.mkOption { -00231| type = lib.types.listOf (lib.types.enum [ -00232| "publicDns" -00233| "publicIp" -00234| "privateIp" -00235| ]); -00236| default = [ -00237| "publicDns" -00238| "publicIp" -00239| "privateIp" -00240| ]; -00241| description = "Preferred host fields for connecting to EC2 machines."; -00242| }; -00243| -00244| ssh = lib.mkOption { -00245| type = sshConfigType; -00246| default = { }; -00247| description = "Default SSH settings for EC2 machines."; -00248| }; -00249| }; -00250| -00251| machines = lib.mkOption { -00252| type = lib.types.attrsOf machineType; -00253| default = { }; -00254| description = "Machine inventory definitions to emit via infra outputs."; -00255| }; -00256| -00257| sync-outputs = lib.mkOption { -00258| type = lib.types.listOf lib.types.str; -00259| default = [ "machines" ]; -00260| description = "Which outputs to sync to the storage backend."; -00261| }; -00262| }; -00263| -00264| config = lib.mkIf cfg.enable { -00265| stack.infra.enable = lib.mkDefault true; -00266| -00267| stack.infra.modules.machines = { -00268| name = "Machine Inventory"; -00269| description = "Machine inventory provider for Colmena deployments"; -00270| path = ./index.ts; -00271| inputs = { -00272| source = cfg.source; -00273| machines = cfg.machines; -00274| aws = { -00275| region = cfg.aws.region; -00276| instanceIds = cfg.aws.instance-ids; -00277| filters = cfg.aws.filters; -00278| nameTagKeys = cfg.aws.name-tag-keys; -00279| roleTagKeys = cfg.aws.role-tag-keys; -00280| tagKeys = cfg.aws.tag-keys; -00281| envTagKeys = cfg.aws.env-tag-keys; -00282| hostPreference = cfg.aws.host-preference; -00283| ssh = { -00284| user = cfg.aws.ssh.user; -00285| port = cfg.aws.ssh.port; -00286| keyPath = cfg.aws.ssh.keyPath; -00287| }; -00288| }; -00289| }; -00290| dependencies = lib.optionalAttrs (cfg.source == "aws-ec2") { -00291| "@aws-sdk/client-ec2" = "catalog:"; -00292| }; -00293| outputs = -00294| let -00295| mkOutput = key: desc: { -00296| description = desc; -00297| sensitive = false; -00298| sync = builtins.elem key cfg.sync-outputs; -00299| }; -00300| in -00301| { -00302| machines = mkOutput "machines" "Machine inventory (JSON)"; -00303| }; -00304| }; -00305| }; -00306| } -00307| - -(End of file - total 307 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Data comes from the agent via colmena-machines.json -00008| * and colmena-app-deploy.json state files. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, -00017| CardDescription, -00018| CardHeader, -00019| CardTitle, -00020| } from "@ui/card"; -00021| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs"; -00022| import { -00023| Activity, -00024| AlertCircle, -00025| CheckCircle, -00026| CloudOff, -00027| Cpu, -00028| HardDrive, -00029| Loader2, -00030| Network, -00031| Play, -00032| RefreshCw, -00033| Rocket, -00034| Server, -00035| Settings, -00036| Shield, -00037| XCircle, -00038| } from "lucide-react"; -00039| import { useAgentContext } from "@/lib/agent-provider"; -00040| import { useNixConfig } from "@/lib/use-agent"; -00041| import { PanelHeader } from "../shared/panel-header"; -00042| import { cn } from "@/lib/utils"; -00043| -00044| // ============================================================================= -00045| // Types -00046| // ============================================================================= -00047| -00048| interface MachineInfo { -00049| id: string; -00050| name: string; -00051| host: string | null; -00052| ssh: { -00053| user: string; -00054| port: number; -00055| keyPath: string | null; -00056| }; -00057| tags: string[]; -00058| roles: string[]; -00059| provider: string | null; -00060| arch: string | null; -00061| publicIp: string | null; -00062| privateIp: string | null; -00063| targetEnv: string | null; -00064| labels: Record; -00065| } -00066| -00067| interface AppDeployMapping { -00068| enable: boolean; -00069| targets: string[]; -00070| resolvedTargets: string[]; -00071| role: string | null; -00072| nixosModules: string[]; -00073| system: string | null; -00074| } -00075| -00076| interface ColmenaConfig { -00077| enable: boolean; -00078| machineSource: string; -00079| generateHive: boolean; -00080| config: string; -00081| machineCount: number; -00082| machineIds: string[]; -00083| } -00084| -00085| // ============================================================================= -00086| // Hooks -00087| // ============================================================================= -00088| -00089| function useColmenaData() { -00090| const { data: nixConfig, isLoading, refetch } = useNixConfig(); -00091| -00092| const result = useMemo(() => { -00093| const cfg = nixConfig as Record | null | undefined; -00094| if (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null }; -00095| -00096| const serializable = cfg.serializable as Record | undefined; -00097| const colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null; -00098| -00099| // Try to get machines from colmena serialized data or panels -00100| const colmenaData = cfg.colmena as Record | undefined; -00101| const machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record; -00102| -00103| // Apps with deploy config -00104| const rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>; -00105| const appDeploy: Record = {}; -00106| -00107| for (const [appName, appCfg] of Object.entries(rawApps)) { -00108| const deploy = appCfg.deploy as Record | undefined; -00109| if (deploy?.enable) { -00110| appDeploy[appName] = { -00111| enable: true, -00112| targets: (deploy.targets as string[]) ?? [], -00113| resolvedTargets: (deploy.resolvedTargets as string[]) ?? [], -00114| role: (deploy.role as string | null) ?? null, -00115| nixosModules: (deploy.nixosModules as string[]) ?? [], -00116| system: (deploy.system as string | null) ?? null, -00117| }; -00118| } -00119| } -00120| -00121| return { machines: machinesComputed, appDeploy, colmenaConfig }; -00122| }, [nixConfig]); -00123| -00124| return { ...result, isLoading, refetch }; -00125| } -00126| -00127| // ============================================================================= -00128| // Sub-components -00129| // ============================================================================= -00130| -00131| function MachineCard({ machine }: { machine: MachineInfo }) { -00132| const isReachable = machine.host !== null; -00133| -00134| return ( -00135| -00139| -00140|
-00141|
-00142| -00143| {machine.name} -00144|
-00145|
-00146| {isReachable ? ( -00147| -00148| -00149| {machine.host} -00150| -00151| ) : ( -00152| -00153| -00154| no host -00155| -00156| )} -00157|
-00158|
-00159| -00160|
-00161|
-00162| -00163| {machine.ssh.user}@{machine.ssh.port} -00164|
-00165| {machine.arch && ( -00166|
-00167| -00168| {machine.arch} -00169|
-00170| )} -00171| {machine.provider && ( -00172|
-00173| -00174| {machine.provider} -00175|
-00176| )} -00177| {machine.targetEnv && ( -00178|
-00179| -00180| {machine.targetEnv} -00181|
-00182| )} -00183|
-00184| -00185| {(machine.tags.length > 0 || machine.roles.length > 0) && ( -00186|
-00187| {machine.roles.map((role) => ( -00188| -00189| {role} -00190| -00191| ))} -00192| {machine.tags.map((tag) => ( -00193| -00194| {tag} -00195| -00196| ))} -00197|
-00198| )} -00199|
-00200|
-00201| ); -00202| } -00203| -00204| function AppTargetRow({ -00205| appName, -00206| deploy, -00207| machines, -00208| }: { -00209| appName: string; -00210| deploy: AppDeployMapping; -00211| machines: Record; -00212| }) { -00213| const resolvedNames = deploy.resolvedTargets.map( -00214| (id) => machines[id]?.name ?? id, -00215| ); -00216| -00217| return ( -00218|
-00219|
-00220|
-00221| -00222|
-00223|
-00224|

{appName}

-00225|

-00226| {deploy.targets.length > 0 -00227| ? `Targets: ${deploy.targets.join(", ")}` -00228| : "No targets defined"} -00229|

-00230|
-00231|
-00232|
-00233| {deploy.role && ( -00234| -00235| {deploy.role} -00236| -00237| )} -00238| -00239| {resolvedNames.length} machine{resolvedNames.length !== 1 ? "s" : ""} -00240| -00241|
-00242|
-00243| ); -00244| } -00245| -00246| // ============================================================================= -00247| // Main Component -00248| // ============================================================================= -00249| -00250| export function DeployPanel() { -00251| const { isConnected } = useAgentContext(); -00252| const { machines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData(); -00253| const [isRefreshing, setIsRefreshing] = useState(false); -00254| -00255| const machineList = Object.values(machines); -00256| const machineCount = machineList.length; -00257| const appDeployEntries = Object.entries(appDeploy); -00258| const healthyCount = machineList.filter((m) => m.host !== null).length; -00259| const unhealthyCount = machineCount - healthyCount; -00260| -00261| const handleRefresh = async () => { -00262| setIsRefreshing(true); -00263| try { -00264| await refetch(); -00265| } finally { -00266| setIsRefreshing(false); -00267| } -00268| }; -00269| -00270| if (!isConnected) { -00271| return ( -00272|
-00273| -00277| -00278| -00279| -00280|
-00281|

Agent Not Connected

-00282|

-00283| Connect to the stack agent to manage deployments. -00284|

-00285|
-00286|
-00287|
-00288|
-00289| ); -00290| } -00291| -00292| if (isLoading) { -00293| return ( -00294|
-00295| -00296|
-00297| ); -00298| } -00299| -00300| return ( -00301|
-00302| -00312| -00315| {isRefreshing ? "Refreshing..." : "Refresh"} -00316| -00317| } -00318| /> -00319| -00320| {/* Status overview */} -00321|
-00322| -00323| -00324|
-00325| -00326|
-00327|
-00328|

Machines

-00329|

{machineCount}

-00330|
-00331|
-00332|
-00333| -00334| -00335|
0 ? "bg-green-500/10" : "bg-secondary", -00338| )}> -00339| 0 ? "text-green-500" : "text-muted-foreground", -00342| )} /> -00343|
-00344|
-00345|

Reachable

-00346|

{healthyCount}

-00347|
-00348|
-00349|
-00350| -00351| -00352|
0 ? "bg-amber-500/10" : "bg-secondary", -00355| )}> -00356| 0 ? "text-amber-500" : "text-muted-foreground", -00359| )} /> -00360|
-00361|
-00362|

Unreachable

-00363|

{unhealthyCount}

-00364|
-00365|
-00366|
-00367| -00368| -00369|
0 ? "bg-blue-500/10" : "bg-secondary", -00372| )}> -00373| 0 ? "text-blue-500" : "text-muted-foreground", -00376| )} /> -00377|
-00378|
-00379|

Deploy-enabled apps

-00380|

{appDeployEntries.length}

-00381|
-00382|
-00383|
-00384|
-00385| -00386| -00387| -00388| -00389| Machines -00390| {machineCount > 0 && ( -00391| -00392| {machineCount} -00393| -00394| )} -00395| -00396| -00397| App Targets -00398| {appDeployEntries.length > 0 && ( -00399| -00400| {appDeployEntries.length} -00401| -00402| )} -00403| -00404| Actions -00405| Settings -00406| -00407| -00408| {/* Machines Tab */} -00409| -00410| {machineCount === 0 ? ( -00411| -00412| -00413| -00414|
-00415|

No Machines

-00416|

-00417| Machine inventory is empty. Run infra:deploy and infra:pull-outputs to populate it, then reload the shell. -00418|

-00419|
-00420|
-00421|
-00422| ) : ( -00423|
-00424| {machineList.map((machine) => ( -00425| -00426| ))} -00427|
-00428| )} -00429|
-00430| -00431| {/* App Targets Tab */} -00432| -00433| {appDeployEntries.length === 0 ? ( -00434| -00435| -00436| -00437|
-00438|

No App Targets

-00439|

-00440| No apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config. -00441|

-00442|
-00443|
-00444|
-00445| ) : ( -00446|
-00447| {appDeployEntries.map(([appName, deploy]) => ( -00448| -00454| ))} -00455|
-00456| )} -00457|
-00458| -00459| {/* Actions Tab */} -00460| -00461| -00462| -00463| Colmena Actions -00464| -00465| Run Colmena commands against your fleet -00466| -00467| -00468| -00469| {machineCount === 0 ? ( -00470|
-00471|
-00472| -00473|

-00474| No machines in inventory. Provision infrastructure first. -00475|

-00476|
-00477|
-00478| ) : ( -00479| <> -00480|
-00481| -00485| -00489| -00493|
-00494|

-00495| Actions run the generated wrapper scripts with your configured defaults. -00496| Use the CLI for advanced options: colmena-apply --on tag:prod -00497|

-00498| -00499| )} -00500|
-00501|
-00502|
-00503| -00504| {/* Settings Tab */} -00505| -00506| -00507| -00508| Colmena Configuration -00509| -00510| Current Colmena module settings -00511| -00512| -00513| -00514|
-00515|
-00516|

Machine Source

-00517|

-00518| {colmenaConfig?.machineSource ?? "infra"} -00519|

-00520|
-00521|
-00522|

Hive Config

-00523|

-00524| {colmenaConfig?.config ?? ".stack/state/colmena/hive.nix"} -00525|

-00526|
-00527|
-00528|

Generate Hive

-00529|

-00530| {colmenaConfig?.generateHive ? "Yes" : "No"} -00531|

-00532|
-00533|
-00534|

Machine Count

-00535|

-00536| {colmenaConfig?.machineCount ?? machineCount} -00537|

-00538|
-00539|
-00540|
-00541|
-00542|
-00543|
-00544|
-00545| ); -00546| } -00547| - -(End of file - total 547 lines) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -Now I have a thorough understanding of the system. Let me create the docs. - -**How machines work in Studio UI**: The Deploy panel (`/studio/deploy`) reads machine inventory from `nixConfig.serializable.colmena` and `nixConfig.colmena.machinesComputed`. When the Machines tab is empty, it shows guidance to run `infra:deploy` and `infra:pull-outputs`. The machines come from either static Nix definitions or AWS EC2 discovery via the machines infra module. - -Let me create the new docs and update the index. - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "content": "---\ntitle: Machines\ndescription: Define and discover your deployment target machines for NixOS deployments\nicon: server\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel's machine inventory system lets you define the servers your applications deploy to. Machines can be declared statically in Nix or discovered dynamically from AWS EC2. The inventory feeds into Colmena for NixOS deployments and is visible in the Studio UI's Deploy panel.\n\n## How It Works\n\nMachine inventory flows through three layers:\n\n1. **Nix config** -- You declare machines (static) or EC2 discovery rules in `.stack/config.nix`\n2. **Infra module** -- The machines infra module resolves the inventory at deploy time (for EC2, it queries the AWS API)\n3. **Studio UI** -- The Deploy panel reads the resolved inventory and displays machine cards, app-to-machine mappings, and Colmena actions\n\n## Quick Start\n\n\n\n\n### Enable the machines module\n\nAdd to your `.stack/config.nix`:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\"; # or \"aws-ec2\"\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"10.0.1.10\";\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n ssh.user = \"deploy\";\n };\n };\n};\n```\n\n\n\n\n### Deploy infrastructure\n\nRun `infra:deploy` to provision and register the machine inventory:\n\n```bash\ninfra:deploy\n```\n\n\n\n\n### Pull outputs and reload\n\nPull the resolved inventory into your local state and reload the devshell:\n\n```bash\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\n\n\n\n### View in Studio\n\nOpen the Studio UI and navigate to **Deploy**. Your machines appear in the Machines tab with their status, roles, and connection info.\n\n\n\n\n## Static Machines\n\nFor fixed infrastructure (bare metal, pre-provisioned VMs, Hetzner, etc.), declare machines directly:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\";\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"web1.example.com\";\n roles = [ \"web\" \"app\" ];\n tags = [ \"production\" \"us-east\" ];\n provider = \"hetzner\";\n arch = \"x86_64-linux\";\n publicIp = \"203.0.113.10\";\n privateIp = \"10.0.1.10\";\n targetEnv = \"production\";\n\n ssh = {\n user = \"deploy\";\n port = 22;\n keyPath = \"~/.ssh/deploy_ed25519\";\n };\n\n labels = {\n team = \"platform\";\n cost-center = \"infra\";\n };\n };\n\n db-1 = {\n name = \"Database Primary\";\n host = \"10.0.2.10\";\n roles = [ \"database\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n targetEnv = \"production\";\n ssh.user = \"root\";\n };\n };\n};\n```\n\n### Machine Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | `string` | Human-friendly display name |\n| `host` | `string` | SSH hostname or IP address |\n| `ssh.user` | `string` | SSH user (default: `root`) |\n| `ssh.port` | `int` | SSH port (default: `22`) |\n| `ssh.keyPath` | `string?` | Path to SSH private key |\n| `roles` | `[string]` | Roles for app-to-machine targeting |\n| `tags` | `[string]` | Tags for grouping and filtering |\n| `provider` | `string?` | Infrastructure provider (aws, hetzner, etc.) |\n| `arch` | `string?` | System architecture (e.g., `x86_64-linux`) |\n| `publicIp` | `string?` | Public IP address |\n| `privateIp` | `string?` | Private IP address |\n| `targetEnv` | `string?` | Environment label (production, staging, etc.) |\n| `labels` | `{string: string}` | Arbitrary key-value metadata |\n| `nixosProfile` | `string?` | NixOS profile to deploy |\n| `nixosModules` | `[string]` | Extra NixOS modules for this machine |\n| `env` | `{string: string}` | Environment variables for this machine |\n| `metadata` | `{string: any}` | Extra metadata for downstream tooling |\n\n## AWS EC2 Discovery\n\nFor dynamic infrastructure, Stack can discover machines from AWS EC2 at deploy time:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"aws-ec2\";\n\n aws = {\n region = \"us-west-2\";\n\n # EC2 filters (same syntax as AWS CLI --filters)\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n };\n};\n```\n\nWhen `infra:deploy` runs, the machines module queries the EC2 API using the specified filters and builds an inventory from the results.\n\n### EC2 Tag Mapping\n\nEC2 instances are mapped to machine properties using configurable tag keys:\n\n| Machine Property | Default Tag Keys | Example Tag Value |\n|-----------------|-----------------|-------------------|\n| `name` | `Name` | `web-server-1` |\n| `roles` | `stack:role`, `role` | `web,app` |\n| `tags` | `stack:tag`, `tag` | `production,us-west` |\n| `targetEnv` | `stack:env`, `env`, `stage` | `production` |\n\nCustomize the tag keys used for mapping:\n\n```nix\nstackpanel.infra.machines.aws = {\n region = \"us-west-2\";\n\n # Which EC2 tags map to machine roles\n role-tag-keys = [ \"stack:role\" \"Role\" ];\n\n # Which EC2 tags map to machine tags\n tag-keys = [ \"stack:tag\" \"Environment\" ];\n\n # Which EC2 tags map to target environment\n env-tag-keys = [ \"stack:env\" \"Stage\" ];\n\n # Which EC2 tags provide the machine name\n name-tag-keys = [ \"Name\" ];\n\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n ];\n};\n```\n\n### Host Resolution\n\nWhen discovering EC2 instances, Stack picks the SSH host using a preference order:\n\n```nix\nstackpanel.infra.machines.aws = {\n # Try public DNS first, then public IP, then private IP\n host-preference = [ \"publicDns\" \"publicIp\" \"privateIp\" ];\n};\n```\n\nFor VPN or private-network setups where you connect to private IPs:\n\n```nix\nstackpanel.infra.machines.aws = {\n host-preference = [ \"privateIp\" ];\n};\n```\n\n### Default SSH Settings for EC2\n\nApply SSH defaults to all discovered EC2 machines:\n\n```nix\nstackpanel.infra.machines.aws = {\n ssh = {\n user = \"ec2-user\";\n port = 22;\n keyPath = \"~/.ssh/aws_deploy\";\n };\n};\n```\n\n### Filtering by Instance IDs\n\nTarget specific instances instead of using filters:\n\n```nix\nstackpanel.infra.machines.aws = {\n instance-ids = [\n \"i-0abc123def456789a\"\n \"i-0def456789abc1230\"\n ];\n};\n```\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:\n\n- Machine name and hostname\n- SSH connection info (user, port)\n- Architecture and provider\n- Roles (colored badges) and tags\n- Reachability status (green check or amber warning)\n\nWhen the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab\n\nShows which apps are configured for deployment and their target machines. Each row displays:\n\n- App name\n- Target machines (resolved from roles or explicit targets)\n- Role badge (if targeting by role)\n- Machine count\n\n### Actions Tab\n\nProvides buttons for Colmena deployment actions:\n\n- **colmena eval** -- Evaluate the NixOS configuration\n- **colmena build** -- Build the NixOS closures\n- **colmena apply** -- Deploy to target machines\n\n\nThe Actions tab is disabled when no machines are in the inventory. Provision infrastructure first with `infra:deploy`.\n\n\n### Settings Tab\n\nDisplays the current Colmena configuration: machine source, hive config path, whether hive generation is enabled, and total machine count.\n\n## Mapping Apps to Machines\n\nAfter machines are in your inventory, configure apps to deploy to them:\n\n```nix\nstackpanel.apps.api = {\n port = 1;\n root = \"./apps/api\";\n\n # Deploy this app to machines with the \"web\" role\n deploy = {\n enable = true;\n role = \"web\";\n };\n};\n\nstackpanel.apps.worker = {\n port = 2;\n root = \"./apps/worker\";\n\n # Deploy to specific machines by name\n deploy = {\n enable = true;\n targets = [ \"worker-1\" \"worker-2\" ];\n };\n};\n```\n\n## End-to-End Example\n\nHere is a complete setup with AWS EC2 discovery, app targeting, and infra provisioning:\n\n```nix\n{\n # Infrastructure provisioning\n stack.infra = {\n enable = true;\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n\n # AWS secrets (IAM role, KMS key)\n aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n };\n\n # Machine discovery\n machines = {\n enable = true;\n source = \"aws-ec2\";\n aws = {\n region = \"us-west-2\";\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n ssh.user = \"deploy\";\n };\n };\n };\n\n # Apps with deployment targets\n stack.apps = {\n web = {\n port = 0;\n root = \"./apps/web\";\n deploy = {\n enable = true;\n role = \"web\";\n };\n };\n\n api = {\n port = 1;\n root = \"./apps/api\";\n deploy = {\n enable = true;\n role = \"api\";\n };\n };\n };\n}\n```\n\nThen deploy:\n\n```bash\n# Provision infrastructure and discover machines\ninfra:deploy\n\n# Pull machine inventory into local state\ninfra:pull-outputs\n\n# Reload shell to pick up new inventory\nexit && nix develop --impure\n\n# View machines in Studio\n# Navigate to /studio/deploy\n\n# Deploy with Colmena\ncolmena apply\n```\n\n## Troubleshooting\n\n### \"No Machines\" in the Deploy panel\n\nThe machine inventory is populated by `infra:deploy`. If you see no machines:\n\n1. Run `infra:deploy` to execute the machines infra module\n2. Run `infra:pull-outputs` to sync the inventory to local state\n3. Reload your devshell (`exit && nix develop --impure`)\n4. Refresh the Deploy panel in Studio\n\n### EC2 discovery returns no instances\n\nCheck your filters and region. Test directly with the AWS CLI:\n\n```bash\naws ec2 describe-instances \\\n --region us-west-2 \\\n --filters \"Name=instance-state-name,Values=running\" \"Name=tag:Project,Values=myapp\" \\\n --query 'Reservations[].Instances[].{Id:InstanceId,Name:Tags[?Key==`Name`].Value|[0]}'\n```\n\n### SSH connection failures\n\nVerify SSH connectivity to the resolved host:\n\n```bash\nssh -o ConnectTimeout=5 deploy@ 'echo ok'\n```\n\nCommon issues:\n- Security group doesn't allow SSH from your IP\n- SSH key path is incorrect\n- Using public DNS but instance only has a private IP (adjust `host-preference`)\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Infrastructure](/docs/deployment/infrastructure) for the infra module system\n- [Fly.io](/docs/deployment/fly) for container-based deployment\n- [Cloudflare](/docs/deployment/cloudflare) for edge deployment\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/infrastructure.mdx", - "content": "---\ntitle: Infrastructure\ndescription: Provision cloud infrastructure with Alchemy and the infra module system\nicon: layers\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel includes a modular infrastructure provisioning system built on [Alchemy](https://alchemy.run). It provisions AWS resources (IAM roles, KMS keys, OIDC providers), databases (Neon), caches (Upstash), deployment targets (Cloudflare Workers), and machine inventories -- all declared in Nix and deployed with a single command.\n\n## How It Works\n\nThe infra system has three parts:\n\n1. **Nix modules** declare what infrastructure you need (`.stack/config.nix`)\n2. **Code generation** produces TypeScript modules in `packages/infra/` with typed inputs\n3. **Alchemy runtime** provisions the actual cloud resources when you run `infra:deploy`\n\nInfrastructure outputs (ARNs, URLs, keys) are synced back to your storage backend (SOPS, SSM, or Chamber) so other parts of your stack can reference them.\n\n## Quick Start\n\n\n\n\n### Enable infrastructure\n\nAdd to `.stack/config.nix`:\n\n```nix\nstackpanel.infra = {\n enable = true;\n\n # Where to store provisioned outputs\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n};\n```\n\n\n\n\n### Enable modules\n\nTurn on the infrastructure modules you need:\n\n```nix\n# AWS IAM + KMS for secrets\nstackpanel.infra.aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n};\n\n# Database (Neon Postgres)\nstackpanel.infra.database = {\n enable = true;\n provider = \"neon\";\n neon.region = \"aws-us-east-1\";\n};\n```\n\n\n\n\n### Reload and deploy\n\n```bash\n# Reload to pick up config changes\nexit && nix develop --impure\n\n# Deploy all infrastructure\ninfra:deploy\n```\n\n\n\n\n## Available Modules\n\n| Module | What it provisions | Key options |\n|--------|-------------------|-------------|\n| **aws-secrets** | IAM role, OIDC provider, KMS key + alias | `oidc.provider`, `iam.role-name`, `kms.alias` |\n| **deployment** | Cloudflare Workers/Pages per app | Per-app `deployment.host = \"cloudflare\"` |\n| **database** | Neon Postgres or devenv/Docker Postgres | `provider` (neon/devenv/docker) |\n| **cache** | Upstash Redis or devenv/Docker Redis | `provider` (upstash/devenv/docker) |\n| **machines** | Machine inventory (static or AWS EC2) | `source` (static/aws-ec2) |\n\n## Deploying Infrastructure\n\n### Basic Deploy\n\n```bash\n# Deploy all modules\ninfra:deploy\n```\n\nThis runs `alchemy deploy` in the generated `packages/infra/` directory. Alchemy evaluates each module, creates or updates resources, and writes state to its `.alchemy/` directory.\n\n### Adopting Existing Resources\n\nIf your AWS resources already exist (e.g., you created an IAM role manually), use the `--adopt` flag to bring them under Alchemy management without recreating them:\n\n```bash\ninfra:deploy --adopt\n```\n\nThis tells Alchemy to adopt existing resources instead of failing with \"already exists\" errors. The infra modules' custom resources (IAM roles, KMS keys) are built to handle adoption gracefully -- they detect existing resources and update them in place.\n\n\nThe `--adopt` flag is an Alchemy CLI option, not a per-resource setting. However, the Stack infra modules are written to be adopt-safe by default -- IAM roles use get-or-create logic, and KMS keys look up existing aliases before creating new keys.\n\n\n### Stage Selection\n\nDeploy to a specific stage (environment):\n\n```bash\ninfra:deploy --stage production\ninfra:deploy --stage staging\n```\n\nWithout `--stage`, Alchemy defaults to your username as the stage name. This lets each developer have isolated infrastructure for testing.\n\n### Destroying Infrastructure\n\n```bash\n# Remove all provisioned resources\ninfra:destroy\n\n# Remove a specific stage\ninfra:destroy --stage staging\n```\n\n\n`infra:destroy` removes cloud resources permanently. Adopted resources (IAM roles, KMS keys) are not deleted -- they're only unlinked from Alchemy state.\n\n\n## Storage Backends\n\nInfrastructure outputs (ARNs, URLs, connection strings) need to be stored somewhere accessible to your apps and CI. Stack supports three backends:\n\n### SOPS (Recommended for teams)\n\nOutputs are written to a SOPS-encrypted YAML file in your repo:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"sops\";\n sops.group = \"dev\"; # writes to .stack/secrets/vars/dev.sops.yaml\n};\n```\n\nAfter `infra:deploy`, outputs like `aws-secrets-roleArn` appear in the SOPS file. They're decrypted at runtime using your team's AGE keys.\n\n### AWS SSM Parameter Store\n\nOutputs are written to SSM parameters:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"ssm\";\n ssm.prefix = \"/myapp/infra\";\n};\n```\n\n### Chamber\n\nOutputs are written via Chamber:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"chamber\";\n chamber.service = \"myapp\";\n};\n```\n\n### None\n\nSkip output sync entirely (outputs are only in Alchemy state):\n\n```nix\nstackpanel.infra.storage-backend.type = \"none\";\n```\n\n## Studio UI: Infrastructure Panel\n\nThe Studio UI's **Infrastructure** panel (`/studio/infra`) provides a visual interface for managing your provisioned infrastructure. It has five tabs:\n\n### Status Tab\n\nShows an overview of your infrastructure configuration:\n- SST project name and region\n- OIDC provider type\n- IAM role name and ARN\n- KMS key status\n- Config file path\n\n### Deploy Tab\n\nDeploy or destroy infrastructure from the UI:\n- Stage selector (dev, staging, production)\n- Deploy button (runs `sst deploy --stage `)\n- Remove button (runs `sst remove --stage `)\n- Live command output\n\n### Outputs Tab\n\nDisplays all provisioned infrastructure outputs:\n- ARNs, URLs, and connection strings\n- Copy-to-clipboard for each value\n- Output source module\n\n### Resources Tab\n\nLists all deployed AWS resources by type and URN.\n\n### Configure Tab\n\nFull configuration form for SST/infrastructure settings:\n- Enable toggle\n- Project name, region, account ID\n- KMS key settings\n- OIDC provider selection (GitHub Actions, Fly.io, Roles Anywhere)\n- IAM role name\n\n## AWS Secrets Module\n\nThe `aws-secrets` module is the most commonly used infra module. It provisions:\n\n- **IAM Role** -- For CI/CD and local AWS access\n- **OIDC Provider** -- For passwordless authentication from GitHub Actions or Fly.io\n- **KMS Key + Alias** -- For encrypting secrets at rest\n- **KMS Access Policy** -- Grants the IAM role encrypt/decrypt permissions\n\n```nix\nstackpanel.infra.aws.secrets = {\n enable = true;\n region = \"us-west-2\";\n\n iam = {\n role-name = \"myapp-secrets-role\";\n additional-policies = [\n \"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess\"\n ];\n };\n\n kms = {\n alias = \"myapp-secrets\";\n deletion-window-days = 30;\n };\n\n oidc = {\n provider = \"github-actions\";\n github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n branch = \"*\";\n };\n };\n};\n```\n\n### OIDC Providers\n\n| Provider | Use case | Authentication flow |\n|----------|----------|-------------------|\n| `github-actions` | CI/CD from GitHub | GitHub OIDC token -> STS AssumeRoleWithWebIdentity |\n| `flyio` | Fly.io Machines | Fly OIDC token -> STS AssumeRoleWithWebIdentity |\n| `roles-anywhere` | Local dev with Step CA | mTLS certificate -> IAM Roles Anywhere |\n\n## Deployment Module\n\nThe deployment infra module provisions hosting resources for your apps. Currently supports Cloudflare Workers:\n\n```nix\nstackpanel.apps.web = {\n port = 0;\n framework.tanstack-start.enable = true;\n\n deployment = {\n enable = true;\n host = \"cloudflare\";\n bindings = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n secrets = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n };\n};\n```\n\nSupported framework/host combinations:\n\n| Framework | Cloudflare | Fly.io |\n|-----------|-----------|--------|\n| `tanstack-start` | TanStackStart Worker | Container |\n| `nextjs` | Nextjs Worker | Container |\n| `vite` | Vite Worker | Container |\n| `hono` | Worker (Node compat) | Container |\n| `astro` | Astro Worker | Container |\n| `remix` | Remix Worker | Container |\n| `nuxt` | Nuxt Worker | Container |\n\n## Generated Files\n\nThe infra system generates a complete TypeScript package at `packages/infra/`:\n\n```\npackages/infra/\n alchemy.run.ts # Orchestrator (imports all modules, syncs outputs)\n package.json # Dependencies from all active modules\n tsconfig.json # TypeScript config\n src/\n index.ts # Infra class (input resolution, output sync)\n types.ts # Per-module input TypeScript interfaces\n resources/\n iam-role.ts # Custom adopt-safe IAM Role resource\n kms-key.ts # Custom adopt-safe KMS Key resource\n kms-alias.ts # Custom KMS Alias resource\n modules/\n aws-secrets/\n index.ts # AWS secrets provisioning\n policies.ts # IAM policy builders\n deployment.ts # App deployment to Cloudflare\n database.ts # Database provisioning\n```\n\nThese files are regenerated on each devshell entry. The `alchemy.run.ts` orchestrator imports all active modules and runs `Infra.syncAll()` to write outputs to your storage backend.\n\n## Pulling Outputs\n\nAfter deploying, pull outputs from your storage backend into the local Nix state:\n\n```bash\ninfra:pull-outputs\n```\n\nThis reads outputs from Chamber/SSM and writes them to `.stack/data/infra-outputs.nix`, making them available as `config.stack.infra.outputs.*` in your Nix config.\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `infra:deploy` | Deploy all infrastructure modules |\n| `infra:deploy --adopt` | Deploy, adopting existing resources |\n| `infra:deploy --stage ` | Deploy to a specific stage |\n| `infra:destroy` | Remove all provisioned resources |\n| `infra:dev` | Start infrastructure dev mode |\n| `infra:pull-outputs` | Pull outputs from storage backend |\n\nAll commands accept additional Alchemy CLI options. Run `infra:deploy --help` for the full list.\n\n## Troubleshooting\n\n### \"EntityAlreadyExists\" on first deploy\n\nYour AWS resources already exist from a previous provisioning tool. Use `--adopt`:\n\n```bash\ninfra:deploy --adopt\n```\n\n### \"Resource already exists\" Alchemy error\n\nThis is different from the AWS error above. It means the Alchemy resource type is registered twice. This can happen if you have conflicting Alchemy versions. Check `packages/infra/package.json` and ensure a single `alchemy` version.\n\n### Outputs not showing up after deploy\n\n1. Check that `storage-backend` is configured (not `\"none\"`)\n2. Run `infra:pull-outputs` to sync outputs to local state\n3. Reload the devshell to pick up new outputs\n\n### \"storage-bakend\" typo error\n\nIf you see `The option 'stack.infra.storage-bakend' does not exist`, check your config for the typo. The correct option is `storage-backend` (with a `c`).\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Machines](/docs/deployment/machines) for machine inventory\n- [SST](/docs/deployment/sst) for the older SST-based infrastructure\n- [Secrets](/docs/secrets) for encrypted secrets management\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.8s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.1s) - -Now update the deployment index and meta.json: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "\n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n", - "newString": "\n \n Provision cloud resources with the Alchemy-based infra module system\n \n \n Define and discover deployment target machines (static or AWS EC2)\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "- Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up.", - "newString": "- Need AWS infrastructure? The [infra module system](./infrastructure) provisions it with IAM, KMS, and OIDC already wired up.\n- Need to manage deployment targets? The [machines module](./machines) discovers EC2 instances or accepts static definitions." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "### SST (AWS)\n\nSST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials.", - "newString": "### Infrastructure (Alchemy)\n\nThe [infra module system](./infrastructure) is the most comprehensive approach -- it provisions cloud infrastructure (KMS keys, IAM roles, databases, caches) alongside your deployment using Alchemy. It handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. Use `infra:deploy --adopt` to bring existing resources under management.\n\n### Machines\n\nThe [machines module](./machines) maintains an inventory of your deployment targets. Machines can be declared statically or discovered from AWS EC2 at deploy time. The inventory feeds into the Studio UI's Deploy panel and Colmena for NixOS deployments.\n\n### SST (Legacy)\n\n[SST](./sst) is an alternative infrastructure approach using the SST framework directly. For new projects, the [infra module system](./infrastructure) is recommended as it provides more flexibility and integrates with the broader Stack infra pipeline." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"containers\", \"cloudflare\", \"fly\", \"sst\"]\n}", - "newString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.6s) - -Here's a summary of what was done: - -## How to Set Up Machines in Studio UI - -The **Deploy** panel in Studio (`/studio/deploy`) shows your machine inventory. To populate it: - -1. Enable the machines module in `.stack/config.nix` with either `source = "static"` (declare machines in Nix) or `source = "aws-ec2"` (discover from EC2 at deploy time) -2. Run `infra:deploy` to provision/discover machines -3. Run `infra:pull-outputs` to sync inventory to local state -4. Reload devshell and open Studio -- machines appear in the Deploy panel's Machines tab - -The Deploy panel has four tabs: **Machines** (card grid with host/SSH/roles/tags), **App Targets** (which apps deploy where), **Actions** (Colmena eval/build/apply buttons), and **Settings** (Colmena config). - -## Docs Created/Updated - -**New pages:** -- `apps/docs/content/docs/deployment/machines.mdx` -- Comprehensive machine inventory docs covering static machines, AWS EC2 discovery (tag mapping, host resolution, SSH defaults), Studio UI Deploy panel walkthrough, app-to-machine mapping, and end-to-end example -- `apps/docs/content/docs/deployment/infrastructure.mdx` -- Infrastructure provisioning docs covering the Alchemy-based infra module system, available modules (aws-secrets, deployment, database, cache, machines), `--adopt` for existing resources, stage selection, storage backends (SOPS/SSM/Chamber), generated files, Studio UI Infrastructure panel, and CLI reference - -**Updated pages:** -- `apps/docs/content/docs/deployment/index.mdx` -- Added Infrastructure and Machines cards at the top, updated platform-specific notes to reference new docs and mark SST as legacy -- `apps/docs/content/docs/deployment/meta.json` -- Added `infrastructure` and `machines` to the page order - ---- - -## User - -the studio UI should allow me to add/configure machines fully - ---- - -## Assistant (Build · claude-opus-4-6 · 168.7s) - -_Thinking:_ - -The user wants the Studio UI to have full machine configuration capabilities - not just viewing machines, but adding, editing, and configuring them directly from the UI. Let me explore what's currently there and what needs to be built. - -Let me look at: -1. The current deploy panel to understand the read-only view -2. How other panels handle config editing (e.g., the infra panel's Configure tab, the apps panel's config forms) -3. How data gets written back to Nix config via the agent -4. The agent API endpoints for writing nix data - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore config editing patterns in Studio", - "prompt": "I need to understand how the Studio UI writes configuration back to Nix. Search thoroughly for:\n\n1. How `useNixData` and `useNixDataMutation` work - find the hooks in apps/web/src/lib/use-agent.ts or similar\n2. How the infra panel's Configure tab saves changes (apps/web/src/components/studio/panels/infra/use-sst-config.ts)\n3. How other panels handle form state + saving to Nix (look for patterns using agent client methods like `updateNixData`, `setNixData`, `writeNixData`, or similar)\n4. The agent HTTP client methods for writing data back - search in apps/web/src/lib/agent-http-client.ts or packages/agent-client\n5. Find examples of dialogs/forms that add new items to Nix config (e.g., add-variable-dialog, add-user dialogs, etc.)\n\nReturn:\n- The exact hook signatures for reading/writing nix data\n- The agent client method signatures for writing data\n- 2-3 concrete examples of how existing panels save config changes (file paths, line numbers, patterns)\n- How the data flows: UI form -> hook -> agent client -> Go agent -> nix file write", - "subagent_type": "explore" -} -``` -**Output:** -``` -Now I have a complete picture. Here is the comprehensive analysis: - ---- - -## Complete Data Flow: Studio UI -> Nix File Writes - -### 1. Hook Signatures for Reading/Writing Nix Data - -There are **two generations** of hooks and **three write mechanisms**: - -#### A. Legacy Generic Hook: `useNixData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1153-1200 - -```typescript -function useNixData( - entity: string, - options: { initialData?: T; autoRefetch?: boolean } = {}, -): { - // ... all useQuery return fields - data: T | null; - mutate: (data: T) => Promise; // writes the entire entity - refetch: () => Promise<...>; -} -``` - -- **Read:** `GET /api/nix/data?entity=` -- transforms response keys from kebab-case to snake_case via `kebabToSnake()` -- **Write (mutate):** `POST /api/nix/data` with `{ entity, data: snakeToKebab(data) }` -- transforms keys from snake_case to kebab-case - -#### B. Legacy Map Hook: `useNixMapData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1210-1267 - -```typescript -function useNixMapData( - entity: string, - options: { initialData?: Record; autoRefetch?: boolean } = {}, -): { - data: Record; - set: (key: string, value: V) => Promise; // key-level upsert - update: (key: string, updates: Partial) => Promise; // merge update - remove: (key: string) => Promise; // key-level delete - refetch: () => Promise<...>; -} -``` - -- Uses `MapEntityClient` internally (in `agent.ts`, lines 112-163) -- **set** -> `POST /api/nix/data { entity, key, data }` -- **remove** -> `POST /api/nix/data { entity, key, delete: true }` - -#### C. Modern Typed Hooks: Connect-RPC (e.g., `useApps`, `useSetApps`) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 336-540 - -```typescript -function useApps(): UseQueryResult>; -function useSetApps(): UseMutationResult; // calls client.setApps(apps) -function useVariables(): UseQueryResult>; -function useSetVariables(): UseMutationResult; // calls client.setVariables(variables) -function useUsers(): UseQueryResult<...>; -function useSetUsers(): UseMutationResult; // calls client.setUsers(users) -function useConfig(): UseQueryResult<...>; -function useSetConfig(): UseMutationResult; // calls client.setConfig(config) -function useSecrets(): UseQueryResult<...>; -function useSetSecrets(): UseMutationResult; // calls client.setSecrets(secrets) -function useAws(): UseQueryResult<...>; -function useSetAws(): UseMutationResult; // calls client.setAws(aws) -``` - -These use `createClient(AgentService, transport)` to call the proto-defined RPCs. - -#### D. Patch Hook: `usePatchNixData` (Connect-RPC) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1718-1750 - -```typescript -function usePatchNixData(): UseMutationResult; - -interface PatchNixDataParams { - entity: string; // e.g., "apps", "config" - key: string; // e.g., "web" (for map entities), "" for non-map - path: string; // dot-separated camelCase path, e.g., "go.mainPackage" - value: string; // JSON-encoded value, e.g., '"./cmd/api"' - valueType: string; // "string" | "bool" | "number" | "list" | "object" | "null" -} -``` - -This is the surgical editor -- changes a single field without replacing the whole entity. - ---- - -### 2. Agent HTTP Client Methods for Writing Data - -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent.ts` - -#### Generic REST methods (lines 607-675): -```typescript -class AgentHttpClient { - async get(path: string): Promise; - async post(path: string, body: unknown): Promise; - async delete(path: string): Promise; -} -``` - -#### Nix entity clients (lines 514-557): -```typescript -nix = { - apps: new MapEntityClient(this, "apps"), - services: new MapEntityClient(this, "services"), - users: new MapEntityClient(this, "users"), - variables: new MapEntityClient(this, "variables"), - tasks: new MapEntityClient(this, "tasks"), - generatedFiles: new MapEntityClient(this, "generated-files"), - entity: (name: string) => new MapEntityClient(this, name), - config: async (options) => { /* GET /api/nix/config */ }, - refreshConfig: async () => { /* POST /api/nix/config */ }, -}; -``` - -#### MapEntityClient (lines 112-163): -```typescript -class MapEntityClient { - async all(): Promise>; // GET /api/nix/data?entity=... - async get(key: string): Promise; // reads all, extracts key - async set(key: string, value: V): Promise; // POST /api/nix/data {entity, key, data} - async update(key: string, updates: Partial): Promise; // read-merge-set - async remove(key: string): Promise; // POST /api/nix/data {entity, key, delete:true} -} -``` - ---- - -### 3. Concrete Examples of How Panels Save Config Changes - -#### Example 1: SST Infra Panel -- Configuration Tab -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` - -**Pattern:** `useNixData` (whole-entity replacement) - -1. **Hook setup** (line 78-82): `const { data: sstData, mutate: setSstData } = useNixData("sst", { initialData: DEFAULT_SST_DATA });` -2. **Local form state** (lines 85-97): `useState` for each field (`formData`, `hasChanges`, etc.) -3. **Sync effect** (lines 100-154): When `sstData` changes, merge with inherited defaults and populate form state -4. **Field updaters** (lines 157-192): `updateField()`, `updateNestedField()`, `updateOidcProviderField()` set local state and mark `hasChanges = true` -5. **Save** (lines 195-208): `handleSave()` calls `await setSstData(formData)` -- this triggers: - - `useNixData.mutation.mutationFn` -> `client.post("/api/nix/data", { entity: "sst", data: snakeToKebab(formData) })` - - Go agent `handleNixDataWrite` -> `s.store.WriteEntity("sst", data)` -> serializes to Nix, writes `.stack/config.nix` - -#### Example 2: Configuration Panel -- AWS/Step CA/Theme/IDE -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/configuration/use-configuration.ts` - -**Pattern:** Multiple independent `useNixData` hooks with per-section save callbacks - -1. **Hook setup** (lines 116-140): Multiple calls: - ```typescript - const { data: stepCaData, mutate: setStepCa } = useNixData("step-ca"); - const { data: awsData, mutate: setAws } = useNixData("aws"); - const { data: themeData, mutate: setTheme } = useNixData("theme"); - const { data: ideData, mutate: setIde } = useNixData("ide"); - ``` -2. **Per-section save** (lines 459-475 for Step CA): - ```typescript - const saveStepCa = useCallback(async () => { - await setStepCa({ - ...(stepCaData ?? {}), - enable: stepEnabled, - "ca-url": optionalValue(stepCaUrl), - "ca-fingerprint": optionalValue(stepCaFingerprint), - // ... other fields - }); - }, [...deps]); - ``` - Each `setXxx()` call replaces the entire entity in its data file. - -#### Example 3: Add Variable Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/variables/add-variable-dialog.tsx` - -**Pattern:** Direct `MapEntityClient` for key-level writes (not whole entity) - -1. **Get client** (line 64): `const agentClient = useAgentClient();` -2. **On submit** (lines 214-238): - ```typescript - const variablesClient = client.nix.mapEntity("variables"); - const existing = await variablesClient.get(fullId); - if (existing) { toast.error("already exists"); return; } - const newVariable: Variable = { id: fullId, value: trimmedValue }; - await variablesClient.set(fullId, newVariable); - ``` -3. For **secrets** (lines 204-221): Also writes to SOPS via `client.writeGroupSecret({...})` plus creates a variable stub entry. -4. The `variablesClient.set(key, value)` call -> `POST /api/nix/data { entity: "variables", key: fullId, data: snakeToKebabValues({...}) }` -> Go agent `handleKeyLevelWrite` -> `s.store.SetKey("variables", key, value)` -- reads existing map, inserts key, serializes entire map back to Nix. - -#### Example 4: Add App Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx` - -Same pattern as variables (lines 76-100): -```typescript -const appsClient = client.nix.mapEntity("apps"); -const existingApp = await appsClient.get(values.id); -if (existingApp) { toast.error("already exists"); return; } -const newApp: AppEntity = { id: values.id, name: ..., path: ..., ... }; -await appsClient.set(values.id, newApp); -``` - -#### Example 5: Packages Panel -- Whole-Entity Array Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/packages/use-packages.ts` - -```typescript -const { data: userPackages, mutate: setUserPackages } = useNixData("packages", { initialData: [] }); - -const handleAddPackage = async (pkg) => { - const newPackages = [...currentPackages, attrPath]; - await setUserPackages(newPackages); // replaces entire packages list -}; -``` - ---- - -### 4. Complete Data Flow Diagram - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ UI Form (React Component) │ -│ ├── useState for local form state │ -│ ├── onChange handlers update local state │ -│ └── onSave calls hook mutate/set method │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Hook Layer (use-agent.ts) │ -│ │ -│ Path A: useNixData(entity).mutate(data) │ -│ ├── snakeToKebab(data) transforms keys to Nix convention │ -│ └── client.post("/api/nix/data", { entity, data }) │ -│ │ -│ Path B: MapEntityClient.set(key, value) │ -│ ├── snakeToKebabValues({[key]: value}) transforms inner keys │ -│ └── client.post("/api/nix/data", { entity, key, data }) │ -│ │ -│ Path C: usePatchNixData().mutate({entity,key,path,value,valueType}) │ -│ └── Connect-RPC: client.patchNixData({...}) │ -│ │ -│ Path D: useSetApps/useSetVariables etc. │ -│ └── Connect-RPC: client.setApps(proto_message) │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ HTTP or Connect-RPC to localhost:9876 - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Go Agent (apps/stack-go/internal/agent/server/) │ -│ │ -│ Path A/B: handleNixData (nix_data.go:42) │ -│ ├── POST: handleNixDataWrite (line 191) │ -│ │ ├── With key: handleKeyLevelWrite -> store.SetKey(entity,key,val)│ -│ │ │ Reads existing map, inserts/updates key, writes back │ -│ │ └── Without key: store.WriteEntity(entity, data) │ -│ │ Replaces entire entity │ -│ └── GET: handleNixDataRead -> store.ReadEntity / evaluateConfig │ -│ │ -│ Path C: PatchNixData (connect_patch.go:25) │ -│ ├── Builds full path: entity + "." + key + "." + path │ -│ ├── Prepends "stack." -> strips it -> gets config-internal path │ -│ └── store.PatchConsolidatedData(configPath, parsedValue) │ -│ Walks dot-separated path, creates intermediate maps, sets value │ -│ │ -│ Path D: SetApps etc. (connect_entities_gen.go) │ -│ └── writeNixEntityJSON(entity, protojsonBytes) │ -│ CamelCaseToNixJSON -> unwrap envelope -> WriteEntity │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Store Layer (apps/stack-go/pkg/nixdata/store.go) │ -│ │ -│ WriteEntity(entity, data) (line 177): │ -│ ├── If consolidated config: Read config.nix -> update entity key │ -│ │ -> WriteConsolidatedData (line 427) │ -│ │ -> nixser.SerializeWithSections(data) -> write .stack/ │ -│ │ config.nix │ -│ └── If legacy per-file: nixser.SerializeIndented(data) │ -│ -> write .stack/data/.nix │ -│ │ -│ SetKey(entity, key, value) (line 267): │ -│ readExistingMap -> set key in map -> writeMap │ -│ -> WriteConsolidatedData or per-file write │ -│ │ -│ PatchConsolidatedData(path, value) (line 456): │ -│ ReadConsolidatedData -> walk path with CamelToKebab conversion │ -│ -> set leaf value -> WriteConsolidatedData │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Nix File on Disk │ -│ │ -│ .stack/config.nix (consolidated, primary target) │ -│ Contains: { apps = {...}; variables = {...}; sst = {...}; ... } │ -│ │ -│ .stack/data/.nix (legacy per-entity files) │ -│ Contains: { key1 = {...}; key2 = {...}; } │ -│ │ -│ Serialization: Go nixser package converts Go map[string]any -> Nix │ -│ expression syntax (attrsets, strings, lists, booleans, numbers) │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - -### Key-Case Transformation Boundary - -The system maintains two naming conventions: -- **TypeScript/Proto side:** `snake_case` (matching protobuf conventions) -- **Nix side:** `kebab-case` (matching Nix conventions) - -Transformation happens in two places: -1. **TypeScript:** `snakeToKebab()` / `kebabToSnake()` in `/Users/cm/git/darkmatter/stack/apps/web/src/lib/nix-data/index.ts` (lines 48-84). Map keys (user-defined IDs like app names, variable names) are explicitly preserved using the `MAP_FIELD_NAMES` set (lines 20-42). -2. **Go:** `CamelCaseToNixJSON()` / `NixJSONToCamelCase()` in `/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/transform.go` for the Connect-RPC path. Same map-field-names exemption list. - -### SSE Feedback Loop - -After any write, the Go agent broadcasts an SSE event (`config.changed`) which the `AgentSSEProvider` in the browser receives, triggering TanStack Query cache invalidation so all UI panels automatically refresh with the new data. - - -session_id: ses_35678281cffeylSnbvcqulDIO4 - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.5s) - -Let me look at a concrete add dialog example and the nix data hooks more closely to match the patterns exactly. - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { Button } from "@ui/button"; -00004| import { -00005| Dialog, -00006| DialogContent, -00007| DialogDescription, -00008| DialogFooter, -00009| DialogHeader, -00010| DialogTitle, -00011| } from "@ui/dialog"; -00012| import { Loader2, Plus } from "lucide-react"; -00013| import { useCallback, useState } from "react"; -00014| import { toast } from "sonner"; -00015| import { useAgentContext, useAgentClient } from "@/lib/agent-provider"; -00016| import type { AppEntity } from "@/lib/types"; -00017| -00018| import { -00019| type AppForm, -00020| AppFormFields, -00021| type AppFormValues, -00022| parsePortValue, -00023| } from "./app-form-fields"; -00024| -00025| interface AddAppDialogProps { -00026| onSuccess: () => void; -00027| } -00028| -00029| export function AddAppDialog({ onSuccess }: AddAppDialogProps) { -00030| const { token } = useAgentContext(); -00031| const [dialogOpen, setDialogOpen] = useState(false); -00032| const [isSaving, setIsSaving] = useState(false); -00033| const [formRef, setFormRef] = useState(null); -00034| const [isFormValid, setIsFormValid] = useState(false); -00035| -00036| const handleFormReady = useCallback((form: AppForm) => { -00037| setFormRef(form); -00038| }, []); -00039| -00040| const handleValuesChange = useCallback((values: AppFormValues) => { -00041| const hasId = values.id?.trim().length > 0; -00042| const hasPath = values.path?.trim().length > 0; -00043| setIsFormValid(hasId && hasPath); -00044| }, []); -00045| -00046| const handleOpenChange = (open: boolean) => { -00047| setDialogOpen(open); -00048| if (!open) { -00049| // Reset form and validity when closing -00050| formRef?.reset(); -00051| setIsFormValid(false); -00052| } -00053| }; -00054| -00055| const handleSubmit = async () => { -00056| if (!formRef || !token) { -00057| toast.error("Not connected to agent"); -00058| return; -00059| } -00060| -00061| // Trigger validation -00062| const isValid = await formRef.trigger(); -00063| if (!isValid) return; -00064| -00065| const values = formRef.getValues(); -00066| -00067| if (!values.id.trim()) { -00068| toast.error("Please enter an app ID"); -00069| return; -00070| } -00071| -00072| setIsSaving(true); -00073| try { -00074| const client = useAgentClient(); -00075| if (token) client.setToken(token); -00076| const appsClient = client.nix.mapEntity("apps"); -00077| -00078| const existingApp = await appsClient.get(values.id); -00079| if (existingApp) { -00080| toast.error(`App "${values.id}" already exists`); -00081| setIsSaving(false); -00082| return; -00083| } -00084| -00085| // With simplified schema: environments is Record -00086| // Start with default "dev" environment -00087| const newApp: AppEntity = { -00088| id: values.id, -00089| name: values.name || values.id, -00090| description: values.description || undefined, -00091| path: values.path || `apps/${values.id}`, -00092| type: values.type || "bun", -00093| port: parsePortValue(values.port), -00094| domain: values.domain || undefined, -00095| environments: { -00096| dev: { name: "dev", env: {} }, -00097| }, -00098| }; -00099| -00100| await appsClient.set(values.id, newApp); -00101| toast.success( -00102| `Created app "${values.id}". You can now configure tasks and variables.`, -00103| ); -00104| handleOpenChange(false); -00105| onSuccess(); -00106| } catch (err) { -00107| toast.error(err instanceof Error ? err.message : "Failed to create app"); -00108| } finally { -00109| setIsSaving(false); -00110| } -00111| }; -00112| -00113| return ( -00114| -00115| -00123| -00124| -00125| Add New App -00126| -00127| Create a new app. You can configure tasks and variables after -00128| creation. -00129| -00130| -00131|
-00132| -00138|
-00139| -00140| -00143| -00147| -00148|
-00149|
-00150| ); -00151| } -00152| - -(End of file - total 152 lines) -
-``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts", - "offset": 1150, - "limit": 130 -} -``` -**Output:** -``` - -01151| * - aws → useAws() -01152| */ -01153| export function useNixData( -01154| entity: string, -01155| options: { initialData?: T; autoRefetch?: boolean } = {}, -01156| ) { -01157| const { initialData, autoRefetch = true } = options; -01158| const client = useAgentClient(); -01159| const queryClient = useQueryClient(); -01160| -01161| const queryKey = [...agentQueryKeys.all, "entity", entity]; -01162| -01163| const query = useQuery({ -01164| queryKey, -01165| queryFn: async () => { -01166| // Use the underlying HTTP API to fetch entity data -01167| const response = await client.get<{ -01168| success: boolean; -01169| data: { entity: string; exists: boolean; data: T }; -01170| }>(`/api/nix/data?entity=${encodeURIComponent(entity)}`); -01171| -01172| if (!response.success || !response.data.exists) { -01173| return null; -01174| } -01175| return kebabToSnake(response.data.data) as T; -01176| }, -01177| initialData, -01178| refetchOnWindowFocus: autoRefetch, -01179| }); -01180| -01181| const mutation = useMutation({ -01182| mutationFn: async (data: T) => { -01183| await client.post("/api/nix/data", { -01184| entity, -01185| data: snakeToKebab(data), -01186| }); -01187| // Return the new data -01188| return data; -01189| }, -01190| onSuccess: () => { -01191| queryClient.invalidateQueries({ queryKey }); -01192| }, -01193| }); -01194| -01195| return { -01196| ...query, -01197| mutate: mutation.mutateAsync, -01198| refetch: query.refetch, -01199| }; -01200| } -01201| -01202| /** -01203| * Generic hook for map-style Nix data entities with key-level operations. -01204| * Uses the legacy HTTP client under the hood. -01205| * -01206| * @deprecated For standard entities, prefer specific hooks: -01207| * - apps → useApps() + useSetApps() -01208| * - variables → useVariables() + useSetVariables() -01209| */ -01210| export function useNixMapData( -01211| entity: string, -01212| options: { initialData?: Record; autoRefetch?: boolean } = {}, -01213| ) { -01214| const { initialData, autoRefetch = true } = options; -01215| const client = useAgentClient(); -01216| const queryClient = useQueryClient(); -01217| const mapClient = useMemo( -01218| () => client.mapEntity(entity), -01219| [client, entity], -01220| ); -01221| -01222| const queryKey = [...agentQueryKeys.all, "mapEntity", entity]; -01223| -01224| const query = useQuery({ -01225| queryKey, -01226| queryFn: async () => { -01227| return mapClient.all(); -01228| }, -01229| initialData, -01230| refetchOnWindowFocus: autoRefetch, -01231| }); -01232| -01233| const setMutation = useMutation({ -01234| mutationFn: async ({ key, value }: { key: string; value: V }) => { -01235| await mapClient.set(key, value); -01236| }, -01237| onSuccess: () => { -01238| queryClient.invalidateQueries({ queryKey }); -01239| }, -01240| }); -01241| -01242| const updateMutation = useMutation({ -01243| mutationFn: async ({ key, updates }: { key: string; updates: Partial }) => { -01244| await mapClient.update(key, updates); -01245| }, -01246| onSuccess: () => { -01247| queryClient.invalidateQueries({ queryKey }); -01248| }, -01249| }); -01250| -01251| const removeMutation = useMutation({ -01252| mutationFn: async (key: string) => { -01253| await mapClient.remove(key); -01254| }, -01255| onSuccess: () => { -01256| queryClient.invalidateQueries({ queryKey }); -01257| }, -01258| }); -01259| -01260| return { -01261| ...query, -01262| set: (key: string, value: V) => setMutation.mutateAsync({ key, value }), -01263| update: (key: string, updates: Partial) => updateMutation.mutateAsync({ key, updates }), -01264| remove: (key: string) => removeMutation.mutateAsync(key), -01265| refetch: query.refetch, -01266| }; -01267| } -01268| -01269| // ============================================================================= -01270| // Turbo Package Graph -01271| // ============================================================================= -01272| -01273| export interface TurboTask { -01274| name: string; -01275| command?: string; -01276| } -01277| -01278| export interface TurboPackage { -01279| name: string; -01280| path: string; - -(File has more lines. Use 'offset' parameter to read beyond line 1280) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -Now let me look at what imports are available in the UI components and how dialogs are structured: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent-provider.tsx", - "offset": 1, - "limit": 50 -} -``` -**Output:** -``` - -00002| -00003| import { -00004| createContext, -00005| type ReactNode, -00006| useCallback, -00007| useContext, -00008| useEffect, -00009| useMemo, -00010| useRef, -00011| useState, -00012| } from "react"; -00013| import { AgentHttpClient, AGENT_AUTH_ERROR_EVENT } from "./agent"; -00014| import { useAgent, useAgentHealth } from "@/lib/use-agent"; -00015| import { useAgentSSEOptional } from "@/lib/agent-sse-provider"; -00016| -00017| const STORAGE_KEY = "stack.agent.token"; -00018| -00019| /** -00020| * Get the initial token from localStorage (runs synchronously during useState init). -00021| * This prevents a flash of "Connect to Agent" on page refresh. -00022| */ -00023| function getInitialToken(): string | null { -00024| if (typeof window === "undefined") return null; -00025| -00026| // Check for token in query parameter first -00027| const urlParams = new URLSearchParams(window.location.search); -00028| const queryToken = urlParams.get("token"); -00029| if (queryToken && queryToken.length >= 10) { -00030| return queryToken; -00031| } -00032| -00033| // Otherwise try localStorage -00034| return localStorage.getItem(STORAGE_KEY); -00035| } -00036| -00037| /** -00038| * Decode a JWT token without verifying the signature. -00039| * Returns the payload or null if invalid. -00040| */ -00041| function decodeJWT(token: string): { agent_id?: string; exp?: number } | null { -00042| try { -00043| const parts = token.split("."); -00044| if (parts.length !== 3) return null; -00045| -00046| // Decode the payload (second part) -00047| const payload = parts[1]; -00048| // Handle URL-safe base64 -00049| const base64 = payload.replace(/-/g, "+").replace(/_/g, "/"); -00050| const jsonPayload = atob(base64); -00051| return JSON.parse(jsonPayload); - -(File has more lines. Use 'offset' parameter to read beyond line 51) - -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/web/src/components/studio/panels/deploy/*" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 36.1s) - -Good -- there's only one file in the deploy folder. Now I'll build the complete machine management UI. I'll create: - -1. A `use-machines.ts` hook for reading/writing machine config -2. A `machine-form-fields.tsx` shared form component -3. An `add-machine-dialog.tsx` for adding machines -4. An `edit-machine-dialog.tsx` for editing machines -5. Update `deploy-panel.tsx` to integrate everything - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "content": "\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";\n\n// =============================================================================\n// Types — match the Nix module schema (nix/stack/infra/modules/machines)\n// =============================================================================\n\nexport interface MachineSSH {\n\tuser: string;\n\tport: number;\n\tkey_path: string | null;\n}\n\nexport interface MachineConfig {\n\tid?: string | null;\n\tname: string | null;\n\thost: string | null;\n\tssh: MachineSSH;\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublic_ip: string | null;\n\tprivate_ip: string | null;\n\ttarget_env: string | null;\n\tlabels: Record;\n\tnixos_profile: string | null;\n\tnixos_modules: string[];\n\tenv: Record;\n\tmetadata: Record;\n}\n\nexport interface AwsFilter {\n\tname: string;\n\tvalues: string[];\n}\n\nexport interface AwsMachinesConfig {\n\tregion: string | null;\n\tinstance_ids: string[];\n\tfilters: AwsFilter[];\n\tname_tag_keys: string[];\n\trole_tag_keys: string[];\n\ttag_keys: string[];\n\tenv_tag_keys: string[];\n\thost_preference: string[];\n\tssh: MachineSSH;\n}\n\nexport interface MachinesModuleConfig {\n\tenable: boolean;\n\tsource: \"static\" | \"aws-ec2\";\n\taws: AwsMachinesConfig;\n\tmachines: Record;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nexport const DEFAULT_SSH: MachineSSH = {\n\tuser: \"root\",\n\tport: 22,\n\tkey_path: null,\n};\n\nexport const DEFAULT_MACHINE: MachineConfig = {\n\tid: null,\n\tname: null,\n\thost: null,\n\tssh: { ...DEFAULT_SSH },\n\ttags: [],\n\troles: [],\n\tprovider: null,\n\tarch: null,\n\tpublic_ip: null,\n\tprivate_ip: null,\n\ttarget_env: null,\n\tlabels: {},\n\tnixos_profile: null,\n\tnixos_modules: [],\n\tenv: {},\n\tmetadata: {},\n};\n\nconst DEFAULT_AWS_CONFIG: AwsMachinesConfig = {\n\tregion: null,\n\tinstance_ids: [],\n\tfilters: [{ name: \"instance-state-name\", values: [\"running\"] }],\n\tname_tag_keys: [\"Name\"],\n\trole_tag_keys: [\"stack:role\", \"role\"],\n\ttag_keys: [\"stack:tag\", \"tag\"],\n\tenv_tag_keys: [\"stack:env\", \"env\", \"stage\"],\n\thost_preference: [\"publicDns\", \"publicIp\", \"privateIp\"],\n\tssh: { ...DEFAULT_SSH },\n};\n\nconst DEFAULT_CONFIG: MachinesModuleConfig = {\n\tenable: false,\n\tsource: \"static\",\n\taws: DEFAULT_AWS_CONFIG,\n\tmachines: {},\n};\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_CONFIG;\n\t\tconst machines = (rawInfra as Record).machines as Partial | undefined;\n\t\tif (!machines) return DEFAULT_CONFIG;\n\t\treturn {\n\t\t\tenable: machines.enable ?? false,\n\t\t\tsource: machines.source ?? \"static\",\n\t\t\taws: {\n\t\t\t\t...DEFAULT_AWS_CONFIG,\n\t\t\t\t...(machines.aws ?? {}),\n\t\t\t},\n\t\t\tmachines: (machines.machines ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: MachinesModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\tmachines: newConfig,\n\t\t} as any);\n\t};\n\n\tconst addMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeMachine = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.machines;\n\t\tupdated.machines = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateSettings = async (settings: Partial) => {\n\t\tawait saveConfig({ ...config, ...settings });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [113:8] Variable 'client' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "export function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "export function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [5:10] Identifier 'useAgentClient' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "import { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";", - "newString": "import { useNixData } from \"@/lib/use-agent\";" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 29.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx", - "content": "\"use client\";\n\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport type { MachineConfig } from \"./use-machines\";\n\ninterface MachineFormFieldsProps {\n\tvalues: MachineConfig;\n\tonChange: (values: MachineConfig) => void;\n\tshowIdField?: boolean;\n\tmachineId?: string;\n\tonIdChange?: (id: string) => void;\n}\n\nconst ARCH_OPTIONS = [\n\t{ value: \"x86_64-linux\", label: \"x86_64-linux\" },\n\t{ value: \"aarch64-linux\", label: \"aarch64-linux\" },\n];\n\nconst PROVIDER_OPTIONS = [\n\t{ value: \"aws\", label: \"AWS\" },\n\t{ value: \"hetzner\", label: \"Hetzner\" },\n\t{ value: \"gcp\", label: \"GCP\" },\n\t{ value: \"azure\", label: \"Azure\" },\n\t{ value: \"digitalocean\", label: \"DigitalOcean\" },\n\t{ value: \"bare-metal\", label: \"Bare Metal\" },\n];\n\nconst ENV_OPTIONS = [\n\t{ value: \"production\", label: \"Production\" },\n\t{ value: \"staging\", label: \"Staging\" },\n\t{ value: \"development\", label: \"Development\" },\n];\n\nfunction updateField(\n\tvalues: MachineConfig,\n\tonChange: (v: MachineConfig) => void,\n\tkey: K,\n\tvalue: MachineConfig[K],\n) {\n\tonChange({ ...values, [key]: value });\n}\n\nexport function MachineFormFields({\n\tvalues,\n\tonChange,\n\tshowIdField,\n\tmachineId,\n\tonIdChange,\n}: MachineFormFieldsProps) {\n\treturn (\n\t\t
\n\t\t\t{/* Identity */}\n\t\t\t{showIdField && (\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t onIdChange?.(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUnique identifier used as the Nix attribute name. Use lowercase with hyphens.\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t)}\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\tupdateField(values, onChange, \"name\", e.target.value || null)\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t{/* Connection */}\n\t\t\t
\n\t\t\t\tConnection\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"host\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tuser: e.target.value || \"root\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tport: Number.parseInt(e.target.value, 10) || 22,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tkey_path: e.target.value || null,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Networking */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"public_ip\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"private_ip\",\n\t\t\t\t\t\t\t\te.target.value || null,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Classification */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"arch\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ARCH_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"provider\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{PROVIDER_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"target_env\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ENV_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Roles and Tags */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"roles\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for app-to-machine targeting\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"tags\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for grouping and filtering\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.7s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport {\n\ttype MachineConfig,\n\tDEFAULT_MACHINE,\n\ttype useMachinesConfig,\n} from \"./use-machines\";\n\ninterface AddMachineDialogProps {\n\tmachines: ReturnType;\n}\n\nexport function AddMachineDialog({ machines }: AddMachineDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\tconst [machineId, setMachineId] = useState(\"\");\n\tconst [values, setValues] = useState({ ...DEFAULT_MACHINE });\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetMachineId(\"\");\n\t\t\tsetValues({ ...DEFAULT_MACHINE });\n\t\t}\n\t};\n\n\tconst isValid = machineId.trim().length > 0 && /^[a-z0-9-]+$/.test(machineId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = machineId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"Machine ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (machines.config.machines[id]) {\n\t\t\ttoast.error(`Machine \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.addMachine(id, values);\n\t\t\ttoast.success(`Machine \"${id}\" added`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add machine\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tAdd Machine\n\t\t\t\t\t\n\t\t\t\t\t\tAdd a new machine to your deployment inventory.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2 } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport type { MachineConfig, useMachinesConfig } from \"./use-machines\";\n\ninterface EditMachineDialogProps {\n\tmachineId: string;\n\tmachine: MachineConfig;\n\tmachines: ReturnType;\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n}\n\nexport function EditMachineDialog({\n\tmachineId,\n\tmachine,\n\tmachines,\n\topen,\n\tonOpenChange,\n}: EditMachineDialogProps) {\n\tconst [saving, setSaving] = useState(false);\n\tconst [deleting, setDeleting] = useState(false);\n\tconst [values, setValues] = useState({ ...machine });\n\n\tuseEffect(() => {\n\t\tif (open) {\n\t\t\tsetValues({ ...machine });\n\t\t}\n\t}, [open, machine]);\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateMachine(machineId, values);\n\t\t\ttoast.success(`Machine \"${machineId}\" updated`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to update machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\tsetDeleting(true);\n\t\ttry {\n\t\t\tawait machines.removeMachine(machineId);\n\t\t\ttoast.success(`Machine \"${machineId}\" removed`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to remove machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetDeleting(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tEdit Machine: {machineId}\n\t\t\t\t\t\n\t\t\t\t\t\tUpdate machine configuration. Changes are written to your Nix config.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{deleting && }\n\t\t\t\t\t\tDelete\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 96.6s) - -Now update the deploy panel to integrate everything -- add machine management, edit capability, settings for AWS EC2 config: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "content": "\"use client\";\n\n/**\n * Deploy Panel - Colmena-centric deployment management.\n *\n * Shows machine inventory, app-to-machine mapping, and deploy actions\n * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and\n * AWS EC2 discovery configuration.\n */\n\nimport { useState, useMemo } from \"react\";\nimport { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@ui/card\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@ui/tabs\";\nimport {\n\tActivity,\n\tAlertCircle,\n\tCheckCircle,\n\tCloudOff,\n\tCpu,\n\tHardDrive,\n\tLoader2,\n\tNetwork,\n\tPencil,\n\tPlay,\n\tRefreshCw,\n\tRocket,\n\tServer,\n\tSettings,\n\tShield,\n\tXCircle,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { useAgentContext } from \"@/lib/agent-provider\";\nimport { useNixConfig } from \"@/lib/use-agent\";\nimport { PanelHeader } from \"../shared/panel-header\";\nimport { cn } from \"@/lib/utils\";\nimport { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface MachineInfo {\n\tid: string;\n\tname: string;\n\thost: string | null;\n\tssh: {\n\t\tuser: string;\n\t\tport: number;\n\t\tkeyPath: string | null;\n\t};\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublicIp: string | null;\n\tprivateIp: string | null;\n\ttargetEnv: string | null;\n\tlabels: Record;\n}\n\ninterface AppDeployMapping {\n\tenable: boolean;\n\ttargets: string[];\n\tresolvedTargets: string[];\n\trole: string | null;\n\tnixosModules: string[];\n\tsystem: string | null;\n}\n\ninterface ColmenaConfig {\n\tenable: boolean;\n\tmachineSource: string;\n\tgenerateHive: boolean;\n\tconfig: string;\n\tmachineCount: number;\n\tmachineIds: string[];\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nfunction useColmenaData() {\n\tconst { data: nixConfig, isLoading, refetch } = useNixConfig();\n\n\tconst result = useMemo(() => {\n\t\tconst cfg = nixConfig as Record | null | undefined;\n\t\tif (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null };\n\n\t\tconst serializable = cfg.serializable as Record | undefined;\n\t\tconst colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null;\n\n\t\tconst colmenaData = cfg.colmena as Record | undefined;\n\t\tconst machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record;\n\n\t\tconst rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>;\n\t\tconst appDeploy: Record = {};\n\n\t\tfor (const [appName, appCfg] of Object.entries(rawApps)) {\n\t\t\tconst deploy = appCfg.deploy as Record | undefined;\n\t\t\tif (deploy?.enable) {\n\t\t\t\tappDeploy[appName] = {\n\t\t\t\t\tenable: true,\n\t\t\t\t\ttargets: (deploy.targets as string[]) ?? [],\n\t\t\t\t\tresolvedTargets: (deploy.resolvedTargets as string[]) ?? [],\n\t\t\t\t\trole: (deploy.role as string | null) ?? null,\n\t\t\t\t\tnixosModules: (deploy.nixosModules as string[]) ?? [],\n\t\t\t\t\tsystem: (deploy.system as string | null) ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { machines: machinesComputed, appDeploy, colmenaConfig };\n\t}, [nixConfig]);\n\n\treturn { ...result, isLoading, refetch };\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\nfunction MachineCard({\n\tmachine,\n\tonEdit,\n}: {\n\tmachine: MachineInfo;\n\tonEdit?: () => void;\n}) {\n\tconst isReachable = machine.host !== null;\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.name || machine.id}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{onEdit && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{isReachable ? (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machine.host}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tno host\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.ssh.user}@{machine.ssh.port}\n\t\t\t\t\t
\n\t\t\t\t\t{machine.arch && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.arch}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.provider && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.provider}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.targetEnv && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.targetEnv}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{(machine.tags.length > 0 || machine.roles.length > 0) && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{machine.roles.map((role) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{role}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{machine.tags.map((tag) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{tag}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t
\n\t\t\t\t)}\n\t\t\t
\n\t\t\n\t);\n}\n\nfunction AppTargetRow({\n\tappName,\n\tdeploy,\n\tmachines,\n}: {\n\tappName: string;\n\tdeploy: AppDeployMapping;\n\tmachines: Record;\n}) {\n\tconst resolvedNames = deploy.resolvedTargets.map(\n\t\t(id) => machines[id]?.name ?? id,\n\t);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appName}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{deploy.targets.length > 0\n\t\t\t\t\t\t\t? `Targets: ${deploy.targets.join(\", \")}`\n\t\t\t\t\t\t\t: deploy.role\n\t\t\t\t\t\t\t\t? `Role: ${deploy.role}`\n\t\t\t\t\t\t\t\t: \"No targets defined\"}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{deploy.role && (\n\t\t\t\t\t\n\t\t\t\t\t\t{deploy.role}\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{resolvedNames.length} machine{resolvedNames.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Settings: AWS EC2 Discovery Configuration\n// =============================================================================\n\nfunction AwsEc2Settings({ machines }: { machines: ReturnType }) {\n\tconst cfg = machines.config.aws;\n\tconst [saving, setSaving] = useState(false);\n\tconst [region, setRegion] = useState(cfg.region ?? \"\");\n\tconst [sshUser, setSshUser] = useState(cfg.ssh.user);\n\tconst [sshPort, setSshPort] = useState(cfg.ssh.port);\n\tconst [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? \"\");\n\tconst [hostPref, setHostPref] = useState(cfg.host_preference.join(\", \"));\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateSettings({\n\t\t\t\tsource: \"aws-ec2\",\n\t\t\t\tenable: true,\n\t\t\t\taws: {\n\t\t\t\t\t...cfg,\n\t\t\t\t\tregion: region || null,\n\t\t\t\t\tssh: {\n\t\t\t\t\t\tuser: sshUser || \"root\",\n\t\t\t\t\t\tport: sshPort || 22,\n\t\t\t\t\t\tkey_path: sshKeyPath || null,\n\t\t\t\t\t},\n\t\t\t\t\thost_preference: hostPref.split(\",\").map((s) => s.trim()).filter(Boolean),\n\t\t\t\t},\n\t\t\t});\n\t\t\ttoast.success(\"AWS EC2 settings saved\");\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to save\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\tAWS EC2 Discovery\n\t\t\t\t\n\t\t\t\t\tAuto-discover machines from running EC2 instances\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRegion(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setHostPref(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\tDefault SSH\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshUser(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshPort(Number.parseInt(e.target.value, 10) || 22)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshKeyPath(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport function DeployPanel() {\n\tconst { isConnected } = useAgentContext();\n\tconst { machines: computedMachines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData();\n\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);\n\tconst [editingMachine, setEditingMachine] = useState<{ id: string; config: MachineConfig } | null>(null);\n\n\t// Merge computed machines (from colmena/infra) with static config machines\n\tconst configMachines = machinesConfig.config.machines;\n\tconst computedList = Object.values(computedMachines);\n\tconst configList = Object.entries(configMachines);\n\n\t// Build combined machine list: computed takes precedence, config fills gaps\n\tconst allMachineEntries = useMemo(() => {\n\t\tconst seen = new Set();\n\t\tconst entries: Array<{ id: string; info: MachineInfo; configurable: boolean }> = [];\n\n\t\t// Add computed machines\n\t\tfor (const m of computedList) {\n\t\t\tseen.add(m.id);\n\t\t\tentries.push({ id: m.id, info: m, configurable: !!configMachines[m.id] });\n\t\t}\n\n\t\t// Add config-only machines not in computed\n\t\tfor (const [key, cfg] of configList) {\n\t\t\tif (!seen.has(key)) {\n\t\t\t\tentries.push({\n\t\t\t\t\tid: key,\n\t\t\t\t\tinfo: {\n\t\t\t\t\t\tid: key,\n\t\t\t\t\t\tname: cfg.name ?? key,\n\t\t\t\t\t\thost: cfg.host,\n\t\t\t\t\t\tssh: { user: cfg.ssh.user, port: cfg.ssh.port, keyPath: cfg.ssh.key_path },\n\t\t\t\t\t\ttags: cfg.tags,\n\t\t\t\t\t\troles: cfg.roles,\n\t\t\t\t\t\tprovider: cfg.provider,\n\t\t\t\t\t\tarch: cfg.arch,\n\t\t\t\t\t\tpublicIp: cfg.public_ip,\n\t\t\t\t\t\tprivateIp: cfg.private_ip,\n\t\t\t\t\t\ttargetEnv: cfg.target_env,\n\t\t\t\t\t\tlabels: cfg.labels,\n\t\t\t\t\t},\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}, [computedList, configList, configMachines]);\n\n\tconst machineCount = allMachineEntries.length;\n\tconst appDeployEntries = Object.entries(appDeploy);\n\tconst healthyCount = allMachineEntries.filter((m) => m.info.host !== null).length;\n\tconst unhealthyCount = machineCount - healthyCount;\n\n\tconst handleRefresh = async () => {\n\t\tsetIsRefreshing(true);\n\t\ttry {\n\t\t\tawait Promise.all([refetch(), machinesConfig.refetch()]);\n\t\t} finally {\n\t\t\tsetIsRefreshing(false);\n\t\t}\n\t};\n\n\tif (!isConnected) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Agent Not Connected

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tConnect to the stack agent to manage deployments.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t);\n\t}\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t);\n\t}\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRefreshing ? \"Refreshing...\" : \"Refresh\"}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t}\n\t\t\t/>\n\n\t\t\t{/* Status overview */}\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Machines

\n\t\t\t\t\t\t\t

{machineCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-green-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-green-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Reachable

\n\t\t\t\t\t\t\t

{healthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-amber-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-amber-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Unreachable

\n\t\t\t\t\t\t\t

{unhealthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-blue-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-blue-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Deploy-enabled apps

\n\t\t\t\t\t\t\t

{appDeployEntries.length}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t\n\n\t\t\t\t{/* Machines Tab */}\n\t\t\t\t\n\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Machines

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tAdd machines manually using the Add Machine button above,\n\t\t\t\t\t\t\t\t\t\tor configure AWS EC2 discovery in Settings and run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:deploy.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{allMachineEntries.map((entry) => (\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst cfg = configMachines[entry.id];\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (cfg) setEditingMachine({ id: entry.id, config: cfg });\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t\n\t\t\t\t\t{appDeployEntries.length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No App Targets

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tNo apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{appDeployEntries.map(([appName, deploy]) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* Actions Tab */}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tColmena Actions\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tRun Colmena commands against your fleet\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\tNo machines in inventory. Add machines or configure EC2 discovery first.\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tActions run the generated wrapper scripts with your configured defaults.\n\t\t\t\t\t\t\t\t\t\tUse the CLI for advanced options: colmena-apply --on tag:prod\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t{/* Settings Tab */}\n\t\t\t\t\n\t\t\t\t\t{/* Module enable + source selection */}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tMachines Module\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tConfigure how machines are sourced for deployment\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tEnable the machines infra module\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({ enable: checked })\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({\n\t\t\t\t\t\t\t\t\t\t\tsource: v as \"static\" | \"aws-ec2\",\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tStatic (defined in config)\n\t\t\t\t\t\t\t\t\t\tAWS EC2 (auto-discover)\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t{colmenaConfig && (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Hive Config

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.config ?? \".stack/state/colmena/hive.nix\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Generate Hive

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.generateHive ? \"Yes\" : \"No\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* AWS EC2 settings (shown when source is aws-ec2) */}\n\t\t\t\t\t{machinesConfig.config.source === \"aws-ec2\" && (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Edit dialog */}\n\t\t\t{editingMachine && (\n\t\t\t\t {\n\t\t\t\t\t\tif (!open) setEditingMachine(null);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.9s) - -No LSP errors in our new files. The pre-existing errors in `use-sst-config.ts`, `dashboard-sidebar.tsx`, and `use-configuration.ts` are unrelated. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.0s) - -Here's what was built: - -## New Files - -**`apps/web/src/components/studio/panels/deploy/use-machines.ts`** -- `useMachinesConfig()` hook that reads/writes the `infra.machines` config via `useNixData<>("infra")` -- Full CRUD: `addMachine()`, `updateMachine()`, `removeMachine()`, `updateSettings()` -- Types matching the Nix module schema: `MachineConfig`, `MachineSSH`, `AwsMachinesConfig`, `MachinesModuleConfig` - -**`apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx`** -- Shared form component used by both add and edit dialogs -- Fields: ID (optional), display name, host, SSH (user/port/key), public/private IP, architecture (select), provider (select), environment (select), roles (comma-separated), tags (comma-separated) -- Grouped into logical fieldsets (Connection, Networking, Classification) - -**`apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx`** -- Dialog with "Add Machine" button that appears in the panel header -- Validates machine ID (lowercase + hyphens), checks for duplicates -- Writes to Nix config via `machines.addMachine()` - -**`apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx`** -- Opens when clicking a machine card or pencil icon -- Full edit form + destructive "Delete" button -- Saves via `machines.updateMachine()` / `machines.removeMachine()` - -## Updated Files - -**`apps/web/src/components/studio/panels/deploy/deploy-panel.tsx`** -- Rewritten to integrate: -- **Machines tab**: Cards are now clickable to edit, show pencil icon, merge computed + config machines -- **Header**: "Add Machine" button next to Refresh -- **Settings tab**: New sections: - - Machines module enable/disable toggle - - Source selector (Static vs AWS EC2) - - Full AWS EC2 discovery config (region, host preference, default SSH) shown when source is `aws-ec2` - - Colmena hive config display -- **Edit dialog**: Wired up with state management, opens on card click - -## Data Flow - -UI form -> `useMachinesConfig()` hook -> `useNixData("infra").mutate()` -> `POST /api/nix/data { entity: "infra", data }` -> Go agent writes `.stack/config.nix` -> SSE `config.changed` event -> UI auto-refreshes - ---- - -## User - -how do i provision the machine as well? - ---- - -## Assistant (Build · claude-opus-4-6 · 175.2s) - -_Thinking:_ - -The user wants to provision machines (create EC2 instances, not just discover existing ones) from the Studio UI. Currently the machines module only supports "static" (manually defined) and "aws-ec2" (discover existing instances). There's no provisioning path. - -Let me look at the existing AWS EC2 infra module to understand what's already there for actually creating EC2 instances. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore EC2 provisioning infra modules", - "prompt": "Search thoroughly for everything related to EC2 instance provisioning (creating instances, not just discovering them):\n\n1. Read nix/stack/infra/modules/aws-ec2/module.nix - understand how EC2 instances are provisioned\n2. Read nix/stack/infra/modules/aws-ec2/index.ts - the Alchemy module that creates EC2 instances\n3. Read nix/stack/infra/modules/aws-ec2-app/module.nix - per-app EC2 resources\n4. Read nix/stack/infra/modules/aws-network/module.nix - VPC/subnet provisioning\n5. Read nix/stack/infra/modules/aws-security-groups/module.nix - security groups\n6. Read nix/stack/infra/modules/aws-key-pairs/module.nix - SSH key pairs\n7. Read nix/stack/infra/modules/aws-iam/module.nix - IAM for EC2\n8. Check what custom Alchemy resources exist for EC2 in nix/stack/infra/templates/ (ec2-instance.ts, etc.)\n9. Check .stack/config.nix for any existing aws-ec2/aws-network config sections\n\nReturn for each module:\n- Whether it exists and what it provisions\n- The Nix options it defines (option names, types, defaults)\n- How it relates to other modules (dependencies)\n- The current config values in .stack/config.nix if any\n- Key line numbers for important sections", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have complete information. Here is the comprehensive report. - ---- - -# EC2 Instance Provisioning -- Complete Analysis - -## Overview - -The EC2 provisioning system is a layered architecture with **7 Nix modules**, **13 custom Alchemy resources** (TypeScript), and a **codegen engine** that wires them together into a deployable `packages/infra` workspace. There are two approaches to EC2 provisioning: a low-level `aws-ec2` module and a high-level `aws-ec2-app` module. - ---- - -## 1. aws-ec2 (Low-Level EC2 Provisioning) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix` -**Exists: YES (207 lines)** -**What it provisions:** Raw EC2 instances from explicit definitions. No automatic VPC/subnet/AMI resolution. - -**Nix Options** (under `options.stack.infra.aws-ec2`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 145-148 | -| `instances` | `listOf instanceType` | `[]` | 151-154 | -| `defaults` | `instanceType` | `{name="default"; ami=""; instance-type="t3.micro"; subnet-id="";}` | 157-166 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines"]` | 168-172 | - -**instanceType submodule** (lines 70-141): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `name` | `str` | *(required)* | 72-75 | -| `ami` | `str` | *(required)* | 77-80 | -| `instance-type` | `str` | `"t3.micro"` | 82-86 | -| `subnet-id` | `str` | *(required)* | 88-91 | -| `security-group-ids` | `listOf str` | `[]` | 93-97 | -| `key-name` | `nullOr str` | `null` | 99-103 | -| `iam-instance-profile` | `nullOr str` | `null` | 105-109 | -| `user-data` | `nullOr str` | `null` | 111-115 | -| `root-volume-size` | `nullOr int` | `null` | 117-121 | -| `associate-public-ip` | `bool` | `true` | 123-127 | -| `tags` | `attrsOf str` | `{}` | 129-133 | -| `machine` | `machineMetaType` | `{}` | 135-139 | - -**machineMetaType** (lines 36-68): `tags` (listOf str), `roles` (listOf str), `target-env` (nullOr str), `arch` (nullOr str), `ssh` (submodule: `user`=root, `port`=22, `key-path`=null) - -**Dependencies:** Registers into `stack.infra.modules.aws-ec2` (line 178). Requires `@aws-sdk/client-ec2`. Auto-enables `stack.infra.enable` (line 176). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines` -- all JSON strings. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/index.ts` -**119 lines.** Simple loop: merges `defaults` into each `instance`, calls `Ec2Instance()` Alchemy resource, collects outputs. Emits machine inventory with `provider: "aws-ec2"`. - -**Key lines:** Instance creation at line 67-79. Machine inventory build at 88-109. Export at 112-118. - ---- - -## 2. aws-ec2-app (High-Level App-Centric EC2) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix` -**Exists: YES (918 lines)** -**What it provisions:** Per-app EC2 infrastructure including: security groups, key pairs, IAM roles + instance profiles, ALBs + target groups + listeners + host-based routing, ECR repos + GitHub OIDC roles, SSM parameters, and EC2 instances with automatic AMI resolution (Ubuntu 24.04 or NixOS). - -**Top-level options** (under `options.stack.infra.aws-ec2-app`, lines 852-876): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 853-856 | -| `defaults` | `appType` | `{}` | 859-863 | -| `apps` | `attrsOf appType` | `{}` | 865-869 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines" "albOutputs" "ecrOutputs" "ssmOutputs"]` | 871-875 | - -**appType submodule** (lines 715-849, ~25 options): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `instance-count` | `int` | `1` | 717-720 | -| `instances` | `listOf instanceType` | `[]` | 722-726 | -| `ami` | `nullOr str` | `null` | 728-733 | -| `os-type` | `enum ["ubuntu" "nixos"]` | `"ubuntu"` | 735-739 | -| `nixos` | `nixosType` | `{}` | 741-745 | -| `instance-type` | `nullOr str` | `null` | 747-751 | -| `vpc-id` | `nullOr str` | `null` | 753-757 | -| `subnet-ids` | `listOf str` | `[]` | 759-763 | -| `security-group-ids` | `listOf str` | `[]` | 765-769 | -| `security-group` | `securityGroupType` | `{}` | 771-775 | -| `key-name` | `nullOr str` | `null` | 777-781 | -| `key-pair` | `keyPairType` | `{}` | 783-787 | -| `iam` | `iamType` | `{}` | 789-793 | -| `iam-instance-profile` | `nullOr str` | `null` | 795-799 | -| `user-data` | `nullOr str` | `null` | 801-805 | -| `root-volume-size` | `nullOr int` | `null` | 807-811 | -| `associate-public-ip` | `bool` | `true` | 813-816 | -| `tags` | `attrsOf str` | `{}` | 819-823 | -| `alb` | `albType` | `{}` | 825-829 | -| `ecr` | `ecrType` | `{}` | 831-835 | -| `ssm` | `ssmType` | `{}` | 837-841 | -| `machine` | `machineMetaType` | `{}` | 843-847 | - -**Major nested submodule types:** - -- **securityGroupType** (lines 80-118): `create`, `name`, `description`, `ingress`/`egress` (listOf ruleType), `tags` -- **ruleType** (lines 36-78): `from-port`, `to-port`, `protocol`, `cidr-blocks`, `ipv6-cidr-blocks`, `security-group-ids`, `description` -- **keyPairType** (lines 120-152): `create`, `name`, `public-key`, `tags`, `destroy-on-delete` -- **iamType** (lines 168-221): `enable`, `role-name`, `assume-role-policy`, `managed-policy-arns` (defaults include SSM + ECR policies), `inline-policies`, `tags`, `instance-profile-name`, `instance-profile-tags` -- **nixosType** (lines 223-249): `ami-id`, `flake-url`, `host-config`, `flake-version` -- **albType** (lines 331-429): `enable`, `create`, `name`, `scheme`, `ip-address-type`, `subnet-ids`, `security-group-ids`, `http`, `https`, `certificate-arn`, `ssl-policy`, `existing-listener-http-arn`, `existing-listener-https-arn`, `hostnames`, `host-rule-priority`, `target-group` -- **albTargetGroupType** (lines 309-329): `port`, `protocol`, `health-check` -- **albHealthCheckType** (lines 251-307): `enabled`, `path`, `protocol`, `port`, `interval`, `timeout`, `healthy-threshold`, `unhealthy-threshold`, `matcher` -- **ecrType** (lines 489-533): `enable`, `create`, `repo-name`, `image-tag-mutability`, `scan-on-push`, `lifecycle-policy`, `github` -- **githubOidcType** (lines 431-487): `enable`, `repo-owner`, `repo-name`, `allowed-branches`, `allowed-workflows`, `allow-tags`, `role-name`, `oidc-provider-arn`, `create-oidc-provider` -- **ssmType** (lines 535-591): `enable`, `region`, `path-prefix`, `parameters`, `secure-parameters`, `env-file-path`, `refresh-script-path`, `install-cli`, `use-chamber` -- **instanceType** (per-instance override, lines 627-713): `name`, `ami`, `os-type`, `nixos`, `instance-type`, `subnet-id`, `security-group-ids`, `key-name`, `iam-instance-profile`, `user-data`, `root-volume-size`, `associate-public-ip`, `tags`, `machine` - -**Dependencies:** `@aws-sdk/client-ec2`, `@aws-sdk/client-iam`, `@aws-sdk/client-elastic-load-balancing-v2`, `@aws-sdk/client-ecr`, `@aws-sdk/client-ssm`, `@aws-sdk/client-sts` (lines 890-896). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines`, `albOutputs`, `ecrOutputs`, `ssmOutputs` (lines 906-914). - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/index.ts` -**962 lines.** The most complex module. Key logic: - -- **Default VPC resolution** (lines 233-255): Auto-discovers default VPC and subnets via DescribeVpcs/DescribeSubnets -- **AMI resolution** (lines 297-342): Auto-resolves Ubuntu 24.04 (Canonical, owner `099720109477`) or NixOS (Determinate Systems, owner `535002876703`, `determinate/nixos/epoch-1/*`) -- **NixOS user data** (lines 344-358): Generates bootstrap script using `fh apply nixos` with FlakeHub URL -- **Per-app loop** (lines 391-946): - 1. Security group creation (lines 406-433) - 2. Key pair import (lines 435-450) - 3. IAM role + instance profile with EC2 trust policy (lines 452-488) - 4. SSM parameter creation (lines 490-523) - 5. ECR repo + GitHub OIDC role for CI push (lines 525-698) - 6. EC2 instance creation with round-robin subnet assignment (lines 700-797) - 7. ALB + target group + listeners + host-based routing (lines 799-945) - ---- - -## 3. aws-network (VPC/Subnet Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix` -**Exists: YES (103 lines)** -**What it provisions:** Nothing -- it *discovers* the default VPC and subnets. This is a read-only module. - -**Nix Options** (under `options.stack.infra.aws-network`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 19-23 | -| `region` | `nullOr str` | `config.stack.aws.roles-anywhere.region or null` | 25-29 | -| `vpc.id` | `nullOr str` | `null` | 32-36 | -| `vpc.use-default` | `bool` | `true` | 38-42 | -| `subnets.ids` | `listOf str` | `[]` | 46-49 | -| `subnets.use-default` | `bool` | `true` | 52-56 | -| `sync-outputs` | `listOf str` | `["vpcId" "subnetIds"]` | 59-63 | - -**Outputs:** `vpcId` (string), `subnetIds` (JSON array), `subnetAzs` (JSON array). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Standalone network discovery. Not required by `aws-ec2-app` which has its own built-in VPC discovery (lines 233-255 of index.ts), but useful for `aws-security-groups` and `aws-ec2` which require explicit VPC/subnet IDs. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/index.ts` -**66 lines.** Simple: discovers default VPC, then subnets in that VPC. Falls back to explicit inputs. - ---- - -## 4. aws-security-groups - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix` -**Exists: YES (145 lines)** -**What it provisions:** Standalone security groups in a given VPC. - -**Nix Options** (under `options.stack.infra.aws-security-groups`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 93-97 | -| `vpc-id` | `str` | *(required)* | 99-102 | -| `groups` | `listOf groupType` | `[]` | 104-108 | -| `sync-outputs` | `listOf str` | `["groupIds"]` | 110-114 | - -**groupType**: `name` (str, required), `description` (nullOr str), `ingress`/`egress` (listOf ruleType), `tags` (attrsOf str). - -**Outputs:** `groupIds` (JSON: `{groupName: groupId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent module. Output group IDs can be manually referenced in `aws-ec2.instances[].security-group-ids`. Not automatically wired to aws-ec2 or aws-ec2-app. The `aws-ec2-app` module has its own inline security group creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/index.ts` -**69 lines.** Loops over groups, creates each via `SecurityGroup()` resource. - ---- - -## 5. aws-key-pairs - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix` -**Exists: YES (84 lines)** -**What it provisions:** EC2 SSH key pairs (imports public keys). - -**Nix Options** (under `options.stack.infra.aws-key-pairs`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 37-40 | -| `keys` | `attrsOf keyType` | `{}` | 43-47 | -| `sync-outputs` | `listOf str` | `["keyNames" "keyPairIds"]` | 49-53 | - -**keyType**: `public-key` (str, required), `tags` (attrsOf str), `destroy-on-delete` (bool, default false). - -**Outputs:** `keyNames` (JSON array), `keyPairIds` (JSON: `{keyName: keyPairId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent. Output key names can be referenced in `aws-ec2.instances[].key-name`. The `aws-ec2-app` module has its own inline key pair creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/index.ts` -**42 lines.** Loops over keys, imports each via `KeyPair()` resource. - ---- - -## 6. aws-iam (EC2-Oriented IAM) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix` -**Exists: YES (128 lines)** -**What it provisions:** A single IAM role with EC2 trust policy + instance profile. - -**Nix Options** (under `options.stack.infra.aws-iam`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 30-33 | -| `role.name` | `str` | *(required)* | 37-39 | -| `role.assume-role-policy` | `nullOr (attrsOf anything)` | `null` (defaults to EC2 trust) | 42-46 | -| `role.managed-policy-arns` | `listOf str` | `[]` | 48-52 | -| `role.inline-policies` | `listOf inlinePolicyType` | `[]` | 54-58 | -| `role.tags` | `attrsOf str` | `{}` | 60-64 | -| `instance-profile.name` | `nullOr str` | `null` (defaults to `-profile`) | 68-72 | -| `instance-profile.tags` | `attrsOf str` | `{}` | 74-78 | -| `sync-outputs` | `listOf str` | `["roleArn" "roleName" "instanceProfileArn" "instanceProfileName"]` | 81-85 | - -**Outputs:** `roleArn`, `roleName`, `instanceProfileArn`, `instanceProfileName`. - -**Dependencies:** `@aws-sdk/client-iam`. - -**Relationship:** Independent standalone module. Output `instanceProfileName` can be referenced in `aws-ec2.instances[].iam-instance-profile`. The `aws-ec2-app` module has its own inline IAM creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/index.ts` -**71 lines.** Creates role with EC2 trust policy (line 38), attaches managed policies, creates instance profile. - ---- - -## 7. machines (Machine Inventory Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` -**Exists: YES (307 lines)** -**What it provisions:** Nothing -- it *discovers* existing EC2 instances and emits a machine inventory JSON for Colmena. - -**Nix Options** (under `options.stack.infra.machines`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 157-161 | -| `source` | `enum ["static" "aws-ec2"]` | `"static"` | 163-169 | -| `aws.region` | `nullOr str` | inherited from `aws.roles-anywhere.region` | 173-177 | -| `aws.instance-ids` | `listOf str` | `[]` | 179-183 | -| `aws.filters` | `listOf awsFilterType` | `[{name="instance-state-name"; values=["running"]}]` | 185-194 | -| `aws.name-tag-keys` | `listOf str` | `["Name"]` | 196-200 | -| `aws.role-tag-keys` | `listOf str` | `["stack:role" "role"]` | 202-209 | -| `aws.tag-keys` | `listOf str` | `["stack:tag" "tag"]` | 211-218 | -| `aws.env-tag-keys` | `listOf str` | `["stack:env" "env" "stage"]` | 220-228 | -| `aws.host-preference` | `listOf enum` | `["publicDns" "publicIp" "privateIp"]` | 230-242 | -| `aws.ssh` | `sshConfigType` | `{user="root"; port=22; keyPath=null}` | 244-248 | -| `machines` | `attrsOf machineType` | `{}` | 251-255 | - -**Outputs:** `machines` (JSON string of machine inventory). - -**Dependencies:** `@aws-sdk/client-ec2` (only when `source == "aws-ec2"`). - -**Relationship:** This is a *discovery* module, not a provisioning module. Its output feeds into Colmena deployment (see below). - ---- - -## 8. Custom Alchemy Resources (Templates) - -All in `/Users/cm/git/darkmatter/stack/nix/stack/infra/templates/`: - -| File | Resource Type | Lines | What it does | -|------|-------------|-------|-------------| -| `ec2-instance.ts` | `stack::Ec2Instance` | 145 | `RunInstancesCommand` to create, `TerminateInstancesCommand` to delete. gp3 volumes. Base64 user data encoding. | -| `security-group.ts` | `stack::SecurityGroup` | 182 | Creates SG, authorizes ingress/egress rules. Adopts existing SGs by name+VPC. | -| `key-pair.ts` | `stack::KeyPair` | 101 | Imports public key via `ImportKeyPairCommand`. Adopts existing by name. | -| `iam-role.ts` | `stack::IamRole` | 153 | Creates/adopts role, updates trust policy, attaches managed + inline policies. | -| `iam-instance-profile.ts` | `stack::IamInstanceProfile` | 137 | Creates/adopts profile, adds role. Full lifecycle (remove role + delete on destroy). | -| `application-load-balancer.ts` | `stack::ApplicationLoadBalancer` | 121 | Creates ALB, returns ARN + DNS + zone ID. | -| `target-group.ts` | `stack::TargetGroup` | 136 | Creates target group with health checks. | -| `listener.ts` | `stack::Listener` | 108 | Creates HTTP/HTTPS listener with SSL policy. | -| `listener-rule.ts` | `stack::ListenerRule` | 105 | Creates host-header routing rules. | -| `target-group-attachment.ts` | `stack::TargetGroupAttachment` | 63 | Registers/deregisters instance to target group. | -| `ecr-repository.ts` | `stack::EcrRepository` | 127 | Creates ECR repo with lifecycle policy. | - -All resources follow the Alchemy `Resource()` pattern with create-or-adopt semantics and `this.phase === "delete"` handling. They are **read at Nix eval time** and written to `packages/infra/src/resources/` by the codegen engine (`codegen.nix`, lines 561-602). - ---- - -## 9. Current config.nix Values - -### File: `/Users/cm/git/darkmatter/stack/.stack/config.nix` - -**Lines 447-475** -- the `infra` section: - -```nix -infra = { - enable = true; # line 448 - storage-backend = { - type = "sops"; # line 450 - sops.group = "dev"; # line 451 - }; - database = { # line 453 - enable = true; - provider = "neon"; - neon = { - region = "aws-us-east-1"; - api-key-ssm-path = "/common/neon-api-key"; - }; - }; - machines = { # line 461 - enable = true; - source = "aws-ec2"; # Discovery mode, not provisioning - aws = { - region = "us-west-2"; - filters = [{ - name = "instance-state-name"; - values = [ "running" ]; - }]; - }; - machines = { }; - }; -}; -``` - -**Key observations about current config:** -- **`infra.enable = true`** -- the infra system is active -- **`machines.enable = true` with `source = "aws-ec2"`** -- EC2 instance *discovery* is enabled (not provisioning) -- **No `aws-ec2` or `aws-ec2-app` sections** -- EC2 provisioning is NOT currently configured -- **No `aws-network` section** -- VPC discovery is not independently enabled -- **No `aws-security-groups` section** -- standalone SG provisioning is not enabled -- **No `aws-key-pairs` section** -- standalone key pair provisioning is not enabled -- **No `aws-iam` section** -- standalone IAM provisioning is not enabled -- Storage backend is SOPS (dev group) -- AWS region from `aws.roles-anywhere.region` = `"us-west-2"` (line 149) - ---- - -## 10. Module Dependency Graph - -``` - ┌─────────────────┐ - │ config.nix │ - │ (infra section) │ - └────────┬────────┘ - │ - ┌────────────┼────────────┐ - │ │ │ - ┌─────▼─────┐ ┌───▼────┐ ┌────▼─────┐ - │ aws-ec2 │ │aws-ec2 │ │ machines │ (discovery) - │ -app │ │(basic) │ │ │ - └─────┬─────┘ └───┬────┘ └──────────┘ - │ │ - ┌───────┬───────┼───────┬───┤ - │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ - SG KeyPair IAM EC2 ALB+TG+Listener+ECR+SSM - (inline) (inline)(inline)(Ec2Instance resource) - │ │ │ │ - ▼ ▼ ▼ ▼ - Alchemy Resources (templates/*.ts) -``` - -**Two provisioning paths:** -1. **Composable (low-level):** `aws-network` -> `aws-security-groups` -> `aws-key-pairs` -> `aws-iam` -> `aws-ec2` (each module independent, manually wire outputs) -2. **All-in-one (high-level):** `aws-ec2-app` (creates SG, key pairs, IAM, ALB, ECR, SSM, and EC2 instances all in one module) - -**Consumer:** The Colmena module (`nix/stack/modules/colmena/module.nix`, lines 577-580) reads machine inventory from `infra.outputs."aws-ec2".machines` OR `infra.outputs."aws-ec2-app".machines` OR `infra.outputs.machines.machines` (the discovery module). - ---- - -## 11. Codegen Wiring - -The codegen engine (`/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix`): -- **Line 225**: Reads `ec2-instance.ts` template -- **Lines 561-566**: Writes it to `${outputDir}/src/resources/ec2-instance.ts` -- **Lines 462-489**: Copies each module's `index.ts` to `${outputDir}/modules/.ts` -- **Lines 261-340**: Generates `alchemy.run.ts` orchestrator that imports all enabled modules -- **Lines 345-348**: Merges all module dependencies into the package.json - -The infra module system entry point is `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` (line 34-35) which imports both `aws-ec2/module.nix` and `aws-ec2-app/module.nix`. - - -session_id: ses_3565ab3f8ffehVv05KuQ9ZqSdj - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.9s) - -You already have a complete EC2 provisioning pipeline built out -- two of them actually: - -1. **`aws-ec2`** -- Low-level: you define instances with explicit AMI, subnet, security groups -2. **`aws-ec2-app`** -- High-level: per-app EC2 with auto-AMI resolution, inline SG/IAM/ALB/ECR creation - -Neither is wired into the Studio UI yet. The Deploy panel currently only manages machine *inventory* (static + discovery), not provisioning. - -Let me add EC2 provisioning configuration to the Settings tab so you can define instances and deploy them from the UI. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { useMemo } from "react"; -00004| import { useNixData } from "@/lib/use-agent"; -00005| -00006| // ============================================================================= -00007| // Types — match the Nix module schema (nix/stack/infra/modules/machines) -00008| // ============================================================================= -00009| -00010| export interface MachineSSH { -00011| user: string; -00012| port: number; -00013| key_path: string | null; -00014| } -00015| -00016| export interface MachineConfig { -00017| id?: string | null; -00018| name: string | null; -00019| host: string | null; -00020| ssh: MachineSSH; -00021| tags: string[]; -00022| roles: string[]; -00023| provider: string | null; -00024| arch: string | null; -00025| public_ip: string | null; -00026| private_ip: string | null; -00027| target_env: string | null; -00028| labels: Record; -00029| nixos_profile: string | null; -00030| nixos_modules: string[]; -00031| env: Record; -00032| metadata: Record; -00033| } -00034| -00035| export interface AwsFilter { -00036| name: string; -00037| values: string[]; -00038| } -00039| -00040| export interface AwsMachinesConfig { -00041| region: string | null; -00042| instance_ids: string[]; -00043| filters: AwsFilter[]; -00044| name_tag_keys: string[]; -00045| role_tag_keys: string[]; -00046| tag_keys: string[]; -00047| env_tag_keys: string[]; -00048| host_preference: string[]; -00049| ssh: MachineSSH; -00050| } -00051| -00052| export interface MachinesModuleConfig { -00053| enable: boolean; -00054| source: "static" | "aws-ec2"; -00055| aws: AwsMachinesConfig; -00056| machines: Record; -00057| } -00058| -00059| // ============================================================================= -00060| // Defaults -00061| // ============================================================================= -00062| -00063| export const DEFAULT_SSH: MachineSSH = { -00064| user: "root", -00065| port: 22, -00066| key_path: null, -00067| }; -00068| -00069| export const DEFAULT_MACHINE: MachineConfig = { -00070| id: null, -00071| name: null, -00072| host: null, -00073| ssh: { ...DEFAULT_SSH }, -00074| tags: [], -00075| roles: [], -00076| provider: null, -00077| arch: null, -00078| public_ip: null, -00079| private_ip: null, -00080| target_env: null, -00081| labels: {}, -00082| nixos_profile: null, -00083| nixos_modules: [], -00084| env: {}, -00085| metadata: {}, -00086| }; -00087| -00088| const DEFAULT_AWS_CONFIG: AwsMachinesConfig = { -00089| region: null, -00090| instance_ids: [], -00091| filters: [{ name: "instance-state-name", values: ["running"] }], -00092| name_tag_keys: ["Name"], -00093| role_tag_keys: ["stack:role", "role"], -00094| tag_keys: ["stack:tag", "tag"], -00095| env_tag_keys: ["stack:env", "env", "stage"], -00096| host_preference: ["publicDns", "publicIp", "privateIp"], -00097| ssh: { ...DEFAULT_SSH }, -00098| }; -00099| -00100| const DEFAULT_CONFIG: MachinesModuleConfig = { -00101| enable: false, -00102| source: "static", -00103| aws: DEFAULT_AWS_CONFIG, -00104| machines: {}, -00105| }; -00106| -00107| // ============================================================================= -00108| // Hook -00109| // ============================================================================= -00110| -00111| export function useMachinesConfig() { -00112| const { data: rawInfra, mutate: setInfra, refetch } = useNixData>("infra"); -00113| -00114| const config = useMemo(() => { -00115| if (!rawInfra) return DEFAULT_CONFIG; -00116| const machines = (rawInfra as Record).machines as Partial | undefined; -00117| if (!machines) return DEFAULT_CONFIG; -00118| return { -00119| enable: machines.enable ?? false, -00120| source: machines.source ?? "static", -00121| aws: { -00122| ...DEFAULT_AWS_CONFIG, -00123| ...(machines.aws ?? {}), -00124| }, -00125| machines: (machines.machines ?? {}) as Record, -00126| }; -00127| }, [rawInfra]); -00128| -00129| const saveConfig = async (newConfig: MachinesModuleConfig) => { -00130| const currentInfra = (rawInfra ?? {}) as Record; -00131| await setInfra({ -00132| ...currentInfra, -00133| machines: newConfig, -00134| } as any); -00135| }; -00136| -00137| const addMachine = async (key: string, machine: MachineConfig) => { -00138| const updated = { ...config }; -00139| updated.machines = { ...updated.machines, [key]: machine }; -00140| if (!updated.enable) updated.enable = true; -00141| await saveConfig(updated); -00142| }; -00143| -00144| const updateMachine = async (key: string, machine: MachineConfig) => { -00145| const updated = { ...config }; -00146| updated.machines = { ...updated.machines, [key]: machine }; -00147| await saveConfig(updated); -00148| }; -00149| -00150| const removeMachine = async (key: string) => { -00151| const updated = { ...config }; -00152| const { [key]: _, ...rest } = updated.machines; -00153| updated.machines = rest; -00154| await saveConfig(updated); -00155| }; -00156| -00157| const updateSettings = async (settings: Partial) => { -00158| await saveConfig({ ...config, ...settings }); -00159| }; -00160| -00161| return { -00162| config, -00163| addMachine, -00164| updateMachine, -00165| removeMachine, -00166| updateSettings, -00167| refetch, -00168| }; -00169| } -00170| - -(End of file - total 170 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.1s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "// =============================================================================\n// EC2 Provisioning Types — match aws-ec2-app Nix module\n// =============================================================================\n\nexport interface Ec2MachineMeta {\n\ttags: string[];\n\troles: string[];\n\ttarget_env: string | null;\n\tarch: string | null;\n\tssh: MachineSSH;\n}\n\nexport interface Ec2SecurityGroupRule {\n\tfrom_port: number;\n\tto_port: number;\n\tprotocol: string;\n\tcidr_blocks: string[];\n\tdescription: string;\n}\n\nexport interface Ec2SecurityGroup {\n\tcreate: boolean;\n\tname: string | null;\n\tdescription: string | null;\n\tingress: Ec2SecurityGroupRule[];\n\tegress: Ec2SecurityGroupRule[];\n}\n\nexport interface Ec2KeyPair {\n\tcreate: boolean;\n\tname: string | null;\n\tpublic_key: string | null;\n}\n\nexport interface Ec2IamConfig {\n\tenable: boolean;\n\trole_name: string | null;\n}\n\nexport interface Ec2AppInstance {\n\tname: string;\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tsubnet_id: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppConfig {\n\tinstance_count: number;\n\tinstances: Ec2AppInstance[];\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tvpc_id: string | null;\n\tsubnet_ids: string[];\n\tsecurity_group_ids: string[];\n\tsecurity_group: Ec2SecurityGroup;\n\tkey_name: string | null;\n\tkey_pair: Ec2KeyPair;\n\tiam: Ec2IamConfig;\n\tuser_data: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppModuleConfig {\n\tenable: boolean;\n\tdefaults: Partial;\n\tapps: Record;\n}\n\nexport const DEFAULT_EC2_MACHINE_META: Ec2MachineMeta = {\n\ttags: [],\n\troles: [],\n\ttarget_env: null,\n\tarch: null,\n\tssh: { ...DEFAULT_SSH },\n};\n\nexport const DEFAULT_EC2_APP: Ec2AppConfig = {\n\tinstance_count: 1,\n\tinstances: [],\n\tami: null,\n\tos_type: \"ubuntu\",\n\tinstance_type: \"t3.micro\",\n\tvpc_id: null,\n\tsubnet_ids: [],\n\tsecurity_group_ids: [],\n\tsecurity_group: {\n\t\tcreate: true,\n\t\tname: null,\n\t\tdescription: null,\n\t\tingress: [\n\t\t\t{ from_port: 22, to_port: 22, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"SSH\" },\n\t\t\t{ from_port: 80, to_port: 80, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTP\" },\n\t\t\t{ from_port: 443, to_port: 443, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTPS\" },\n\t\t],\n\t\tegress: [\n\t\t\t{ from_port: 0, to_port: 0, protocol: \"-1\", cidr_blocks: [\"0.0.0.0/0\"], description: \"All outbound\" },\n\t\t],\n\t},\n\tkey_name: null,\n\tkey_pair: { create: false, name: null, public_key: null },\n\tiam: { enable: true, role_name: null },\n\tuser_data: null,\n\troot_volume_size: null,\n\tassociate_public_ip: true,\n\ttags: {},\n\tmachine: { ...DEFAULT_EC2_MACHINE_META },\n};\n\nconst DEFAULT_EC2_MODULE: Ec2AppModuleConfig = {\n\tenable: false,\n\tdefaults: {},\n\tapps: {},\n};\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [224:7] Variable 'DEFAULT_EC2_MODULE' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now add the provisioning hook alongside the machines hook: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}", - "newString": "\treturn {\n\t\tconfig,\n\t\trawInfra,\n\t\tsetInfra,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n\nexport function useEc2Provisioning() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_EC2_MODULE;\n\t\tconst ec2App = (rawInfra as Record)[\"aws_ec2_app\"] as Partial | undefined;\n\t\tif (!ec2App) return DEFAULT_EC2_MODULE;\n\t\treturn {\n\t\t\tenable: ec2App.enable ?? false,\n\t\t\tdefaults: ec2App.defaults ?? {},\n\t\t\tapps: (ec2App.apps ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: Ec2AppModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\t\"aws_ec2_app\": newConfig,\n\t\t} as any);\n\t};\n\n\tconst addApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeApp = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.apps;\n\t\tupdated.apps = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst setEnabled = async (enabled: boolean) => {\n\t\tawait saveConfig({ ...config, enable: enabled });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddApp,\n\t\tupdateApp,\n\t\tremoveApp,\n\t\tsetEnabled,\n\t\trefetch,\n\t};\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 45.9s) - -Now create the provisioning UI components: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n\ttype Ec2AppConfig,\n\tDEFAULT_EC2_APP,\n\tDEFAULT_EC2_MACHINE_META,\n\ttype useEc2Provisioning,\n} from \"./use-machines\";\n\ninterface ProvisionInstanceDialogProps {\n\tec2: ReturnType;\n}\n\nconst INSTANCE_TYPES = [\n\t\"t3.micro\",\n\t\"t3.small\",\n\t\"t3.medium\",\n\t\"t3.large\",\n\t\"t3.xlarge\",\n\t\"t3.2xlarge\",\n\t\"m6i.large\",\n\t\"m6i.xlarge\",\n\t\"m6i.2xlarge\",\n\t\"c6i.large\",\n\t\"c6i.xlarge\",\n\t\"c6i.2xlarge\",\n\t\"r6i.large\",\n\t\"r6i.xlarge\",\n];\n\nconst AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\n\t// Form state\n\tconst [appId, setAppId] = useState(\"\");\n\tconst [instanceCount, setInstanceCount] = useState(1);\n\tconst [instanceType, setInstanceType] = useState(\"t3.micro\");\n\tconst [osType, setOsType] = useState<\"ubuntu\" | \"nixos\">(\"ubuntu\");\n\tconst [vpcId, setVpcId] = useState(\"\");\n\tconst [subnetIds, setSubnetIds] = useState(\"\");\n\tconst [rootVolumeSize, setRootVolumeSize] = useState(\"\");\n\tconst [associatePublicIp, setAssociatePublicIp] = useState(true);\n\tconst [createSg, setCreateSg] = useState(true);\n\tconst [createKeyPair, setCreateKeyPair] = useState(false);\n\tconst [publicKey, setPublicKey] = useState(\"\");\n\tconst [enableIam, setEnableIam] = useState(true);\n\tconst [roles, setRoles] = useState(\"\");\n\tconst [tags, setTags] = useState(\"\");\n\tconst [targetEnv, setTargetEnv] = useState(\"\");\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetAppId(\"\");\n\t\t\tsetInstanceCount(1);\n\t\t\tsetInstanceType(\"t3.micro\");\n\t\t\tsetOsType(\"ubuntu\");\n\t\t\tsetVpcId(\"\");\n\t\t\tsetSubnetIds(\"\");\n\t\t\tsetRootVolumeSize(\"\");\n\t\t\tsetAssociatePublicIp(true);\n\t\t\tsetCreateSg(true);\n\t\t\tsetCreateKeyPair(false);\n\t\t\tsetPublicKey(\"\");\n\t\t\tsetEnableIam(true);\n\t\t\tsetRoles(\"\");\n\t\t\tsetTags(\"\");\n\t\t\tsetTargetEnv(\"\");\n\t\t}\n\t};\n\n\tconst isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = appId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"App ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (ec2.config.apps[id]) {\n\t\t\ttoast.error(`EC2 app \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst appConfig: Ec2AppConfig = {\n\t\t\t\t...DEFAULT_EC2_APP,\n\t\t\t\tinstance_count: instanceCount,\n\t\t\t\tinstance_type: instanceType,\n\t\t\t\tos_type: osType,\n\t\t\t\tvpc_id: vpcId || null,\n\t\t\t\tsubnet_ids: subnetIds\n\t\t\t\t\t? subnetIds.split(\",\").map((s) => s.trim()).filter(Boolean)\n\t\t\t\t\t: [],\n\t\t\t\troot_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null,\n\t\t\t\tassociate_public_ip: associatePublicIp,\n\t\t\t\tsecurity_group: {\n\t\t\t\t\t...DEFAULT_EC2_APP.security_group,\n\t\t\t\t\tcreate: createSg,\n\t\t\t\t},\n\t\t\t\tkey_pair: {\n\t\t\t\t\tcreate: createKeyPair,\n\t\t\t\t\tname: createKeyPair ? `${id}-key` : null,\n\t\t\t\t\tpublic_key: createKeyPair ? publicKey || null : null,\n\t\t\t\t},\n\t\t\t\tiam: {\n\t\t\t\t\tenable: enableIam,\n\t\t\t\t\trole_name: enableIam ? `${id}-ec2-role` : null,\n\t\t\t\t},\n\t\t\t\ttags: {\n\t\t\t\t\tName: id,\n\t\t\t\t\tManagedBy: \"stack-infra\",\n\t\t\t\t},\n\t\t\t\tmachine: {\n\t\t\t\t\t...DEFAULT_EC2_MACHINE_META,\n\t\t\t\t\troles: roles ? roles.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttags: tags ? tags.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttarget_env: targetEnv || null,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait ec2.addApp(id, appConfig);\n\t\t\ttoast.success(`EC2 app \"${id}\" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add EC2 app\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tProvision EC2 Instances\n\t\t\t\t\t\n\t\t\t\t\t\tDefine EC2 instances to provision. Run infra:deploy after to create them.\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t{/* Identity */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setAppId(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tUnique identifier for this group of instances\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Instance sizing */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setInstanceCount(Number.parseInt(e.target.value, 10) || 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Networking */}\n\t\t\t\t\t
\n\t\t\t\t\t\tNetworking\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setVpcId(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setSubnetIds(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Associate a public IPv4 address

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Auto-create with SSH, HTTP, HTTPS ingress

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Storage */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRootVolumeSize(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* SSH Key */}\n\t\t\t\t\t
\n\t\t\t\t\t\tSSH Key Pair\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Import your public key to AWS

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{createKeyPair && (\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setPublicKey(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* IAM */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Create role with SSM + ECR access

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Machine metadata */}\n\t\t\t\t\t
\n\t\t\t\t\t\tMachine Metadata\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setRoles(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setTags(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [53:7] Variable 'AWS_REGIONS' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "const AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog", - "newString": "export function ProvisionInstanceDialog" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.6s) - -Now add the Provision tab to the deploy panel: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 1, - "limit": 15 -} -``` -**Output:** -``` - -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and -00008| * AWS EC2 discovery configuration. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, - -(File has more lines. Use 'offset' parameter to read beyond line 16) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";", - "newString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { ProvisionInstanceDialog } from \"./provision-instance-dialog\";\nimport {\n\tuseMachinesConfig,\n\tuseEc2Provisioning,\n\ttype MachineConfig,\n\ttype Ec2AppConfig,\n} from \"./use-machines\";" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [62:7] Type 'Ec2AppConfig' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.7s) - -Now integrate the provisioning hook and tab into the main component. Let me find the right insertion points: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 280, - "limit": 30 -} -``` -**Output:** -``` - -00281| // ============================================================================= -00282| // Settings: AWS EC2 Discovery Configuration -00283| // ============================================================================= -00284| -00285| function AwsEc2Settings({ machines }: { machines: ReturnType }) { -00286| const cfg = machines.config.aws; -00287| const [saving, setSaving] = useState(false); -00288| const [region, setRegion] = useState(cfg.region ?? ""); -00289| const [sshUser, setSshUser] = useState(cfg.ssh.user); -00290| const [sshPort, setSshPort] = useState(cfg.ssh.port); -00291| const [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? ""); -00292| const [hostPref, setHostPref] = useState(cfg.host_preference.join(", ")); -00293| -00294| const handleSave = async () => { -00295| setSaving(true); -00296| try { -00297| await machines.updateSettings({ -00298| source: "aws-ec2", -00299| enable: true, -00300| aws: { -00301| ...cfg, -00302| region: region || null, -00303| ssh: { -00304| user: sshUser || "root", -00305| port: sshPort || 22, -00306| key_path: sshKeyPath || null, -00307| }, -00308| host_preference: hostPref.split(",").map((s) => s.trim()).filter(Boolean), -00309| }, -00310| }); - -(File has more lines. Use 'offset' parameter to read beyond line 310) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 356, - "limit": 30 -} -``` -**Output:** -``` - -00357| -00358|
-00359| -00360| setSshPort(Number.parseInt(e.target.value, 10) || 22)} -00364| /> -00365|
-00366|
-00367| -00368| setSshKeyPath(e.target.value)} -00372| /> -00373|
-00374| -00375| -00376| -00377| -00381| -00382| -00383| ); -00384| } -00385| -00386| // ============================================================================= - -(File has more lines. Use 'offset' parameter to read beyond line 386) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.9s) - -Now I'll add the EC2 provisioning list component and wire it into the panel: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "// =============================================================================\n// Main Component\n// =============================================================================", - "newString": "// =============================================================================\n// Provisioning: EC2 Instance List\n// =============================================================================\n\nfunction Ec2AppRow({\n\tappId,\n\tapp,\n\tonRemove,\n}: {\n\tappId: string;\n\tapp: Ec2AppConfig;\n\tonRemove: () => void;\n}) {\n\tconst [removing, setRemoving] = useState(false);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appId}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{app.instance_count} x {app.instance_type ?? \"t3.micro\"} · {app.os_type}\n\t\t\t\t\t\t{app.machine.roles.length > 0 && ` · roles: ${app.machine.roles.join(\", \")}`}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{app.security_group.create && (\n\t\t\t\t\tSG\n\t\t\t\t)}\n\t\t\t\t{app.iam.enable && (\n\t\t\t\t\tIAM\n\t\t\t\t)}\n\t\t\t\t{app.key_pair.create && (\n\t\t\t\t\tKey\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{app.associate_public_ip ? \"public\" : \"private\"}\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsetRemoving(true);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait onRemove();\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tsetRemoving(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t\tdisabled={removing}\n\t\t\t\t>\n\t\t\t\t\t{removing ? (\n\t\t\t\t\t\t\n\t\t\t\t\t) : (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -Good, now wire the hook and Provision tab into the DeployPanel component: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);", - "newString": "\tconst machinesConfig = useMachinesConfig();\n\tconst ec2Provisioning = useEc2Provisioning();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. -ERROR [461:8] Variable 'ec2Provisioning' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.2s) - -Now add the Provision tab alongside the other tabs. Let me find the tabs section: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t", - "newString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tProvision\n\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.8s) - -Now add the Provision tab content. Let me find the right place -- after the Machines tab content: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t", - "newString": "\t\t\t\t{/* Provision Tab */}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

EC2 Instances to Provision

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tDefine instance groups here, then run infra:deploy to create them.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Instances Configured

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tClick Provision EC2 to define instance groups.\n\t\t\t\t\t\t\t\t\t\tEach group gets its own security group, IAM role, and optional key pair.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{Object.entries(ec2Provisioning.config.apps).map(([id, app]) => (\n\t\t\t\t\t\t\t\t ec2Provisioning.removeApp(id)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tRun infra:deploy to\n\t\t\t\t\t\t\t\t\t\tprovision these instances. After provisioning, run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:pull-outputs and\n\t\t\t\t\t\t\t\t\t\treload the shell to see them in the Machines tab.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 16.1s) - -No LSP errors in our files. Let me verify: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "cd apps/web && npx tsc --noEmit --pretty 2>&1 | rg \"deploy/\" | head -20", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Typecheck deploy panel components" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.9s) - -Zero type errors in our deploy files. Now update the docs: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "offset": 230, - "limit": 30 -} -``` -**Output:** -``` - -00231| ssh = { -00232| user = "ec2-user"; -00233| port = 22; -00234| keyPath = "~/.ssh/aws_deploy"; -00235| }; -00236| }; -00237| ``` -00238| -00239| ### Filtering by Instance IDs -00240| -00241| Target specific instances instead of using filters: -00242| -00243| ```nix -00244| stack.infra.machines.aws = { -00245| instance-ids = [ -00246| "i-0abc123def456789a" -00247| "i-0def456789abc1230" -00248| ]; -00249| }; -00250| ``` -00251| -00252| ## Studio UI: Deploy Panel -00253| -00254| The Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs: -00255| -00256| ### Machines Tab -00257| -00258| Displays a card grid of all machines in your inventory. Each card shows: -00259| -00260| - Machine name and hostname - -(File has more lines. Use 'offset' parameter to read beyond line 260) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 22.9s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:", - "newString": "## Provisioning EC2 Instances\n\nBeyond discovering existing machines, Stack can **provision new EC2 instances** via the `aws-ec2-app` infra module. This creates instances with auto-resolved AMIs (Ubuntu 24.04 or NixOS), security groups, IAM roles, and key pairs.\n\n### From the Studio UI\n\n1. Open the **Deploy** panel and go to the **Provision** tab\n2. Click **Provision EC2** to open the configuration dialog\n3. Fill in the instance group: app ID, count, instance type, OS, networking, SSH key, IAM\n4. Click **Add to Config** -- this writes to your Nix config\n5. Run `infra:deploy` from the terminal to create the instances\n6. Run `infra:pull-outputs` and reload the shell to see them in the Machines tab\n\n### From Nix Config\n\n```nix\nstackpanel.infra.aws-ec2-app = {\n enable = true;\n\n apps = {\n web-server = {\n instance-count = 2;\n instance-type = \"t3.small\";\n os-type = \"ubuntu\"; # or \"nixos\"\n associate-public-ip = true;\n\n # Auto-creates a security group with SSH + HTTP + HTTPS\n security-group.create = true;\n\n # Auto-creates IAM role with SSM + ECR access\n iam.enable = true;\n\n # Import your SSH public key\n key-pair = {\n create = true;\n public-key = \"ssh-ed25519 AAAA...\";\n };\n\n # Machine metadata for Colmena targeting\n machine = {\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n target-env = \"production\";\n };\n };\n };\n};\n```\n\nThen deploy:\n\n```bash\ninfra:deploy\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\nThe provisioned instances automatically appear in your machine inventory with their public IPs, and can be targeted by Colmena for NixOS deployments.\n\n### What Gets Created\n\nFor each app group, `aws-ec2-app` provisions:\n\n| Resource | Created when |\n|----------|-------------|\n| Security Group | `security-group.create = true` (default) |\n| SSH Key Pair | `key-pair.create = true` |\n| IAM Role + Instance Profile | `iam.enable = true` (default) |\n| EC2 Instances | Always (count from `instance-count`) |\n| ALB + Target Group | `alb.enable = true` |\n| ECR Repository | `ecr.enable = true` |\n| SSM Parameters | `ssm.enable = true` |\n\nVPC and subnets are auto-discovered from your default VPC unless you specify `vpc-id` and `subnet-ids` explicitly.\n\n\nThe `aws-ec2-app` module auto-resolves AMIs. For Ubuntu, it finds the latest Ubuntu 24.04 LTS from Canonical. For NixOS, it finds the latest Determinate Systems NixOS image. You can override with an explicit `ami` value.\n\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has five tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now update the rest of the Deploy panel docs section to reflect the new Provision tab: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "When the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab", - "newString": "Click a machine card to edit its configuration. Use the **Add Machine** button in the header to add static machines.\n\nWhen the inventory is empty, it shows guidance to add machines or configure EC2 discovery.\n\n### Provision Tab\n\nConfigure EC2 instances to create. Each row shows the instance group with its count, type, OS, and what resources will be created (SG, IAM, Key). Click **Provision EC2** to define a new group. After configuring, run `infra:deploy` to create the actual instances.\n\n### App Targets Tab" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.6s) - -Here's a summary of everything that was added: - -## How to Provision Machines - -The Deploy panel now has a **Provision** tab (between Machines and App Targets). The flow is: - -1. **Deploy panel -> Provision tab -> "Provision EC2" button** opens a dialog where you configure: - - App ID (instance group name) - - Count, instance type, OS (Ubuntu 24.04 / NixOS) - - Networking: VPC, subnets (auto-detected if blank), public IP toggle, auto-created security group - - Storage: root volume size - - SSH: optionally create + import a key pair - - IAM: auto-create role with SSM + ECR access - - Machine metadata: roles, tags, environment (for Colmena targeting) - -2. **"Add to Config"** writes to `.stack/config.nix` under `stack.infra.aws-ec2-app` - -3. **`infra:deploy`** in terminal provisions the actual EC2 instances (with SG, IAM, key pair) - -4. **`infra:pull-outputs`** + shell reload populates the Machines tab with the new instances - -## New/Updated Files - -| File | What | -|------|------| -| `deploy/use-machines.ts` | Added `Ec2AppConfig`, `Ec2AppModuleConfig` types + `useEc2Provisioning()` hook for CRUD on `infra.aws-ec2-app` | -| `deploy/provision-instance-dialog.tsx` | New dialog for configuring EC2 instance groups | -| `deploy/deploy-panel.tsx` | Added **Provision** tab with `Ec2AppRow` list, wired `ProvisionInstanceDialog` + `useEc2Provisioning` | -| `docs/deployment/machines.mdx` | Added "Provisioning EC2 Instances" section covering both Studio UI and Nix config approaches, plus the what-gets-created table | - -Zero type errors across all new files (verified with `tsc --noEmit`). - ---- - -## User - -the machine provisioning is great. but we should create some preset based on the user's apps, since it asks for thihngs like roles, environments, number of machines, etc. we could generate buttons that would fill in sane defaults for provisioning - ---- - -## Assistant (Build · claude-opus-4-6 · 178.1s) - -_Thinking:_ - -The user wants preset buttons that auto-fill the provisioning dialog based on their configured apps. For example, if they have a `web` app and a `server` app, we could generate presets like "Web Server (2x t3.small)" or "API Server (1x t3.micro)" that pre-fill the form with sensible defaults. - -Let me look at how apps are structured in the nix config to understand what data we have available, then build presets from that. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore app config shapes for presets", - "prompt": "I need to understand what app data is available in the Studio UI to generate EC2 provisioning presets. Search for:\n\n1. The MachineInfo type and AppDeployMapping type in deploy-panel.tsx - what fields are available\n2. How apps are loaded in the deploy panel - look at useColmenaData and useNixConfig\n3. The app type definitions - what fields does an app have (framework, deployment.host, container config, etc.)\n4. Look at .stack/config.nix apps section to see real app examples with their frameworks, deployment hosts, container types\n5. What app data is available via useNixConfig() - specifically the apps/appsComputed shape\n\nReturn:\n- The full app type shape available from nixConfig (framework, deployment, container fields)\n- The real app configs from .stack/config.nix\n- How to access the app list from within the deploy panel", - "subagent_type": "explore" -} -``` -**Output:** -``` -Now I have everything needed. Here is the comprehensive summary: - ---- - -## Complete Findings - -### 1. MachineInfo Type and AppDeployMapping Type (deploy-panel.tsx) - -**File**: `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` - -**MachineInfo** (lines 69-86): -```typescript -interface MachineInfo { - id: string; - name: string; - host: string | null; - ssh: { - user: string; - port: number; - keyPath: string | null; - }; - tags: string[]; - roles: string[]; - provider: string | null; - arch: string | null; - publicIp: string | null; - privateIp: string | null; - targetEnv: string | null; - labels: Record; -} -``` - -**AppDeployMapping** (lines 88-95): -```typescript -interface AppDeployMapping { - enable: boolean; - targets: string[]; - resolvedTargets: string[]; - role: string | null; - nixosModules: string[]; - system: string | null; -} -``` - -**ColmenaConfig** (lines 97-104): -```typescript -interface ColmenaConfig { - enable: boolean; - machineSource: string; - generateHive: boolean; - config: string; - machineCount: number; - machineIds: string[]; -} -``` - -### 2. How Apps Are Loaded in the Deploy Panel - -**File**: `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` - -The `useColmenaData()` hook (lines 110-144) works as follows: - -1. Calls `useNixConfig()` to get the full evaluated Nix config JSON -2. Extracts `nixConfig.serializable.colmena` as `ColmenaConfig` -3. Extracts `nixConfig.colmena.machinesComputed` as `Record` -4. Extracts apps from **either** `nixConfig.apps` or `nixConfig.appsComputed` (fallback) -5. For each app, reads `app.deploy` and builds the `AppDeployMapping` if `deploy.enable` is true - -The key line accessing app data is: -```typescript -const rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>; -``` - -The deploy panel also uses two additional hooks from `use-machines.ts`: -- `useMachinesConfig()` -- reads from `useNixData("infra")`, extracting `.machines` -- `useEc2Provisioning()` -- reads from `useNixData("infra")`, extracting `.aws_ec2_app` - -### 3. The Full App Type Shape - -There are **three layers** of app data, each progressively richer: - -#### Layer 1: Proto-generated App (base serializable fields) -**File**: `/Users/cm/git/darkmatter/stack/packages/proto/gen/ts/apps.ts` (lines 18-57) - -```typescript -interface App { - name: string; // Display name - description?: string; // Description - path: string; // Relative path to app directory - type?: string; // Runtime: "bun" | "go" | "python" | "rust" | etc. - port?: number; // Development server port - domain?: string; // Local development domain - environments: { // Per-env config (dev, staging, prod) - [key: string]: AppEnvironment; - }; - deploy?: AppDeploy; // Colmena deployment mapping -} - -interface AppDeploy { - enable: boolean; - targets: string[]; - role?: string; - nixos_modules: string[]; - system?: string; - secrets: string[]; -} - -interface AppEnvironment { - name: string; - description?: string; - env: { [key: string]: string }; // env var name -> value or vals reference -} -``` - -#### Layer 2: Nix-specific app options (not in proto, from core/options/apps.nix) -**File**: `/Users/cm/git/darkmatter/stack/nix/stack/core/options/apps.nix` (lines 107-281) - -These are additional options on each app submodule beyond the proto schema: - -```nix -framework = { - tanstack-start.enable # TanStack Start (SSR + Nitro) - nextjs.enable # Next.js - nextjs.output # "standalone" | "export" - vite.enable # Vite SPA - vite.ssr # bool - vite.assets-dir # string (default "dist") - hono.enable # Hono API server - hono.entrypoint # string (default "src/index.ts") - astro.enable # Astro - remix.enable # Remix - nuxt.enable # Nuxt -}; - -deployment = { - enable # bool - host # null | "cloudflare" | "fly" | "vercel" | "aws" - bindings # list of env var names to bind - secrets # subset of bindings that are sensitive -}; - -tooling = { - install / build / test / dev # nullable tool step submodules - build-steps / formatters / linters # lists of tool steps -}; - -offset # nullable int (port offset from base) -tls # bool (TLS for vhost) -packageName # nullable string (NPM package name for turbo) -``` - -#### Layer 3: Module-injected app options (via appModules) -These are added by individual modules: - -**Container options** (`nix/stack/containers/module.nix`, lines 59-111): -```nix -container = { - enable # bool - name # string - version # string - port # int - registry # string - workingDir # string - buildOutputPath # string - maxLayers # int - defaultCopyArgs # list of strings - env # attrs of strings - type # "bun" | "node" | "go" | "static" | "custom" - startupCommand # nullable string or list of strings - copyToRoot # nullable path or list of paths -}; -``` - -**Fly deployment options** (`nix/stack/deployment/fly/module.nix`, lines 59-135): -```nix -deployment.fly = { - appName # string (defaults to app name) - region # string (defaults to global default or "iad") - memory # int - cpus # int - cpuKind # "shared" | "performance" - autoStart # bool - autoStop # "off" | "stop" | "suspend" - minMachines # int - forceHttps # bool - env # attrs of strings -}; - -deployment.container = { - type # "bun" | "node" | "go" | "static" | "custom" - port # int (default 3000) - entrypoint # nullable string -}; -``` - -Other modules also inject options via `appModules` (go, process-compose, oxlint, linting, etc.). - -#### Layer 4: Computed app data (appsComputed) -**File**: `/Users/cm/git/darkmatter/stack/nix/stack/core/options/apps.nix` (lines 358-406) - -```nix -appsComputed. = { - port # int (deterministic from hash) - domain # string | null (e.g., "web.stack.lan") - url # string | null (e.g., "https://web.stack.lan") - tls # bool - offset # int | null - tooling # raw tooling config - wrappedTooling # derivations (only when pkgs available) - deployTargets # list of target machine ids -}; -``` - -### 4. Real App Configs from .stack/config.nix - -**File**: `/Users/cm/git/darkmatter/stack/.stack/config.nix` (lines 15-139) - -Three apps are defined: - -**`docs`** -- Simple documentation site: -```nix -docs = { - description = "Documentation site"; - domain = "docs"; - name = "docs"; - path = "apps/docs"; - tls = true; - type = "bun"; - environments = { dev = {}; prod = {}; staging = {}; }; - linting.oxlint.enable = true; -}; -``` - -**`stack-go`** -- Go CLI/agent: -```nix -stack-go = { - description = "Stack CLI and agent (Go)"; - name = "stack"; - path = "apps/stack-go"; - type = "go"; - go = { enable = true; binaryName = "stack"; generateFiles = false; ldflags = ["-s" "-w"]; }; - environments.dev.env = { STACKPANEL_TEST_PAIRING_TOKEN = "token123"; }; -}; -``` - -**`web`** -- Full-featured app with framework, deployment, container, and linting: -```nix -web = { - description = "Main web application"; - name = "web"; - path = "apps/web"; - domain = "stack"; - tls = true; - type = "bun"; - framework.tanstack-start.enable = true; - container = { enable = true; type = "bun"; }; - deployment = { - enable = true; - host = "fly"; - bindings = ["DATABASE_URL" "CORS_ORIGIN" "BETTER_AUTH_SECRET" "BETTER_AUTH_URL" "POLAR_ACCESS_TOKEN" "POLAR_SUCCESS_URL"]; - secrets = ["DATABASE_URL" "BETTER_AUTH_SECRET" "POLAR_ACCESS_TOKEN"]; - fly = { appName = "stack-web"; region = "iad"; }; - }; - linting.oxlint = { enable = true; fix = true; plugins = ["react" "typescript"]; categories = { correctness = "error"; suspicious = "warn"; }; }; - commands.dev.command = "bun run -F web dev"; - environments = { dev = { env = { MEMO_MEMOAS_AD = "foobar"; }; }; prod = {}; staging = {}; }; -}; -``` - -There is also a real **EC2 provisioning config** at `infra.aws-ec2-app.apps.stack-staging` (lines 349-431) showing a complete EC2 app with `machine.roles = ["docs" "web"]`, instance type, key pair, security group rules, IAM, and tags. - -### 5. How to Access App Data From Within the Deploy Panel - -There are **two distinct data sources** available in the deploy panel: - -#### Source A: `useApps()` -- Proto-serialized app data (CRUD-capable) -```typescript -import { useApps } from "@/lib/use-agent"; -const { data: apps } = useApps(); -// apps: Record -// Each app has: name, path, type, port, domain, description, environments, deploy -``` -This returns the apps from the `data/apps.nix` file via the Go agent's `getApps` RPC call. This is the **user-editable** app data (proto-serialized). It is what `apps-panel-alt.tsx` uses. - -#### Source B: `useNixConfig()` -- Full evaluated Nix config (read-only) -```typescript -import { useNixConfig } from "@/lib/use-agent"; -const { data: nixConfig } = useNixConfig(); -// nixConfig is the parsed JSON of fullConfig from cli.nix -``` - -The `nixConfig` object has these top-level keys (from `cli.nix` lines 119-240): -```typescript -{ - version: 1, - projectName: string, - projectRoot: string, - basePort: number, - processComposePort: number, - paths: { state, gen, data }, - apps: Record, // computed apps - services: Record, - network: { step: { enable, caUrl } }, - ide: { vscode: { ... } }, - motd: { ... }, - packages: Array<{ name, version, attrPath, source }>, - moduleRequirements: { ... }, - missingFlakeInputs: [...], - healthchecks: [...], - ui: { - extensions: { ... }, - modules: { ... }, - modulesList: [...], - panels: { ... }, - panelModules: [...] - } -} -``` - -**For the deploy panel specifically**, `nixConfig.apps` gives you `{ port, domain, url, tls }` per app. The richer fields (framework, deployment, container) are only available in the raw Nix config which gets passed through additional paths: -- `nixConfig.colmena.machinesComputed` -- machine inventory -- `nixConfig.serializable.colmena` -- colmena module config -- The raw `apps`/`appsComputed` (with framework, deployment, container fields) are available from the **full** `nix eval` output when the FlakeWatcher returns the complete config. - -The deploy panel currently accesses apps via `useColmenaData()` which reads `cfg.apps ?? cfg.appsComputed` from the nixConfig, looking specifically at each app's `.deploy` sub-object. To access framework, container, or deployment.host fields from within the deploy panel, you would use the same `nixConfig` and drill into the app objects: - -```typescript -const { data: nixConfig } = useNixConfig(); -const rawApps = (nixConfig?.apps ?? nixConfig?.appsComputed ?? {}) as Record; - -// Access framework info: -const framework = rawApps["web"]?.framework; // { tanstack-start: { enable: true }, ... } - -// Access deployment host: -const host = rawApps["web"]?.deployment?.host; // "fly" | "cloudflare" | "vercel" | "aws" - -// Access container config: -const container = rawApps["web"]?.container; // { enable: true, type: "bun", ... } -``` - -**Important caveat**: The `fullConfig` in `cli.nix` (the JSON that `getNixConfig` returns) only serializes a **subset** of app data (port, domain, url, tls). The richer fields like `framework`, `deployment`, and `container` are part of the full Nix evaluation but may not all be present in the JSON unless the FlakeWatcher's `GetConfig()` returns the full `nix eval .#stackpanelConfig` output rather than the trimmed `fullConfig`. The deploy panel's `useColmenaData()` already accesses `cfg.apps` expecting the richer shape, which suggests the FlakeWatcher returns the complete evaluated config (not the trimmed CLI version). - - -session_id: ses_35635a1b0ffepGzuwZKyYy6NOD - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/.stack/config.nix", - "offset": 15, - "limit": 140 -} -``` -**Output:** -``` - -00016| docs = { -00017| description = "Documentation site"; -00018| domain = "docs"; -00019| environments = { -00020| dev = { -00021| env = { }; -00022| name = "dev"; -00023| }; -00024| prod = { -00025| env = { }; -00026| name = "prod"; -00027| }; -00028| staging = { -00029| env = { }; -00030| name = "staging"; -00031| }; -00032| }; -00033| linting = { -00034| oxlint = { -00035| enable = true; -00036| }; -00037| }; -00038| name = "docs"; -00039| path = "apps/docs"; -00040| tls = true; -00041| type = "bun"; -00042| }; -00043| stack-go = { -00044| description = "Stack CLI and agent (Go)"; -00045| environments = { -00046| dev = { -00047| env = { -00048| STACKPANEL_TEST_PAIRING_TOKEN = "token123"; -00049| }; -00050| name = "dev"; -00051| }; -00052| }; -00053| go = { -00054| binaryName = "stack"; -00055| enable = true; -00056| generateFiles = false; -00057| ldflags = [ -00058| "-s" -00059| "-w" -00060| ]; -00061| }; -00062| name = "stack"; -00063| path = "apps/stack-go"; -00064| type = "go"; -00065| }; -00066| web = { -00067| commands = { -00068| dev = { -00069| command = "bun run -F web dev"; -00070| }; -00071| }; -00072| container = { -00073| enable = true; -00074| type = "bun"; -00075| }; -00076| deployment = { -00077| bindings = [ -00078| "DATABASE_URL" -00079| "CORS_ORIGIN" -00080| "BETTER_AUTH_SECRET" -00081| "BETTER_AUTH_URL" -00082| "POLAR_ACCESS_TOKEN" -00083| "POLAR_SUCCESS_URL" -00084| ]; -00085| enable = true; -00086| fly = { -00087| appName = "stack-web"; -00088| region = "iad"; -00089| }; -00090| host = "fly"; -00091| secrets = [ -00092| "DATABASE_URL" -00093| "BETTER_AUTH_SECRET" -00094| "POLAR_ACCESS_TOKEN" -00095| ]; -00096| }; -00097| description = "Main web application"; -00098| domain = "stack"; -00099| environments = { -00100| dev = { -00101| env = { -00102| MEMO_MEMOAS_AD = "foobar"; -00103| }; -00104| name = "dev"; -00105| }; -00106| prod = { -00107| env = { }; -00108| name = "prod"; -00109| }; -00110| staging = { -00111| env = { }; -00112| name = "staging"; -00113| }; -00114| }; -00115| framework = { -00116| tanstack-start = { -00117| enable = true; -00118| }; -00119| }; -00120| linting = { -00121| oxlint = { -00122| categories = { -00123| correctness = "error"; -00124| suspicious = "warn"; -00125| }; -00126| enable = true; -00127| fix = true; -00128| plugins = [ -00129| "react" -00130| "typescript" -00131| ]; -00132| }; -00133| }; -00134| name = "web"; -00135| path = "apps/web"; -00136| tls = true; -00137| type = "bun"; -00138| }; -00139| }; -00140| -00141| # --------------------------------------------------------------------------- -00142| # AWS -00143| # --------------------------------------------------------------------------- -00144| aws = { -00145| roles-anywhere = { -00146| account-id = "950224716579"; -00147| enable = true; -00148| profile-arn = "arn:aws:rolesanywhere:us-west-2:950224716579:profile/4e72b392-9074-4e53-8cd0-1ba50856d1ca"; -00149| region = "us-west-2"; -00150| role-name = "darkmatter-dev"; -00151| trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:950224716579:trust-anchor/c99a9383-6be1-48ba-8e63-c3ab6b7069cb"; -00152| }; -00153| }; -00154| -00155| # --------------------------------------------------------------------------- - -(File has more lines. Use 'offset' parameter to read beyond line 155) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.8s) - -Now I understand the data shape. Let me read the current provision dialog and the deploy panel to plan the presets approach: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { Button } from "@ui/button"; -00004| import { -00005| Dialog, -00006| DialogContent, -00007| DialogDescription, -00008| DialogFooter, -00009| DialogHeader, -00010| DialogTitle, -00011| } from "@ui/dialog"; -00012| import { Input } from "@ui/input"; -00013| import { Label } from "@ui/label"; -00014| import { -00015| Select, -00016| SelectContent, -00017| SelectItem, -00018| SelectTrigger, -00019| SelectValue, -00020| } from "@ui/select"; -00021| import { Switch } from "@ui/switch"; -00022| import { Loader2, Plus } from "lucide-react"; -00023| import { useState } from "react"; -00024| import { toast } from "sonner"; -00025| import { -00026| type Ec2AppConfig, -00027| DEFAULT_EC2_APP, -00028| DEFAULT_EC2_MACHINE_META, -00029| type useEc2Provisioning, -00030| } from "./use-machines"; -00031| -00032| interface ProvisionInstanceDialogProps { -00033| ec2: ReturnType; -00034| } -00035| -00036| const INSTANCE_TYPES = [ -00037| "t3.micro", -00038| "t3.small", -00039| "t3.medium", -00040| "t3.large", -00041| "t3.xlarge", -00042| "t3.2xlarge", -00043| "m6i.large", -00044| "m6i.xlarge", -00045| "m6i.2xlarge", -00046| "c6i.large", -00047| "c6i.xlarge", -00048| "c6i.2xlarge", -00049| "r6i.large", -00050| "r6i.xlarge", -00051| ]; -00052| -00053| export function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) { -00054| const [open, setOpen] = useState(false); -00055| const [saving, setSaving] = useState(false); -00056| -00057| // Form state -00058| const [appId, setAppId] = useState(""); -00059| const [instanceCount, setInstanceCount] = useState(1); -00060| const [instanceType, setInstanceType] = useState("t3.micro"); -00061| const [osType, setOsType] = useState<"ubuntu" | "nixos">("ubuntu"); -00062| const [vpcId, setVpcId] = useState(""); -00063| const [subnetIds, setSubnetIds] = useState(""); -00064| const [rootVolumeSize, setRootVolumeSize] = useState(""); -00065| const [associatePublicIp, setAssociatePublicIp] = useState(true); -00066| const [createSg, setCreateSg] = useState(true); -00067| const [createKeyPair, setCreateKeyPair] = useState(false); -00068| const [publicKey, setPublicKey] = useState(""); -00069| const [enableIam, setEnableIam] = useState(true); -00070| const [roles, setRoles] = useState(""); -00071| const [tags, setTags] = useState(""); -00072| const [targetEnv, setTargetEnv] = useState(""); -00073| -00074| const handleOpenChange = (isOpen: boolean) => { -00075| setOpen(isOpen); -00076| if (!isOpen) { -00077| setAppId(""); -00078| setInstanceCount(1); -00079| setInstanceType("t3.micro"); -00080| setOsType("ubuntu"); -00081| setVpcId(""); -00082| setSubnetIds(""); -00083| setRootVolumeSize(""); -00084| setAssociatePublicIp(true); -00085| setCreateSg(true); -00086| setCreateKeyPair(false); -00087| setPublicKey(""); -00088| setEnableIam(true); -00089| setRoles(""); -00090| setTags(""); -00091| setTargetEnv(""); -00092| } -00093| }; -00094| -00095| const isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim()); -00096| -00097| const handleSubmit = async () => { -00098| const id = appId.trim(); -00099| if (!id) { -00100| toast.error("App ID is required"); -00101| return; -00102| } -00103| if (ec2.config.apps[id]) { -00104| toast.error(`EC2 app "${id}" already exists`); -00105| return; -00106| } -00107| -00108| setSaving(true); -00109| try { -00110| const appConfig: Ec2AppConfig = { -00111| ...DEFAULT_EC2_APP, -00112| instance_count: instanceCount, -00113| instance_type: instanceType, -00114| os_type: osType, -00115| vpc_id: vpcId || null, -00116| subnet_ids: subnetIds -00117| ? subnetIds.split(",").map((s) => s.trim()).filter(Boolean) -00118| : [], -00119| root_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null, -00120| associate_public_ip: associatePublicIp, -00121| security_group: { -00122| ...DEFAULT_EC2_APP.security_group, -00123| create: createSg, -00124| }, -00125| key_pair: { -00126| create: createKeyPair, -00127| name: createKeyPair ? `${id}-key` : null, -00128| public_key: createKeyPair ? publicKey || null : null, -00129| }, -00130| iam: { -00131| enable: enableIam, -00132| role_name: enableIam ? `${id}-ec2-role` : null, -00133| }, -00134| tags: { -00135| Name: id, -00136| ManagedBy: "stack-infra", -00137| }, -00138| machine: { -00139| ...DEFAULT_EC2_MACHINE_META, -00140| roles: roles ? roles.split(",").map((s) => s.trim()).filter(Boolean) : [], -00141| tags: tags ? tags.split(",").map((s) => s.trim()).filter(Boolean) : [], -00142| target_env: targetEnv || null, -00143| }, -00144| }; -00145| -00146| await ec2.addApp(id, appConfig); -00147| toast.success(`EC2 app "${id}" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`); -00148| handleOpenChange(false); -00149| } catch (err) { -00150| toast.error(err instanceof Error ? err.message : "Failed to add EC2 app"); -00151| } finally { -00152| setSaving(false); -00153| } -00154| }; -00155| -00156| return ( -00157| -00158| -00162| -00163| -00164| Provision EC2 Instances -00165| -00166| Define EC2 instances to provision. Run infra:deploy after to create them. -00167| -00168| -00169| -00170|
-00171| {/* Identity */} -00172|
-00173| -00174| setAppId(e.target.value)} -00178| /> -00179|

-00180| Unique identifier for this group of instances -00181|

-00182|
-00183| -00184| {/* Instance sizing */} -00185|
-00186|
-00187| -00188| setInstanceCount(Number.parseInt(e.target.value, 10) || 1)} -00194| /> -00195|
-00196|
-00197| -00198| -00208|
-00209|
-00210| -00211| -00220|
-00221|
-00222| -00223| {/* Networking */} -00224|
-00225| Networking -00226| -00227|
-00228|
-00229| -00230| setVpcId(e.target.value)} -00234| /> -00235|
-00236|
-00237| -00238| setSubnetIds(e.target.value)} -00242| /> -00243|
-00244|
-00245| -00246|
-00247|
-00248| -00249|

Associate a public IPv4 address

-00250|
-00251| -00252|
-00253| -00254|
-00255|
-00256| -00257|

Auto-create with SSH, HTTP, HTTPS ingress

-00258|
-00259| -00260|
-00261|
-00262| -00263| {/* Storage */} -00264|
-00265| -00266| setRootVolumeSize(e.target.value)} -00270| /> -00271|
-00272| -00273| {/* SSH Key */} -00274|
-00275| SSH Key Pair -00276| -00277|
-00278|
-00279| -00280|

Import your public key to AWS

-00281|
-00282| -00283|
-00284| -00285| {createKeyPair && ( -00286|
-00287| -00288| setPublicKey(e.target.value)} -00292| /> -00293|
-00294| )} -00295|
-00296| -00297| {/* IAM */} -00298|
-00299|
-00300| -00301|

Create role with SSM + ECR access

-00302|
-00303| -00304|
-00305| -00306| {/* Machine metadata */} -00307|
-00308| Machine Metadata -00309| -00310|
-00311|
-00312| -00313| setRoles(e.target.value)} -00317| /> -00318|
-00319|
-00320| -00321| setTags(e.target.value)} -00325| /> -00326|
-00327|
-00328| -00329|
-00330| -00331| -00341|
-00342|
-00343|
-00344| -00345| -00346| -00347| -00351| -00352|
-00353|
-00354| ); -00355| } -00356| - -(End of file - total 356 lines) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 66.8s) - -Now I have a clear picture. I'll add preset generation that reads the user's apps from `useNixConfig()` and creates smart presets. The presets will appear as buttons above the form that fill in the fields. - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "content": "\"use client\";\n\nimport { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport {\n\tCloud,\n\tDatabase,\n\tGlobe,\n\tLoader2,\n\tPlus,\n\tServer,\n\tSparkles,\n\tTerminal,\n} from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { useNixConfig } from \"@/lib/use-agent\";\nimport {\n\ttype Ec2AppConfig,\n\tDEFAULT_EC2_APP,\n\tDEFAULT_EC2_MACHINE_META,\n\ttype useEc2Provisioning,\n} from \"./use-machines\";\n\n// =============================================================================\n// Presets\n// =============================================================================\n\ninterface Preset {\n\tid: string;\n\tlabel: string;\n\tdescription: string;\n\ticon: typeof Server;\n\tappId: string;\n\tinstanceCount: number;\n\tinstanceType: string;\n\tosType: \"ubuntu\" | \"nixos\";\n\troles: string[];\n\ttags: string[];\n\ttargetEnv: string;\n\trootVolumeSize: number | null;\n}\n\n/**\n * Derive presets from the user's configured apps.\n *\n * For each app we look at type, framework, deployment settings, and container\n * config to suggest sane EC2 defaults.\n */\nfunction derivePresets(\n\tnixConfig: Record | null | undefined,\n\texistingAppIds: Set,\n): Preset[] {\n\tif (!nixConfig) return [];\n\n\tconst rawApps = (nixConfig.apps ?? nixConfig.appsComputed ?? {}) as Record<\n\t\tstring,\n\t\tRecord\n\t>;\n\tconst presets: Preset[] = [];\n\n\tfor (const [appName, app] of Object.entries(rawApps)) {\n\t\tconst appType = (app.type as string) ?? \"bun\";\n\t\tconst framework = app.framework as Record> | undefined;\n\t\tconst deployment = app.deployment as Record | undefined;\n\t\tconst container = app.container as Record | undefined;\n\t\tconst description = (app.description as string) ?? appName;\n\n\t\t// Skip apps that already have EC2 provisioning\n\t\tif (existingAppIds.has(appName)) continue;\n\n\t\t// Determine the kind of workload for sizing heuristics\n\t\tconst isGo = appType === \"go\";\n\t\tconst hasContainer = container?.enable === true;\n\t\tconst deployHost = deployment?.host as string | undefined;\n\n\t\t// Detect framework\n\t\tconst isTanStack = framework?.[\"tanstack-start\"]?.enable === true || framework?.[\"tanstack_start\"]?.enable === true;\n\t\tconst isNextjs = framework?.nextjs?.enable === true;\n\t\tconst isVite = framework?.vite?.enable === true;\n\t\tconst isHono = framework?.hono?.enable === true;\n\t\tconst isAstro = framework?.astro?.enable === true;\n\t\tconst isStaticSite = isVite || isAstro;\n\t\tconst isSSR = isTanStack || isNextjs;\n\t\tconst isApi = isHono || isGo;\n\n\t\t// Skip apps already deployed to non-EC2 hosts (cloudflare, fly)\n\t\t// but still offer them if deployment is disabled or host is null/aws\n\t\tif (deployHost && deployHost !== \"aws\" && deployment?.enable === true) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Build preset\n\t\tlet icon = Server;\n\t\tlet instanceType = \"t3.micro\";\n\t\tlet instanceCount = 1;\n\t\tlet roles: string[] = [appName];\n\t\tlet rootVolumeSize: number | null = null;\n\n\t\tif (isSSR) {\n\t\t\t// SSR frameworks need more RAM for rendering\n\t\t\ticon = Globe;\n\t\t\tinstanceType = \"t3.small\";\n\t\t\tinstanceCount = 2;\n\t\t\troles = [appName, \"web\"];\n\t\t\trootVolumeSize = 20;\n\t\t} else if (isApi) {\n\t\t\ticon = Terminal;\n\t\t\tinstanceType = isGo ? \"t3.micro\" : \"t3.small\";\n\t\t\tinstanceCount = 2;\n\t\t\troles = [appName, \"api\"];\n\t\t} else if (isStaticSite) {\n\t\t\ticon = Cloud;\n\t\t\tinstanceType = \"t3.micro\";\n\t\t\tinstanceCount = 1;\n\t\t\troles = [appName, \"web\"];\n\t\t} else if (hasContainer) {\n\t\t\ticon = Database;\n\t\t\tinstanceType = \"t3.small\";\n\t\t\tinstanceCount = 1;\n\t\t\troles = [appName];\n\t\t\trootVolumeSize = 30;\n\t\t} else {\n\t\t\t// Generic app\n\t\t\tinstanceType = \"t3.micro\";\n\t\t\troles = [appName];\n\t\t}\n\n\t\tconst frameworkLabel = isTanStack\n\t\t\t? \"TanStack Start\"\n\t\t\t: isNextjs\n\t\t\t\t? \"Next.js\"\n\t\t\t\t: isHono\n\t\t\t\t\t? \"Hono\"\n\t\t\t\t\t: isVite\n\t\t\t\t\t\t? \"Vite\"\n\t\t\t\t\t\t: isAstro\n\t\t\t\t\t\t\t? \"Astro\"\n\t\t\t\t\t\t\t: isGo\n\t\t\t\t\t\t\t\t? \"Go\"\n\t\t\t\t\t\t\t\t: appType;\n\n\t\tpresets.push({\n\t\t\tid: appName,\n\t\t\tlabel: appName,\n\t\t\tdescription: `${frameworkLabel} · ${instanceCount}x ${instanceType}`,\n\t\t\ticon,\n\t\t\tappId: appName,\n\t\t\tinstanceCount,\n\t\t\tinstanceType,\n\t\t\tosType: \"ubuntu\",\n\t\t\troles,\n\t\t\ttags: [],\n\t\t\ttargetEnv: \"production\",\n\t\t\trootVolumeSize,\n\t\t});\n\t}\n\n\t// Add generic presets if the user has few apps\n\tif (!existingAppIds.has(\"database\")) {\n\t\tpresets.push({\n\t\t\tid: \"_preset_database\",\n\t\t\tlabel: \"Database Server\",\n\t\t\tdescription: \"PostgreSQL/Redis · 1x t3.medium · 50GB\",\n\t\t\ticon: Database,\n\t\t\tappId: \"database\",\n\t\t\tinstanceCount: 1,\n\t\t\tinstanceType: \"t3.medium\",\n\t\t\tosType: \"ubuntu\",\n\t\t\troles: [\"database\"],\n\t\t\ttags: [],\n\t\t\ttargetEnv: \"production\",\n\t\t\trootVolumeSize: 50,\n\t\t});\n\t}\n\n\treturn presets;\n}\n\n// =============================================================================\n// Dialog\n// =============================================================================\n\ninterface ProvisionInstanceDialogProps {\n\tec2: ReturnType;\n}\n\nconst INSTANCE_TYPES = [\n\t\"t3.micro\",\n\t\"t3.small\",\n\t\"t3.medium\",\n\t\"t3.large\",\n\t\"t3.xlarge\",\n\t\"t3.2xlarge\",\n\t\"m6i.large\",\n\t\"m6i.xlarge\",\n\t\"m6i.2xlarge\",\n\t\"c6i.large\",\n\t\"c6i.xlarge\",\n\t\"c6i.2xlarge\",\n\t\"r6i.large\",\n\t\"r6i.xlarge\",\n];\n\nexport function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) {\n\tconst { data: nixConfig } = useNixConfig();\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\n\t// Form state\n\tconst [appId, setAppId] = useState(\"\");\n\tconst [instanceCount, setInstanceCount] = useState(1);\n\tconst [instanceType, setInstanceType] = useState(\"t3.micro\");\n\tconst [osType, setOsType] = useState<\"ubuntu\" | \"nixos\">(\"ubuntu\");\n\tconst [vpcId, setVpcId] = useState(\"\");\n\tconst [subnetIds, setSubnetIds] = useState(\"\");\n\tconst [rootVolumeSize, setRootVolumeSize] = useState(\"\");\n\tconst [associatePublicIp, setAssociatePublicIp] = useState(true);\n\tconst [createSg, setCreateSg] = useState(true);\n\tconst [createKeyPair, setCreateKeyPair] = useState(false);\n\tconst [publicKey, setPublicKey] = useState(\"\");\n\tconst [enableIam, setEnableIam] = useState(true);\n\tconst [roles, setRoles] = useState(\"\");\n\tconst [tags, setTags] = useState(\"\");\n\tconst [targetEnv, setTargetEnv] = useState(\"\");\n\n\tconst existingAppIds = useMemo(\n\t\t() => new Set(Object.keys(ec2.config.apps)),\n\t\t[ec2.config.apps],\n\t);\n\n\tconst presets = useMemo(\n\t\t() => derivePresets(nixConfig as Record | null, existingAppIds),\n\t\t[nixConfig, existingAppIds],\n\t);\n\n\tconst applyPreset = (preset: Preset) => {\n\t\tsetAppId(preset.appId);\n\t\tsetInstanceCount(preset.instanceCount);\n\t\tsetInstanceType(preset.instanceType);\n\t\tsetOsType(preset.osType);\n\t\tsetRoles(preset.roles.join(\", \"));\n\t\tsetTags(preset.tags.join(\", \"));\n\t\tsetTargetEnv(preset.targetEnv);\n\t\tsetRootVolumeSize(preset.rootVolumeSize?.toString() ?? \"\");\n\t\t// Keep networking/SSH/IAM at their current (default) values\n\t};\n\n\tconst resetForm = () => {\n\t\tsetAppId(\"\");\n\t\tsetInstanceCount(1);\n\t\tsetInstanceType(\"t3.micro\");\n\t\tsetOsType(\"ubuntu\");\n\t\tsetVpcId(\"\");\n\t\tsetSubnetIds(\"\");\n\t\tsetRootVolumeSize(\"\");\n\t\tsetAssociatePublicIp(true);\n\t\tsetCreateSg(true);\n\t\tsetCreateKeyPair(false);\n\t\tsetPublicKey(\"\");\n\t\tsetEnableIam(true);\n\t\tsetRoles(\"\");\n\t\tsetTags(\"\");\n\t\tsetTargetEnv(\"\");\n\t};\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) resetForm();\n\t};\n\n\tconst isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = appId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"App ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (ec2.config.apps[id]) {\n\t\t\ttoast.error(`EC2 app \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst appConfig: Ec2AppConfig = {\n\t\t\t\t...DEFAULT_EC2_APP,\n\t\t\t\tinstance_count: instanceCount,\n\t\t\t\tinstance_type: instanceType,\n\t\t\t\tos_type: osType,\n\t\t\t\tvpc_id: vpcId || null,\n\t\t\t\tsubnet_ids: subnetIds\n\t\t\t\t\t? subnetIds.split(\",\").map((s) => s.trim()).filter(Boolean)\n\t\t\t\t\t: [],\n\t\t\t\troot_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null,\n\t\t\t\tassociate_public_ip: associatePublicIp,\n\t\t\t\tsecurity_group: {\n\t\t\t\t\t...DEFAULT_EC2_APP.security_group,\n\t\t\t\t\tcreate: createSg,\n\t\t\t\t},\n\t\t\t\tkey_pair: {\n\t\t\t\t\tcreate: createKeyPair,\n\t\t\t\t\tname: createKeyPair ? `${id}-key` : null,\n\t\t\t\t\tpublic_key: createKeyPair ? publicKey || null : null,\n\t\t\t\t},\n\t\t\t\tiam: {\n\t\t\t\t\tenable: enableIam,\n\t\t\t\t\trole_name: enableIam ? `${id}-ec2-role` : null,\n\t\t\t\t},\n\t\t\t\ttags: {\n\t\t\t\t\tName: id,\n\t\t\t\t\tManagedBy: \"stack-infra\",\n\t\t\t\t},\n\t\t\t\tmachine: {\n\t\t\t\t\t...DEFAULT_EC2_MACHINE_META,\n\t\t\t\t\troles: roles ? roles.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttags: tags ? tags.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttarget_env: targetEnv || null,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait ec2.addApp(id, appConfig);\n\t\t\ttoast.success(`EC2 app \"${id}\" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add EC2 app\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tProvision EC2 Instances\n\t\t\t\t\t\n\t\t\t\t\t\tDefine EC2 instances to provision. Run infra:deploy after to create them.\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t{/* Presets */}\n\t\t\t\t\t{presets.length > 0 && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tPresets from your apps\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{presets.map((preset) => {\n\t\t\t\t\t\t\t\t\tconst Icon = preset.icon;\n\t\t\t\t\t\t\t\t\tconst isActive = appId === preset.appId;\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t applyPreset(preset)}\n\t\t\t\t\t\t\t\t\t\t\tclassName={`\n\t\t\t\t\t\t\t\t\t\t\t\tinline-flex items-center gap-2 rounded-lg border px-3 py-2\n\t\t\t\t\t\t\t\t\t\t\t\ttext-left text-sm transition-colors cursor-pointer\n\t\t\t\t\t\t\t\t\t\t\t\t${isActive\n\t\t\t\t\t\t\t\t\t\t\t\t\t? \"border-primary bg-primary/5 text-primary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"border-border bg-card text-foreground hover:border-primary/40 hover:bg-secondary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t
{preset.label}
\n\t\t\t\t\t\t\t\t\t\t\t\t
{preset.description}
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Identity */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setAppId(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tUnique identifier for this group of instances\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Instance sizing */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setInstanceCount(Number.parseInt(e.target.value, 10) || 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Networking */}\n\t\t\t\t\t
\n\t\t\t\t\t\tNetworking\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setVpcId(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setSubnetIds(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Associate a public IPv4 address

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Auto-create with SSH, HTTP, HTTPS ingress

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Storage */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRootVolumeSize(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* SSH Key */}\n\t\t\t\t\t
\n\t\t\t\t\t\tSSH Key Pair\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Import your public key to AWS

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{createKeyPair && (\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setPublicKey(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* IAM */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Create role with SSM + ECR access

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Machine metadata */}\n\t\t\t\t\t
\n\t\t\t\t\t\tMachine Metadata\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setRoles(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setTags(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [3:10] Identifier 'Badge' is imported but never used. -help: Consider removing this import. -ERROR [85:9] Variable 'description' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "import { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";", - "newString": "import { Button } from \"@ui/button\";" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [84:9] Variable 'description' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.7s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "\t\tconst description = (app.description as string) ?? appName;\n\n\t\t// Determine the kind of workload for sizing heuristics", - "newString": "\t\t// Determine the kind of workload for sizing heuristics" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 18.1s) - -Zero LSP errors in our files now. Let me do one final typecheck: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "cd apps/web && npx tsc --noEmit --pretty 2>&1 | rg \"deploy/\" | head -20", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Typecheck deploy components for errors" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 21.4s) - -Clean. Here's what was built: - -## Preset System - -The provisioning dialog now generates smart preset buttons from your configured apps. When you open "Provision EC2", you'll see buttons like: - -``` -[web] [docs] [database] -TanStack Start Vite Database Server -2x t3.small 1x t3.micro 1x t3.medium · 50GB -``` - -Clicking a preset fills in the entire form with sane defaults derived from your app's characteristics. - -### How presets are derived - -The `derivePresets()` function in `provision-instance-dialog.tsx:66` reads the user's apps from `useNixConfig()` and applies heuristics: - -| App characteristic | Defaults applied | -|---|---| -| **SSR framework** (TanStack Start, Next.js) | 2x `t3.small`, 20GB root, roles: `[appName, "web"]` | -| **API framework** (Hono, Go) | 2x `t3.micro`/`t3.small`, roles: `[appName, "api"]` | -| **Static site** (Vite, Astro) | 1x `t3.micro`, roles: `[appName, "web"]` | -| **Container app** (no framework detected) | 1x `t3.small`, 30GB root | -| **Generic** | 1x `t3.micro` | - -### Filtering logic - -- Apps already in `ec2.config.apps` are excluded (no duplicate presets) -- Apps deployed to non-EC2 hosts (Cloudflare, Fly) with deployment enabled are skipped -- A generic "Database Server" preset is always included (unless one named `database` exists) - -### UX - -- Preset buttons appear at the top of the dialog with icons and sizing summaries -- The active preset is highlighted with a primary border -- All fields remain editable after applying a preset -- it's just a starting point -- Networking, SSH, and IAM keep their defaults (auto VPC, create SG, enable IAM) - ---- - -## User - -does it make sense to have a module system for alchemy, similar to how we have a module system for apps? that way developers can contribute alchemy modules? - ---- - -## Assistant (Build · claude-opus-4-6 · 149.3s) - -_Thinking:_ - -The user is asking about creating a module system for Alchemy (the infrastructure provisioning framework), similar to how Stack has an app module system. Let me think about this carefully before responding. - -Currently, the infra module system already exists in `nix/stack/infra/` - there are 11 infra modules (aws-secrets, deployment, database, cache, machines, aws-network, aws-security-groups, aws-key-pairs, aws-iam, aws-ec2, aws-ec2-app). Each module has: -- A `module.nix` (Nix options + config) -- An `index.ts` (Alchemy TypeScript that provisions resources) - - -- Registration into `stack.infra.modules.` - -The codegen engine already handles auto-generating the orchestrator, merging dependencies, copying module files, and supporting both single-file and directory-based modules. So a module system does exist, but the real question is whether to formalize it further to make it easy for external developers to contribute Alchemy modules, similar to how devenv has community modules or Terraform has providers. - -Looking at what's already there versus what would need to change: the registry, typed inputs/outputs, and codegen all exist, but there's no discovery mechanism, standard package format, versioning, or testing infrastructure. The TypeScript modules are also tightly coupled to the codegen with specific import paths and the Infra class. The practical question is whether this level of formalization actually makes sense when the current system already lets people add modules by dropping files into the nix directory. - -The module interface itself is well-defined—each needs a Nix options file, TypeScript implementation, declared dependencies, and outputs—which is exactly what the system already supports. The real bottleneck isn't the architecture but how external modules get discovered and loaded. Rather than building something entirely new, the honest path forward is making the existing foundation more accessible to contributors. - -You already have one. The `stack.infra.modules` registry in `nix/stack/infra/options.nix` + the codegen engine in `codegen.nix` is a module system -- each module declares inputs, outputs, dependencies, and a TypeScript implementation, and the codegen wires them into a single deployable package. - -The 11 built-in modules (`aws-secrets`, `deployment`, `database`, `cache`, `machines`, `aws-network`, `aws-security-groups`, `aws-key-pairs`, `aws-iam`, `aws-ec2`, `aws-ec2-app`) all use this system already. - -What's missing is the **external** story. Right now a contributor would have to: - -1. Create a Nix module under `nix/stack/infra/modules//module.nix` -2. Create a TypeScript implementation in the same directory -3. Import it in `nix/stack/infra/default.nix` -4. Hope their module's dependencies don't conflict - -That works for first-party modules but not for community distribution. - -Before building more infrastructure around this, a few things worth considering: - -**What the current system handles well:** -- Input/output contracts between modules -- Dependency declaration (npm packages) -- Code generation (single-file and directory modules, as of our recent change) -- Output syncing to storage backends -- TypeScript type generation from Nix inputs - -**What's actually missing for community modules:** -- No way to load a module from a flake input or external path (modules must live inside the stack source tree) -- No way for users to register modules from their own `.stack/` config -- No isolation between module Alchemy resources (namespace collisions possible) - -**What I'd question building:** -- A package registry / discovery service (premature -- you'd need significant adoption first) -- Versioned module resolution (npm already handles the TypeScript dependency side) -- A module testing framework (each module is just an Alchemy script -- standard testing applies) - -``` -Tool: question - -**Input:** -```json -{ - "questions": [ - { - "question": "Which direction do you want to take this? The simplest high-value change would be letting users register external infra modules from their own config or flake inputs, without touching the stack source tree.", - "header": "Module system scope", - "options": [ - { - "label": "External module loading", - "description": "Let users register infra modules from flake inputs or local paths in their .stack/config.nix, so community modules can be consumed without forking stack" - }, - { - "label": "Module scaffold + docs", - "description": "Create a scaffold command (stack infra:new-module) and contributor docs that make it easy to write and share modules using the existing system" - }, - { - "label": "Both", - "description": "External loading + scaffold tooling + docs" - } - ] - } - ] -} -``` -**Output:** -``` -User has answered your questions: "Which direction do you want to take this? The simplest high-value change would be letting users register external infra modules from their own config or flake inputs, without touching the stack source tree."="Module scaffold + docs". You can now continue with the user's answers in mind. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.1s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Implement stack infra:new-module CLI command or script", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Implement stack infra:new-module CLI command or script", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.4s) - -Let me study the existing modules more closely to nail down the canonical shape, especially the simplest vs most complex examples: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/aws-key-pairs/module.nix -00003| # -00004| # AWS Key Pair provisioning module. -00005| # ============================================================================== -00006| { -00007| lib, -00008| config, -00009| ... -00010| }: -00011| let -00012| cfg = config.stack.infra.aws-key-pairs; -00013| -00014| keyType = lib.types.submodule { -00015| options = { -00016| public-key = lib.mkOption { -00017| type = lib.types.str; -00018| description = "Public key material for the key pair."; -00019| }; -00020| -00021| tags = lib.mkOption { -00022| type = lib.types.attrsOf lib.types.str; -00023| default = { }; -00024| description = "Tags applied to the key pair."; -00025| }; -00026| -00027| destroy-on-delete = lib.mkOption { -00028| type = lib.types.bool; -00029| default = false; -00030| description = "Delete the key pair when running alchemy destroy."; -00031| }; -00032| }; -00033| }; -00034| in -00035| { -00036| options.stack.infra.aws-key-pairs = { -00037| enable = lib.mkOption { -00038| type = lib.types.bool; -00039| default = false; -00040| description = "Enable AWS key pair provisioning."; -00041| }; -00042| -00043| keys = lib.mkOption { -00044| type = lib.types.attrsOf keyType; -00045| default = { }; -00046| description = "Key pair definitions keyed by key name."; -00047| }; -00048| -00049| sync-outputs = lib.mkOption { -00050| type = lib.types.listOf lib.types.str; -00051| default = [ "keyNames" "keyPairIds" ]; -00052| description = "Which outputs to sync to the storage backend."; -00053| }; -00054| }; -00055| -00056| config = lib.mkIf cfg.enable { -00057| stack.infra.enable = lib.mkDefault true; -00058| -00059| stack.infra.modules.aws-key-pairs = { -00060| name = "AWS Key Pairs"; -00061| description = "Import or create EC2 key pairs"; -00062| path = ./index.ts; -00063| inputs = { -00064| keys = cfg.keys; -00065| }; -00066| dependencies = { -00067| "@aws-sdk/client-ec2" = "catalog:"; -00068| }; -00069| outputs = -00070| let -00071| mkOutput = key: desc: { -00072| description = desc; -00073| sensitive = false; -00074| sync = builtins.elem key cfg.sync-outputs; -00075| }; -00076| in -00077| { -00078| keyNames = mkOutput "keyNames" "Key pair names (JSON)"; -00079| keyPairIds = mkOutput "keyPairIds" "Key pair IDs (JSON)"; -00080| }; -00081| }; -00082| }; -00083| } -00084| - -(End of file - total 84 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/index.ts" -} -``` -**Output:** -``` - -00001| // ============================================================================== -00002| // AWS Key Pairs Module -00003| // ============================================================================== -00004| import Infra from "@stack/infra"; -00005| import { KeyPair } from "@stack/infra/resources/key-pair"; -00006| -00007| interface KeyPairInput { -00008| publicKey: string; -00009| tags?: Record; -00010| destroyOnDelete?: boolean; -00011| } -00012| -00013| interface AwsKeyPairsInputs { -00014| keys: Record; -00015| } -00016| -00017| const infra = new Infra("aws-key-pairs"); -00018| const inputs = infra.inputs( -00019| process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES, -00020| ); -00021| -00022| const keyNames: string[] = []; -00023| const keyPairIds: Record = {}; -00024| -00025| for (const [keyName, key] of Object.entries(inputs.keys ?? {})) { -00026| const result = await KeyPair(infra.id(keyName), { -00027| keyName, -00028| publicKey: key.publicKey, -00029| tags: key.tags, -00030| destroyOnDelete: key.destroyOnDelete, -00031| }); -00032| keyNames.push(result.keyName); -00033| if (result.keyPairId) { -00034| keyPairIds[result.keyName] = result.keyPairId; -00035| } -00036| } -00037| -00038| export default { -00039| keyNames: JSON.stringify(keyNames), -00040| keyPairIds: JSON.stringify(keyPairIds), -00041| }; -00042| - -(End of file - total 42 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/cache/module.nix -00003| # -00004| # Cache Infrastructure Module -00005| # -00006| # Provisions cache/Redis resources based on environment: -00007| # - Production/CI: Upstash Redis (serverless, REST API) -00008| # - Local (devenv): Devenv-managed Redis (read-only reference) -00009| # - Local (docker): Docker Valkey/Redis container (fallback) -00010| # -00011| # Usage in .stack/config.nix: -00012| # stack.infra.cache = { -00013| # enable = true; -00014| # provider = "auto"; -00015| # upstash = { -00016| # region = "us-east-1"; -00017| # api-key-ssm-path = "/common/upstash-api-key"; -00018| # email-ssm-path = "/common/upstash-email"; -00019| # }; -00020| # }; -00021| # ============================================================================== -00022| { -00023| lib, -00024| config, -00025| ... -00026| }: -00027| let -00028| cfg = config.stack.infra.cache; -00029| projectName = config.stack.name or "my-project"; -00030| infraCfg = config.stack.infra; -00031| in -00032| { -00033| # ============================================================================ -00034| # Options -00035| # ============================================================================ -00036| options.stack.infra.cache = { -00037| enable = lib.mkOption { -00038| type = lib.types.bool; -00039| default = false; -00040| description = "Enable cache infrastructure provisioning"; -00041| }; -00042| -00043| provider = lib.mkOption { -00044| type = lib.types.enum [ -00045| "auto" -00046| "upstash" -00047| "devenv" -00048| "docker" -00049| ]; -00050| default = "auto"; -00051| description = '' -00052| Cache provider. -00053| -00054| - auto: Detect environment at runtime. Uses devenv if IN_NIX_SHELL is set, -00055| docker if USE_DOCKER=true, otherwise Upstash. -00056| - upstash: Always use Upstash Redis. -00057| - devenv: Always reference devenv-managed Redis. -00058| - docker: Always use Docker Valkey container. -00059| ''; -00060| }; -00061| -00062| # -------------------------------------------------------------------------- -00063| # Upstash configuration -00064| # -------------------------------------------------------------------------- -00065| upstash = { -00066| region = lib.mkOption { -00067| type = lib.types.str; -00068| default = "us-east-1"; -00069| description = "Upstash Redis region"; -00070| }; -00071| -00072| api-key-ssm-path = lib.mkOption { -00073| type = lib.types.str; -00074| default = "/common/upstash-api-key"; -00075| description = "SSM parameter path for the Upstash API key"; -00076| }; -00077| -00078| email-ssm-path = lib.mkOption { -00079| type = lib.types.str; -00080| default = "/common/upstash-email"; -00081| description = "SSM parameter path for the Upstash account email"; -00082| }; -00083| }; -00084| -00085| # -------------------------------------------------------------------------- -00086| # Devenv configuration -00087| # -------------------------------------------------------------------------- -00088| devenv = { -00089| host = lib.mkOption { -00090| type = lib.types.str; -00091| default = "localhost"; -00092| description = "Redis host in devenv"; -00093| }; -00094| -00095| port = lib.mkOption { -00096| type = lib.types.int; -00097| default = 6379; -00098| description = "Redis port in devenv"; -00099| }; -00100| }; -00101| -00102| # -------------------------------------------------------------------------- -00103| # Docker configuration -00104| # -------------------------------------------------------------------------- -00105| docker = { -00106| image = lib.mkOption { -00107| type = lib.types.str; -00108| default = "valkey/valkey"; -00109| description = "Docker image for Redis/Valkey"; -00110| }; -00111| -00112| tag = lib.mkOption { -00113| type = lib.types.str; -00114| default = "latest"; -00115| description = "Docker image tag"; -00116| }; -00117| -00118| port = lib.mkOption { -00119| type = lib.types.int; -00120| default = 6379; -00121| description = "Host port mapping for Docker Redis"; -00122| }; -00123| -00124| network = lib.mkOption { -00125| type = lib.types.str; -00126| default = "${projectName}_network"; -00127| description = "Docker network name"; -00128| }; -00129| }; -00130| -00131| # -------------------------------------------------------------------------- -00132| # Output configuration -00133| # -------------------------------------------------------------------------- -00134| ssm = { -00135| enable = lib.mkOption { -00136| type = lib.types.bool; -00137| default = true; -00138| description = "Write Redis URL and token to SSM Parameter Store"; -00139| }; -00140| -00141| path-prefix = lib.mkOption { -00142| type = lib.types.str; -00143| default = "/${projectName}"; -00144| description = "SSM path prefix (stage is appended)"; -00145| }; -00146| }; -00147| }; -00148| -00149| # ============================================================================ -00150| # Config: register in infra module system -00151| # ============================================================================ -00152| config = lib.mkIf (infraCfg.enable && cfg.enable) { -00153| stack.infra.modules.cache = { -00154| name = "Cache"; -00155| description = "Redis/Valkey cache provisioning (Upstash / devenv / Docker)"; -00156| path = ./index.ts; -00157| inputs = { -00158| inherit projectName; -00159| inherit (cfg) provider; -00160| upstash = { -00161| inherit (cfg.upstash) region; -00162| apiKeySsmPath = cfg.upstash.api-key-ssm-path; -00163| emailSsmPath = cfg.upstash.email-ssm-path; -00164| }; -00165| devenv = { -00166| inherit (cfg.devenv) host port; -00167| }; -00168| docker = { -00169| inherit (cfg.docker) image tag port network; -00170| }; -00171| ssm = { -00172| inherit (cfg.ssm) enable; -00173| pathPrefix = cfg.ssm.path-prefix; -00174| }; -00175| }; -00176| dependencies = { -00177| "alchemy" = "catalog:"; -00178| } -00179| // lib.optionalAttrs (config.stack.alchemy.enable) { -00180| ${config.stack.alchemy.package.name} = "workspace:*"; -00181| }; -00182| outputs = { -00183| redisUrl = { -00184| description = "Redis connection URL (endpoint)"; -00185| sync = true; -00186| }; -00187| redisToken = { -00188| description = "Redis REST token (Upstash only)"; -00189| sensitive = true; -00190| sync = true; -00191| }; -00192| provider = { -00193| description = "Active cache provider (upstash, devenv, docker)"; -00194| sync = true; -00195| }; -00196| }; -00197| }; -00198| }; -00199| } -00200| - -(End of file - total 200 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.2s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix", - "offset": 1, - "limit": 120 -} -``` -**Output:** -``` - -00002| # infra/options.nix -00003| # -00004| # Core options for the infrastructure module system. -00005| # -00006| # Defines: -00007| # - stack.infra.enable, framework, output-dir, key-format -00008| # - stack.infra.storage-backend (chamber, sops, ssm, none) -00009| # - stack.infra.modules (internal registry) -00010| # - stack.infra.package (generated package.json config) -00011| # - stack.infra.outputs (stub for cross-resource references) -00012| # ============================================================================== -00013| { -00014| lib, -00015| config, -00016| ... -00017| }: -00018| let -00019| cfg = config.stack.infra; -00020| projectName = config.stack.name or "my-project"; -00021| -00022| # ============================================================================ -00023| # Alchemy peer dependency mapping -00024| # -00025| # Maps alchemy resource imports to their required npm peer dependencies. -00026| # Used for validation to ensure all required dependencies are declared. -00027| # ============================================================================ -00028| -00029| # ============================================================================ -00030| # Submodule: output declaration (used in module registry) -00031| # ============================================================================ -00032| outputDeclType = lib.types.submodule { -00033| options = { -00034| description = lib.mkOption { -00035| type = lib.types.str; -00036| default = ""; -00037| description = "Human-readable description of this output"; -00038| }; -00039| -00040| sensitive = lib.mkOption { -00041| type = lib.types.bool; -00042| default = false; -00043| description = "Whether this output contains sensitive data"; -00044| }; -00045| -00046| sync = lib.mkOption { -00047| type = lib.types.bool; -00048| default = false; -00049| description = "Whether to sync this output to the storage backend"; -00050| }; -00051| }; -00052| }; -00053| -00054| # ============================================================================ -00055| # Submodule: infra module registry entry -00056| # ============================================================================ -00057| infraModuleType = lib.types.submodule { -00058| options = { -00059| name = lib.mkOption { -00060| type = lib.types.str; -00061| description = "Human-readable name of this infra module"; -00062| }; -00063| -00064| description = lib.mkOption { -00065| type = lib.types.str; -00066| default = ""; -00067| description = "Description of what this module provisions"; -00068| }; -00069| -00070| path = lib.mkOption { -00071| type = lib.types.path; -00072| description = '' -00073| Path to the TypeScript module file. -00074| Must default-export a Record of outputs. -00075| Use `import Infra from "@stack/infra"` for the library. -00076| ''; -00077| }; -00078| -00079| inputs = lib.mkOption { -00080| type = lib.types.attrsOf lib.types.anything; -00081| default = { }; -00082| description = '' -00083| Configuration values passed to the module at runtime. -00084| Serialized to JSON in .stack/state/infra-inputs.json. -00085| Values matching ENC[age,...] are decrypted at runtime. -00086| ''; -00087| }; -00088| -00089| dependencies = lib.mkOption { -00090| type = lib.types.attrsOf lib.types.str; -00091| default = { }; -00092| description = '' -00093| NPM dependencies this module requires. -00094| Merged into the infra package.json dependencies. -00095| -00096| For external flake modules, prefer providing bunDeps instead -00097| for pre-validated, reproducible dependencies. -00098| ''; -00099| }; -00100| -00101| bunDeps = lib.mkOption { -00102| type = lib.types.nullOr lib.types.package; -00103| default = null; -00104| description = '' -00105| Pre-fetched Bun dependency cache from bun2nix.fetchBunDeps. -00106| -00107| External flakes should provide this for reproducible builds. -00108| When provided, these deps are validated at Nix eval time and -00109| merged with other modules' deps. -00110| -00111| Example (in a flake providing an infra module): -00112| bunDeps = bun2nix.fetchBunDeps { -00113| bunNix = ./bun.nix; -00114| }; -00115| -00116| The flake should also ship package.json, bun.lock, and bun.nix -00117| alongside the module. -00118| ''; -00119| }; -00120| -00121| outputs = lib.mkOption { - -(File has more lines. Use 'offset' parameter to read beyond line 121) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix", - "offset": 121, - "limit": 100 -} -``` -**Output:** -``` - -00122| type = lib.types.attrsOf outputDeclType; -00123| default = { }; -00124| description = '' -00125| Output declarations for this module. -00126| Keys must match the keys of the default export from the TypeScript file. -00127| Only outputs with sync=true are written to the storage backend. -00128| ''; -00129| }; -00130| }; -00131| }; -00132| -00133| in -00134| { -00135| # ============================================================================ -00136| # Options -00137| # ============================================================================ -00138| options.stack.infra = { -00139| enable = lib.mkOption { -00140| type = lib.types.bool; -00141| default = false; -00142| description = "Enable the infrastructure module system"; -00143| }; -00144| -00145| framework = lib.mkOption { -00146| type = lib.types.enum [ "alchemy" ]; -00147| default = "alchemy"; -00148| description = '' -00149| IaC framework to use for infrastructure provisioning. -00150| Currently only "alchemy" is supported. The alchemy module at -00151| config.stack.alchemy provides the shared SDK configuration -00152| (version, state store, helpers) that this module consumes. -00153| ''; -00154| }; -00155| -00156| output-dir = lib.mkOption { -00157| type = lib.types.str; -00158| default = "packages/infra"; -00159| description = "Directory for generated infrastructure files (relative to project root)"; -00160| }; -00161| -00162| key-format = lib.mkOption { -00163| type = lib.types.str; -00164| default = "$module-$key"; -00165| description = '' -00166| Template for output storage keys. -00167| Variables: $module (module ID), $key (output key name). -00168| Example: "$module-$key" -> "aws-secrets-roleArn" -00169| ''; -00170| }; -00171| -00172| # ========================================================================== -00173| # Storage backend for persisting outputs -00174| # ========================================================================== -00175| storage-backend = { -00176| type = lib.mkOption { -00177| type = lib.types.enum [ -00178| "chamber" -00179| "sops" -00180| "ssm" -00181| "none" -00182| ]; -00183| default = "none"; -00184| description = "Storage backend for persisting infrastructure outputs"; -00185| }; -00186| -00187| chamber = { -00188| service = lib.mkOption { -00189| type = lib.types.str; -00190| default = ""; -00191| description = '' -00192| Chamber service name for output storage. -00193| Outputs are written as: chamber write -- -00194| ''; -00195| }; -00196| }; -00197| -00198| sops = { -00199| file-path = lib.mkOption { -00200| type = lib.types.str; -00201| default = ".stack/secrets/vars/dev.sops.yaml"; -00202| description = '' -00203| Path to SOPS-encrypted YAML file for infra outputs. -00204| Defaults to the dev group file. Uses `sops set` for non-destructive -00205| per-key updates, preserving existing secrets in the file. -00206| ''; -00207| }; -00208| -00209| group = lib.mkOption { -00210| type = lib.types.str; -00211| default = "dev"; -00212| description = '' -00213| Secrets group to write outputs to (e.g., "dev", "prod", "common"). -00214| Used to resolve the SOPS file path from the secrets directory: -00215| /vars/.sops.yaml -00216| When set, overrides file-path. -00217| ''; -00218| }; -00219| }; -00220| -00221| ssm = { - -(File has more lines. Use 'offset' parameter to read beyond line 221) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.7s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -Now I have a clear picture of the canonical module shape. Let me check where the scripts are registered to understand where to add the scaffold script: - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "infra:deploy|infra:destroy|infra:dev|infra:pull", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix" -} -``` -**Output:** -``` -Found 7 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 18: # - Registers scripts (infra:deploy, infra:destroy, etc.) - Line 691: "infra:deploy" = { - Line 708: "infra:destroy" = { - Line 725: "infra:dev" = { - Line 738: "infra:pull-outputs" = { - Line 925: name = "infra:deploy"; - Line 929: name = "infra:pull-outputs"; -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 691, - "limit": 80 -} -``` -**Output:** -``` - -00692| exec = '' -00693| cd "${outputDir}" && bunx alchemy deploy "$@" -00694| ''; -00695| description = "Deploy infrastructure via alchemy"; -00696| args = [ -00697| { -00698| name = "--stage"; -00699| description = "Deployment stage (e.g., dev, prod)"; -00700| } -00701| { -00702| name = "..."; -00703| description = "Additional alchemy deploy arguments"; -00704| } -00705| ]; -00706| }; -00707| -00708| "infra:destroy" = { -00709| exec = '' -00710| cd "${outputDir}" && bunx alchemy destroy "$@" -00711| ''; -00712| description = "Destroy infrastructure via alchemy"; -00713| args = [ -00714| { -00715| name = "--stage"; -00716| description = "Deployment stage to destroy"; -00717| } -00718| { -00719| name = "..."; -00720| description = "Additional alchemy destroy arguments"; -00721| } -00722| ]; -00723| }; -00724| -00725| "infra:dev" = { -00726| exec = '' -00727| cd "${outputDir}" && bunx alchemy dev "$@" -00728| ''; -00729| description = "Start infrastructure dev mode"; -00730| args = [ -00731| { -00732| name = "..."; -00733| description = "Additional alchemy dev arguments"; -00734| } -00735| ]; -00736| }; -00737| -00738| "infra:pull-outputs" = { -00739| exec = -00740| let -00741| storageType = cfg.storage-backend.type; -00742| dataDir = config.stack.dirs.data; -00743| outputsFile = "${dataDir}/infra-outputs.nix"; -00744| in -00745| if storageType == "chamber" then -00746| '' -00747| echo "Pulling outputs from chamber..." -00748| SERVICE="${cfg.storage-backend.chamber.service}" -00749| OUTPUT_FILE="${outputsFile}" -00750| -00751| echo "{" > "$OUTPUT_FILE" -00752| ${lib.concatMapStringsSep "\n" ( -00753| id: -00754| let -00755| mod = cfg.modules.${id}; -00756| syncKeys = builtins.filter (k: (mod.outputs.${k}.sync or false)) (builtins.attrNames mod.outputs); -00757| in -00758| '' -00759| echo ' ${id} = {' >> "$OUTPUT_FILE" -00760| ${lib.concatMapStringsSep "\n" ( -00761| key: -00762| let -00763| formattedKey = builtins.replaceStrings [ "$module" "$key" ] [ id key ] cfg.key-format; -00764| in -00765| '' -00766| VALUE=$(chamber read -q "$SERVICE" "${formattedKey}" 2>/dev/null || echo "") -00767| if [ -n "$VALUE" ]; then -00768| echo ' ${key} = "'"$VALUE"'";' >> "$OUTPUT_FILE" -00769| fi -00770| '' -00771| ) syncKeys} - -(File has more lines. Use 'offset' parameter to read beyond line 771) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -Good. Now let me create the scaffold script and template files. The scaffold will create the two files a module needs -- `module.nix` and `index.ts` -- with a working skeleton: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 685, - "limit": 10 -} -``` -**Output:** -``` - -00686| -00687| # ========================================================================== -00688| # Shell scripts -00689| # ========================================================================== -00690| stack.scripts = { -00691| "infra:deploy" = { -00692| exec = '' -00693| cd "${outputDir}" && bunx alchemy deploy "$@" -00694| ''; -00695| description = "Deploy infrastructure via alchemy"; - -(File has more lines. Use 'offset' parameter to read beyond line 695) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 33.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "oldString": " \"infra:deploy\" = {\n exec = ''\n cd \"${outputDir}\" && bunx alchemy deploy \"$@\"\n '';\n description = \"Deploy infrastructure via alchemy\";", - "newString": " \"infra:new-module\" = {\n exec = ''\n MODULE_ID=\"''${1:?Usage: infra:new-module }\"\n\n # Validate module ID\n if ! echo \"$MODULE_ID\" | grep -qE '^[a-z][a-z0-9-]*$'; then\n echo \"Error: Module ID must be lowercase alphanumeric with hyphens (e.g., my-module)\"\n exit 1\n fi\n\n MODULE_DIR=\"nix/stack/infra/modules/$MODULE_ID\"\n if [ -d \"$MODULE_DIR\" ]; then\n echo \"Error: Module directory $MODULE_DIR already exists\"\n exit 1\n fi\n\n MODULE_NAME=$(echo \"$MODULE_ID\" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)} 1')\n\n echo \"Creating infra module: $MODULE_ID ($MODULE_NAME)\"\n mkdir -p \"$MODULE_DIR\"\n\n # Generate module.nix\n cat > \"$MODULE_DIR/module.nix\" << 'NIXEOF'\n # ==============================================================================\n # infra/modules/MODULE_ID/module.nix\n # ==============================================================================\n {\n lib,\n config,\n ...\n }:\n let\n cfg = config.stack.infra.MODULE_ID;\n in\n {\n options.stack.infra.MODULE_ID = {\n enable = lib.mkOption {\n type = lib.types.bool;\n default = false;\n description = \"Enable MODULE_NAME provisioning.\";\n };\n\n # Add your module options here. Example:\n # region = lib.mkOption {\n # type = lib.types.str;\n # default = \"us-east-1\";\n # description = \"AWS region.\";\n # };\n };\n\n config = lib.mkIf cfg.enable {\n stack.infra.enable = lib.mkDefault true;\n\n stack.infra.modules.MODULE_ID = {\n name = \"MODULE_NAME\";\n description = \"MODULE_NAME infrastructure provisioning\";\n path = ./index.ts;\n\n # Nix values serialized to JSON and available at runtime\n # via infra.inputs() in the TypeScript file.\n inputs = {\n # Add your input values here. Example:\n # region = cfg.region;\n };\n\n # NPM packages required by the TypeScript implementation.\n dependencies = {\n # \"@aws-sdk/client-s3\" = \"catalog:\";\n };\n\n # Outputs the TypeScript module's default export must provide.\n # Keys with sync=true are written to the storage backend.\n outputs = {\n # exampleArn = {\n # description = \"ARN of the provisioned resource\";\n # sensitive = false;\n # sync = true;\n # };\n };\n };\n };\n }\n NIXEOF\n\n # Replace placeholders\n sed -i \"\" \"s/MODULE_ID/$MODULE_ID/g\" \"$MODULE_DIR/module.nix\"\n sed -i \"\" \"s/MODULE_NAME/$MODULE_NAME/g\" \"$MODULE_DIR/module.nix\"\n\n # Generate index.ts\n cat > \"$MODULE_DIR/index.ts\" << 'TSEOF'\n // ==============================================================================\n // MODULE_NAME Infra Module\n //\n // This module is executed by `infra:deploy` via Alchemy.\n //\n // The default export must be a Record matching the output\n // keys declared in module.nix.\n // ==============================================================================\n import Infra from \"@stack/infra\";\n\n // Typed inputs — define an interface matching your module.nix inputs.\n interface Inputs {\n // region: string;\n }\n\n const infra = new Infra(\"MODULE_ID\");\n const inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n );\n\n // ---------------------------------------------------------------------------\n // Provision resources here.\n //\n // Use Alchemy resources or AWS SDK clients:\n //\n // import { SomeResource } from \"@stack/infra/resources/some-resource\";\n // const resource = await SomeResource(infra.id(\"my-resource\"), { ... });\n //\n // Or use alchemy built-in resources:\n //\n // import { Role } from \"alchemy/aws\";\n // const role = await Role(infra.id(\"role\"), { roleName: \"...\", ... });\n //\n // ---------------------------------------------------------------------------\n\n // Export outputs matching the keys in module.nix outputs.\n export default {\n // exampleArn: resource.arn,\n };\n TSEOF\n\n sed -i \"\" \"s/MODULE_ID/$MODULE_ID/g\" \"$MODULE_DIR/index.ts\"\n sed -i \"\" \"s/MODULE_NAME/$MODULE_NAME/g\" \"$MODULE_DIR/index.ts\"\n\n # Trim leading whitespace from heredoc indentation\n sed -i \"\" 's/^ //' \"$MODULE_DIR/module.nix\"\n sed -i \"\" 's/^ //' \"$MODULE_DIR/index.ts\"\n\n echo \"\"\n echo \"Created:\"\n echo \" $MODULE_DIR/module.nix — Nix options and module registration\"\n echo \" $MODULE_DIR/index.ts — TypeScript Alchemy implementation\"\n echo \"\"\n echo \"Next steps:\"\n echo \" 1. Add your options to module.nix\"\n echo \" 2. Implement provisioning logic in index.ts\"\n echo \" 3. Import the module in nix/stack/infra/default.nix:\"\n echo \" ./modules/$MODULE_ID/module.nix\"\n echo \" 4. Enable in .stack/config.nix:\"\n echo \" stack.infra.$MODULE_ID.enable = true;\"\n echo \" 5. Run: infra:deploy\"\n '';\n description = \"Scaffold a new infra module\";\n args = [\n {\n name = \"\";\n description = \"Module identifier (lowercase, hyphens, e.g., my-s3-buckets)\";\n }\n ];\n };\n\n \"infra:deploy\" = {\n exec = ''\n cd \"${outputDir}\" && bunx alchemy deploy \"$@\"\n '';\n description = \"Deploy infrastructure via alchemy\";" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 57.7s) - -Now write the docs: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/infra-modules.mdx", - "content": "---\ntitle: Writing Infra Modules\ndescription: Create custom infrastructure modules for the Alchemy-based provisioning system\nicon: puzzle\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel's infrastructure system is modular. Each infra module is a pair of files -- a Nix module that declares options and a TypeScript file that provisions resources via Alchemy. You can write your own modules to provision any cloud resource.\n\n## Module Anatomy\n\nEvery infra module has exactly two files:\n\n```\nnix/stack/infra/modules//\n module.nix # Nix options + module registration\n index.ts # TypeScript Alchemy implementation\n```\n\n**`module.nix`** declares:\n- Options under `stack.infra.` (what users configure)\n- Registration in `stack.infra.modules.` with:\n - `name` / `description` -- metadata\n - `path` -- pointer to the TypeScript file\n - `inputs` -- Nix values serialized to JSON for the TypeScript runtime\n - `dependencies` -- NPM packages the TypeScript file needs\n - `outputs` -- what the module produces (ARNs, URLs, etc.)\n\n**`index.ts`** implements:\n- Reads inputs via `infra.inputs()`\n- Provisions resources using Alchemy resources or AWS SDK clients\n- Default-exports a `Record` matching the output keys declared in `module.nix`\n\n## Scaffold a Module\n\nThe fastest way to start:\n\n```bash\ninfra:new-module my-s3-buckets\n```\n\nThis creates `nix/stack/infra/modules/my-s3-buckets/` with a working `module.nix` and `index.ts` skeleton. Then:\n\n\n\n\n### Add options\n\nEdit `module.nix` to declare what users configure:\n\n```nix\noptions.stack.infra.my-s3-buckets = {\n enable = lib.mkOption {\n type = lib.types.bool;\n default = false;\n description = \"Enable S3 bucket provisioning.\";\n };\n\n buckets = lib.mkOption {\n type = lib.types.attrsOf (lib.types.submodule {\n options = {\n region = lib.mkOption {\n type = lib.types.str;\n default = \"us-east-1\";\n };\n versioning = lib.mkOption {\n type = lib.types.bool;\n default = true;\n };\n };\n });\n default = {};\n description = \"S3 buckets to create.\";\n };\n};\n```\n\n\n\n\n### Wire inputs and outputs\n\nIn the `config` block of `module.nix`, pass Nix values to the TypeScript runtime and declare what the module produces:\n\n```nix\nconfig = lib.mkIf cfg.enable {\n stack.infra.enable = lib.mkDefault true;\n\n stack.infra.modules.my-s3-buckets = {\n name = \"S3 Buckets\";\n description = \"Provision S3 buckets with versioning\";\n path = ./index.ts;\n\n inputs = {\n buckets = cfg.buckets;\n };\n\n dependencies = {\n \"@aws-sdk/client-s3\" = \"catalog:\";\n };\n\n outputs = {\n bucketArns = {\n description = \"Bucket ARNs (JSON)\";\n sync = true;\n };\n };\n };\n};\n```\n\n\n\n\n### Implement provisioning\n\nEdit `index.ts` to provision resources:\n\n```typescript\nimport Infra from \"@stack/infra\";\n\ninterface BucketInput {\n region: string;\n versioning: boolean;\n}\n\ninterface Inputs {\n buckets: Record;\n}\n\nconst infra = new Infra(\"my-s3-buckets\");\nconst inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n);\n\nconst {\n S3Client,\n CreateBucketCommand,\n PutBucketVersioningCommand,\n} = await import(\"@aws-sdk/client-s3\");\n\nconst bucketArns: Record = {};\n\nfor (const [name, bucket] of Object.entries(inputs.buckets)) {\n const client = new S3Client({ region: bucket.region });\n\n try {\n await client.send(new CreateBucketCommand({ Bucket: name }));\n } catch (err: any) {\n if (err.name !== \"BucketAlreadyOwnedByYou\") throw err;\n }\n\n if (bucket.versioning) {\n await client.send(new PutBucketVersioningCommand({\n Bucket: name,\n VersioningConfiguration: { Status: \"Enabled\" },\n }));\n }\n\n bucketArns[name] = `arn:aws:s3:::${name}`;\n}\n\nexport default {\n bucketArns: JSON.stringify(bucketArns),\n};\n```\n\n\n\n\n### Register the module\n\nAdd the import to `nix/stack/infra/default.nix`:\n\n```nix\nimports = [\n # ... existing modules\n ./modules/my-s3-buckets/module.nix\n];\n```\n\n\n\n\n### Enable and deploy\n\nIn `.stack/config.nix`:\n\n```nix\nstackpanel.infra.my-s3-buckets = {\n enable = true;\n buckets = {\n \"myapp-uploads\" = {\n region = \"us-west-2\";\n versioning = true;\n };\n \"myapp-backups\" = {\n region = \"us-west-2\";\n versioning = false;\n };\n };\n};\n```\n\nThen deploy:\n\n```bash\ninfra:deploy\n```\n\n\n\n\n## Module Contract\n\n### Inputs\n\nInputs are Nix values that get serialized to JSON. The TypeScript file reads them at runtime:\n\n```typescript\nconst infra = new Infra(\"my-module\");\nconst inputs = infra.inputs(\n process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES,\n);\n```\n\n**Key-case transformation**: Nix uses `kebab-case`, TypeScript receives `camelCase`. The codegen handles the conversion automatically. So `api-key-ssm-path` in Nix becomes `apiKeySsmPath` in TypeScript.\n\n### Outputs\n\nThe TypeScript file's default export must be a `Record` where keys match the `outputs` declared in `module.nix`:\n\n```typescript\n// module.nix declares: outputs.bucketArns, outputs.roleArn\nexport default {\n bucketArns: JSON.stringify(arns), // complex values as JSON strings\n roleArn: role.arn, // simple strings directly\n};\n```\n\nOutputs with `sync = true` are written to the storage backend (SOPS, SSM, or Chamber) by the Alchemy orchestrator after all modules complete.\n\nOutputs with `sensitive = true` are marked for encryption in the storage backend.\n\n### Dependencies\n\nNPM dependencies are declared in `module.nix` and merged into the generated `packages/infra/package.json`:\n\n```nix\ndependencies = {\n \"@aws-sdk/client-s3\" = \"catalog:\"; # from bun catalog\n \"some-package\" = \"^1.2.3\"; # pinned version\n};\n```\n\n### Using Alchemy Resources\n\nFor common AWS resources, use the custom Alchemy resources in `@stack/infra/resources/`:\n\n```typescript\nimport { IamRole } from \"@stack/infra/resources/iam-role\";\nimport { KmsKey } from \"@stack/infra/resources/kms-key\";\nimport { Ec2Instance } from \"@stack/infra/resources/ec2-instance\";\nimport { SecurityGroup } from \"@stack/infra/resources/security-group\";\n```\n\nThese resources are adopt-safe (they detect and import existing resources rather than failing).\n\nYou can also use Alchemy's built-in resources:\n\n```typescript\nimport { Role } from \"alchemy/aws\";\nimport { GitHubOIDCProvider } from \"alchemy/aws/oidc\";\n```\n\n### Resource IDs\n\nUse `infra.id()` to generate namespaced resource IDs that avoid collisions between modules:\n\n```typescript\nconst role = await IamRole(infra.id(\"role\"), { ... });\nconst key = await KmsKey(infra.id(\"kms\"), { ... });\n```\n\nThis produces IDs like `my-module/role` and `my-module/kms`.\n\n## Directory Modules\n\nFor complex modules, split the TypeScript into multiple files by using a directory:\n\n```\nnix/stack/infra/modules/my-module/\n module.nix # set path = ./impl;\n impl/\n index.ts # main entry point (default export)\n policies.ts # helper functions\n constants.ts # shared constants\n```\n\nIn `module.nix`, set `path = ./impl;` (pointing to the directory). The codegen copies all `*.ts` files from the directory to `packages/infra/modules/my-module/`.\n\nImport between files uses standard relative imports:\n\n```typescript\n// index.ts\nimport { buildPolicy } from \"./policies\";\n```\n\n## Existing Modules Reference\n\nStudy these modules as examples:\n\n| Module | Complexity | Good example of |\n|--------|-----------|-----------------|\n| `aws-key-pairs` | Simple (84 lines Nix, 42 lines TS) | Minimal module, loop over inputs |\n| `aws-security-groups` | Simple (145 lines Nix, 69 lines TS) | Nested submodule types |\n| `aws-iam` | Medium (128 lines Nix, 71 lines TS) | IAM role + instance profile |\n| `cache` | Medium (200 lines Nix, variable TS) | Multi-provider pattern |\n| `aws-secrets` | Medium (directory module) | Split into policies.ts + index.ts |\n| `aws-ec2-app` | Complex (918 lines Nix, 962 lines TS) | Per-app loop, auto-discovery, multiple resource types |\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `infra:new-module ` | Scaffold a new module with `module.nix` + `index.ts` |\n| `infra:deploy` | Deploy all enabled modules |\n| `infra:destroy` | Remove provisioned resources |\n| `infra:pull-outputs` | Pull outputs from storage backend |\n\n## Tips\n\n- **Start small**: Begin with a single resource type and add complexity later\n- **Use adopt patterns**: Check if a resource exists before creating it, especially for IAM roles and KMS keys\n- **JSON-encode complex outputs**: Outputs must be strings. Use `JSON.stringify()` for arrays and objects\n- **Test locally**: Run `infra:deploy --stage dev-$USER` to test without affecting shared infrastructure\n- **Check the generated code**: Look at `packages/infra/modules/.ts` to verify the codegen output matches your source\n\n## Reference\n\n- [Infrastructure](/docs/deployment/infrastructure) for the infra system overview\n- [Machines](/docs/deployment/machines) for machine inventory\n- [Deployment Overview](/docs/deployment) for all deployment options\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.3s) - -Now update the meta.json and deployment index to include the new page: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}", - "newString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"infra-modules\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}" -} -``` -**Error:** -``` -Error: File /Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json has been modified since it was last read. -Last modification: 2026-03-01T13:40:54.298Z -Last read: 2026-03-01T13:09:36.853Z - -Please read the file again before modifying it. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.1s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json" -} -``` -**Output:** -``` - -00001| { -00002| "title": "Containers & Deployment", -00003| "description": "Build containers and deploy to production", -00004| "icon": "cloud-upload", -00005| "pages": ["index", "infrastructure", "machines", "cloudflare", "fly", "containers", "sst"] -00006| } -00007| - -(End of file - total 7 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "\"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]", - "newString": "\"pages\": [\"index\", \"infrastructure\", \"machines\", \"infra-modules\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.4s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 22, - "limit": 22 -} -``` -**Output:** -``` - -00023| -00024| ## What Runs Where -00025| -00026| | Host | Provisioning path | Notes | -00027| |------|-------------------|-------| -00028| | `cloudflare` | `infra:deploy` (Alchemy infra module) | Framework × host maps to a Cloudflare Alchemy resource | -00029| | `fly` | Fly module workflow (`flyctl`, generated `fly.toml`) | Container-based deploy flow; not managed by infra deployment module | -00030| -00031| -00032| The infra deployment module currently provisions Cloudflare apps only. Fly apps are handled by the Fly deployment module. -00033| -00034| -00035| ## Minimal Example -00036| -00037| ```nix -00038| { -00039| stack.deployment.defaultHost = "cloudflare"; -00040| -00041| stack.apps.web = { -00042| path = "apps/web"; -00043| framework.tanstack-start.enable = true; -00044| deployment = { - -(File has more lines. Use 'offset' parameter to read beyond line 44) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 1, - "limit": 22 -} -``` -**Output:** -``` - -00002| title: Containers & Deployment -00003| description: Host-aware deployment for Cloudflare and Fly.io -00004| icon: cloud -00005| --- -00006| -00007| import { Cards, Card } from "fumadocs-ui/components/card"; -00008| import { Callout } from "fumadocs-ui/components/callout"; -00009| -00010| Stack deployment is app-first: each app opts into deployment, chooses a host, and then host-specific modules generate the files/scripts needed for that host. -00011| -00012| ## Routing Model -00013| -00014| Deployment selection is resolved in this order: -00015| -00016| 1. `stack.apps..deployment.host` (explicit per app) -00017| 2. `stack.deployment.defaultHost` (global fallback, default: `"cloudflare"`) -00018| -00019| An app is deployable when: -00020| -00021| - `stack.apps..deployment.enable = true` -00022| - one framework flag is enabled (for example `framework.tanstack-start.enable = true`) -00023| - -(File has more lines. Use 'offset' parameter to read beyond line 23) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.0s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.1s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "Cards\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -No files found -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.3s) - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "Cards|Card title\"", - "path": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` -Found 3 matches -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx: - Line 7: import { Cards, Card } from "fumadocs-ui/components/card"; - Line 71: - Line 84: -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "offset": 68, - "limit": 20 -} -``` -**Output:** -``` - -00069| ## Provider Guides -00070| -00071| -00072| -00073| Configure per-app Cloudflare options and deploy with `infra:deploy` -00074| -00075| -00076| Generate `fly.toml`, use wrapped `fly-` commands, and deploy containers -00077| -00078| -00079| Understand infra modules, Alchemy generation, stages, and output sync -00080| -00081| -00082| Define machine inventory for Colmena and infrastructure-aware workflows -00083| -00084| -00085| - -(End of file - total 85 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.2s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": " \n Define machine inventory for Colmena and infrastructure-aware workflows\n \n", - "newString": " \n Define machine inventory for Colmena and infrastructure-aware workflows\n \n \n Scaffold and develop custom Alchemy modules for the infra system\n \n" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 78.5s) - -Now let me verify the scaffold script works: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c bash -lc 'infra:new-module test-example'", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 120000, - "description": "Test scaffold command generates module files" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/jlmnkmhk3xg3vngj90npf497qn5cq7f0-vendor-env.drv'... -building '/nix/store/fim5b2b3kas7a2b4msa7z5yl8j3pmxdn-go-cache-env.drv'... -building '/nix/store/0c542jravs13kkyiial58nwpcpgsp0dx-stack-0.1.0.drv'... -building '/nix/store/0lg9wabl1716sdzdvw95gawh8qi5ksx6-infra-new-module.drv'... -building '/nix/store/2rad2l3c9wlw17mzz68ifbiaawqkyr1q-stack-scripts.drv'... -building '/nix/store/9j9dkjgyfjr5n89vd74g8l46qs3hrr0l-stack-profile.drv'... -building '/nix/store/hdylk86pwhx3pnndq2gx52vgr1n4jyba-stack-generate-bin.drv'... -building '/nix/store/38j99v4bji5kmvx3vp9ydm0m45v6425l-stack-gc-update.drv'... -building '/nix/store/g25yfzmjlgs6ywwj1gcb4xldp1b64nzb-stack-shellhook.drv'... -building '/nix/store/yq8ad3cgkcqkqyd9xb5v0pr1sp97vsqb-stack-stack-env.drv'... -Config validated for stack -✓ Stack initialized -warning: unknown experimental feature 'build-time-fetch-tree' -warning: unknown experimental feature 'parallel-eval' -warning: unknown experimental feature 'external-builders' -warning: unknown setting 'eval-cores' -warning: unknown setting 'external-builders' -warning: unknown setting 'lazy-trees' -Checking if CA is reachable... OK -Checking if root cert is installed... OK -Checking if fingerprint matches... OK -Checking if device certificate exists... OK -Checking if device certificate is valid... OK - -All checks passed! -📜 stack scripts loaded - remove packages/infra/modules/deployment.ts (stale) - write .gitignore (block, updated) - write .stack/state/infra-inputs.json - write .vscode/settings.json - write .zed/settings.json - write packages/infra/README.md - write packages/infra/alchemy.run.ts - write packages/infra/modules/aws-ec2-app.ts - write packages/infra/package.json - write packages/infra/src/types.ts -files: 9 written, 69 unchanged, 1 removed -Stack development environment - -📦 Stack Ports (project: stack) - Stable port: 6400 - - Apps: - docs: 5738 -> https://docs.stack.lan - stack-go: 5797 - web: 5775 -> https://stack.stack.lan - - Services: - Minio: 6498 - Minio Console: 6436 - PostgreSQL: 6404 - Redis: 6494 - - Tip: Set STACKPANEL_QUIET=1 to hide this message - - → .stack/bin: 220 binaries -Caddy config directories created at /Users/cm/.config/caddy -Site configuration saved to /Users/cm/.config/caddy/sites.d/stackpanel_docs_stackpanel_lan.caddy - -Run 'caddy-start' or 'caddy-restart' to apply changes -Caddy config directories created at /Users/cm/.config/caddy -Site configuration saved to /Users/cm/.config/caddy/sites.d/stackpanel_stackpanel_stackpanel_lan.caddy - -Run 'caddy-start' or 'caddy-restart' to apply changes -✅ Stack Nix module system initialized -stack core initialized - | | | - __| __| _' | __| | / __ \ _' | __ \ _ \ | - \__ \ | ( | ( < | | ( | | | __/ | - ____/ \__| \__,_| \___| _|\_\ .__/ \__,_| _| _| \___| _| - _| - -╭────────────────────────────────────────────────────────────────────────╮ -│ │ -│ stackpanel Shell │ -│ Your reproducible development environment is ready │ -│ │ -│ ───��──────────────────────────────────────────────────────────────── │ -│ Status │ -│ Agent ● localhost:9876 │ -│ Services ○ minio ○ minio_console ○ postgres ○ redis │ -│ Health ███████░░░ 22/28 │ -│ ● aws-roles-anywhere 3/3 │ -│ ● deployment-fly 2/2 │ -│ ● docker 1/1 │ -│ ● env-codegen 2/2 │ -│ ● go 3/3 │ -│ ● oxlint 2/3 │ -│ ● secrets 5/10 │ -│ ● step-ca 4/4 │ -│ Shell ⚠ stale (config changed, reload shell) │ -│ Studio http://localhost:3000/studio?project=stackpanel │ -│ │ -│ Environment │ -│ Node 22.22.0 • Bun 1.3.3 • Go 1.25.6 • Python 3.12.12 │ -│ PostgreSQL 17.7 • Redis 8.2.3 • Docker 28.5.2 │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Quick Start │ -│ dev  Start all development services │ -│ dev stop  Stop all services │ -│ sp status  Open interactive dashboard │ -│ sp commands  List all available commands │ -│ │ -│ Shortcuts │ -│ sp  = stackpanel │ -│ spx = run devshell commands │ -│ x  = same as spx │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ ⚠ Action Required │ -│ ! oxlint: 1/3 checks failing → sp status │ -│ ✗ secrets: 5/10 checks failing → sp status │ -│ ! Shell is stale (config changed) → exit && direnv allow │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Resources │ -│ Docs https://stackpanel.dev/docs │ -│ │ -╰────────────────────────────────────────────────────────────────────────╯ - -═══════════════════════════════════════════════════════════════ -Shell hook completed at 2026-03-01 07:17:51 -Log saved to: /Users/cm/git/darkmatter/stack/.stack/state/shell.log -═══════════════════════════════════════════════════════════════ -Creating infra module: test-example (Test Example) - -Created: - nix/stack/infra/modules/test-example/module.nix — Nix options and module registration - nix/stack/infra/modules/test-example/index.ts — TypeScript Alchemy implementation - -Next steps: - 1. Add your options to module.nix - 2. Implement provisioning logic in index.ts - 3. Import the module in nix/stack/infra/default.nix: - ./modules/test-example/module.nix - 4. Enable in .stack/config.nix: - stack.infra.test-example.enable = true; - 5. Run: infra:deploy - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.1s) - -Let me check the generated files look clean: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/test-example/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/test-example/module.nix -00003| # ============================================================================== -00004| { -00005| lib, -00006| config, -00007| ... -00008| }: -00009| let -00010| cfg = config.stack.infra.test-example; -00011| in -00012| { -00013| options.stack.infra.test-example = { -00014| enable = lib.mkOption { -00015| type = lib.types.bool; -00016| default = false; -00017| description = "Enable Test Example provisioning."; -00018| }; -00019| -00020| # Add your module options here. Example: -00021| # region = lib.mkOption { -00022| # type = lib.types.str; -00023| # default = "us-east-1"; -00024| # description = "AWS region."; -00025| # }; -00026| }; -00027| -00028| config = lib.mkIf cfg.enable { -00029| stack.infra.enable = lib.mkDefault true; -00030| -00031| stack.infra.modules.test-example = { -00032| name = "Test Example"; -00033| description = "Test Example infrastructure provisioning"; -00034| path = ./index.ts; -00035| -00036| # Nix values serialized to JSON and available at runtime -00037| # via infra.inputs() in the TypeScript file. -00038| inputs = { -00039| # Add your input values here. Example: -00040| # region = cfg.region; -00041| }; -00042| -00043| # NPM packages required by the TypeScript implementation. -00044| dependencies = { -00045| # "@aws-sdk/client-s3" = "catalog:"; -00046| }; -00047| -00048| # Outputs the TypeScript module's default export must provide. -00049| # Keys with sync=true are written to the storage backend. -00050| outputs = { -00051| # exampleArn = { -00052| # description = "ARN of the provisioned resource"; -00053| # sensitive = false; -00054| # sync = true; -00055| # }; -00056| }; -00057| }; -00058| }; -00059| } -00060| - -(End of file - total 60 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/test-example/index.ts" -} -``` -**Output:** -``` - -00001| // ============================================================================== -00002| // Test Example Infra Module -00003| // -00004| // This module is executed by `infra:deploy` via Alchemy. -00005| // -00006| // The default export must be a Record matching the output -00007| // keys declared in module.nix. -00008| // ============================================================================== -00009| import Infra from "@stack/infra"; -00010| -00011| // Typed inputs — define an interface matching your module.nix inputs. -00012| interface Inputs { -00013| // region: string; -00014| } -00015| -00016| const infra = new Infra("test-example"); -00017| const inputs = infra.inputs( -00018| process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES, -00019| ); -00020| -00021| // --------------------------------------------------------------------------- -00022| // Provision resources here. -00023| // -00024| // Use Alchemy resources or AWS SDK clients: -00025| // -00026| // import { SomeResource } from "@stack/infra/resources/some-resource"; -00027| // const resource = await SomeResource(infra.id("my-resource"), { ... }); -00028| // -00029| // Or use alchemy built-in resources: -00030| // -00031| // import { Role } from "alchemy/aws"; -00032| // const role = await Role(infra.id("role"), { roleName: "...", ... }); -00033| // -00034| // --------------------------------------------------------------------------- -00035| -00036| // Export outputs matching the keys in module.nix outputs. -00037| export default { -00038| // exampleArn: resource.arn, -00039| }; -00040| - -(End of file - total 40 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.6s) - -Clean scaffold output. Now clean up the test module: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "rm -rf nix/stack/infra/modules/test-example", - "workdir": "/Users/cm/git/darkmatter/stack", - "description": "Remove test scaffold module" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.0s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing infra module structure to define the canonical module shape", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Create scaffold template files for a new infra module", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Implement infra:new-module scaffold script in Nix", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Write contributor docs for infra module development", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Add module development docs to the docs site", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.4s) - -Here's a summary of what was built: - -## `infra:new-module` Scaffold Command - -Registered in `nix/stack/infra/codegen.nix` as a devshell script. Usage: - -```bash -infra:new-module my-s3-buckets -``` - -Creates: -``` -nix/stack/infra/modules/my-s3-buckets/ - module.nix # Nix options + module registration (with commented examples) - index.ts # TypeScript Alchemy implementation (with commented examples) -``` - -Validated: module ID must be lowercase alphanumeric with hyphens, directory must not already exist. The generated files have the module ID and name substituted throughout, and include commented examples for options, inputs, dependencies, outputs, and resource provisioning. - -The output includes clear next steps: -1. Add options to `module.nix` -2. Implement logic in `index.ts` -3. Import in `nix/stack/infra/default.nix` -4. Enable in `.stack/config.nix` -5. Run `infra:deploy` - -## Documentation - -`apps/docs/content/docs/deployment/infra-modules.mdx` -- comprehensive guide covering: - -- **Module anatomy**: the two-file contract (module.nix + index.ts) -- **Step-by-step walkthrough**: building an S3 bucket module from scaffold to deploy -- **Module contract**: inputs (key-case transformation), outputs (JSON strings), dependencies, Alchemy resources, resource IDs (`infra.id()`) -- **Directory modules**: splitting complex modules into multiple TypeScript files -- **Existing modules reference table**: ranked by complexity with what each is a good example of -- **Tips**: adopt patterns, testing with stages, checking generated code - ---- - diff --git a/docs/machines.md b/docs/machines.md deleted file mode 100644 index 08a44999..00000000 --- a/docs/machines.md +++ /dev/null @@ -1,12490 +0,0 @@ -# Infra module deploy --adopt failure despite help showing option - -**Session ID:** ses_35892eb07ffevEFiAHEU4zQAjT -**Created:** 2/28/2026, 7:24:29 PM -**Updated:** 3/1/2026, 6:25:35 AM - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "bunx alchemy deploy --adopt", - "workdir": "/Users/cm/git/darkmatter/stack/packages/infra", - "timeout": 120000, - "description": "Retest adopt deployment after IAM role changes" -} -``` -**Output:** -``` -Alchemy (v0.77.5) -App: stack-infra -Phase: up -Stage: cm -150 | // same (peers do not fix this). -151 | // MITIGATION: to ensure that most cases of accidental double-registration -152 | // are caught, we're going to check the handler's toString() to see if it's -153 | // the same as the previous handler. if it is, we're going to overwrite it. -154 | if (HANDLERS.get(type)!.toString() !== handler.toString()) { -155 | throw new Error(`Resource ${type} already exists`); - ^ -error: Resource iam::Role already exists - at Resource (/Users/cm/git/darkmatter/stack/node_modules/.bun/alchemy@0.77.5+f090c1b6923cb64f/node_modules/alchemy/src/resource.ts:155:17) - at /Users/cm/git/darkmatter/stack/packages/infra/src/resources/iam-role.ts:28:24 - -Bun v1.3.3 (macOS arm64) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/templates/iam-role.ts\n@@\n export const IamRole = Resource(\n- \"iam::Role\",\n+ \"stack::IamRole\",\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/templates/iam-role.ts -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.4s) - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/packages/infra/src/resources/iam-role.ts\n@@\n export const IamRole = Resource(\n- \"iam::Role\",\n+ \"stack::IamRole\",\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -M packages/infra/src/resources/iam-role.ts -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 9.3s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "bunx alchemy deploy --adopt", - "workdir": "/Users/cm/git/darkmatter/stack/packages/infra", - "timeout": 120000, - "description": "Validate deploy adopts existing IAM role" -} -``` -**Output:** -``` -Alchemy (v0.77.5) -App: stack-infra -Phase: up -Stage: cm -[creating] aws-secrets-role Creating Resource... -[created] aws-secrets-role Created Resource -[creating] aws-secrets-oidc Creating Resource... -[created] aws-secrets-oidc Created Resource -[creating] aws-secrets-kms-key Creating Resource... -[created] aws-secrets-kms-key Created Resource -[creating] aws-secrets-kms-alias Creating Resource... -[created] aws-secrets-kms-alias Created Resource -[updating] aws-secrets-role Updating Resource... -[updated] aws-secrets-role Updated Resource -161 | ); -162 | } -163 | } -164 | -165 | // Future: fly, vercel, aws -166 | throw new Error(`Unsupported host "${app.host}"`); - ^ -error: Unsupported host "fly" - at deployApp (/Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:166:13) - at deployApp (/Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:94:3) - at /Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:175:26 - -Bun v1.3.3 (macOS arm64) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.6s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git status --short", - "workdir": "/Users/cm/git/darkmatter/stack", - "description": "Show modified files after infra refactor" -} -``` -**Output:** -``` -M .gitignore -MM .stack/config.nix -M .stack/gen/zed/settings.json -M .stack/modules/default.nix -A .stack/modules/generate-config-example.nix -A .stack/secrets/README.md -A .stack/secrets/keys/.sops.yaml -A .stack/secrets/keys/secrets.nix -M .stack/secrets/vars/dev.sops.yaml -M .vscode/settings.json -M apps/docs/content/docs/deployment/cloudflare.mdx -M apps/docs/content/docs/deployment/containers.mdx -M apps/docs/content/docs/deployment/fly.mdx -M apps/docs/content/docs/deployment/index.mdx -M apps/docs/content/docs/internal/deployment/fly/module.mdx - M apps/docs/content/docs/internal/infra/options.mdx - M apps/docs/content/docs/internal/secrets.mdx - M apps/docs/content/docs/secrets/index.mdx - M apps/docs/content/docs/secrets/master-keys.mdx - M apps/docs/content/docs/secrets/variables.mdx -A apps/stack-go/cmd/cli/config_generate_example.go -M apps/stack-go/internal/agent/server/agenix.go -M apps/stack-go/internal/agent/server/secrets_groups.go -A apps/web/.stack/secrets/state/manifest.json -M apps/web/.stack/secrets/vars/.sops.yaml -A apps/web/.tasks/bin/container-build -A apps/web/.tasks/bin/container-push - A apps/web/fly.toml -M apps/web/src/components/studio/panels/apps-panel-alt.tsx -M apps/web/src/components/studio/panels/apps/app-variables-section/components.tsx -M apps/web/src/components/studio/panels/apps/app-variables-section/use-app-variables-section.ts -M apps/web/src/components/studio/panels/apps/utils/environment-helpers.ts -M apps/web/src/components/studio/panels/apps/utils/index.ts -M apps/web/src/components/studio/panels/variables/add-variable-dialog.tsx -M apps/web/src/components/studio/panels/variables/constants.ts -M apps/web/src/components/studio/panels/variables/edit-secret-dialog.tsx -M apps/web/src/components/studio/panels/variables/edit-secret-dialog/types.ts -M apps/web/src/components/studio/panels/variables/edit-secret-dialog/use-edit-secret-dialog.ts -M apps/web/src/components/studio/panels/variables/edit-variable-dialog.tsx -M apps/web/src/components/studio/panels/variables/variables-panel.tsx -M apps/web/src/lib/types.ts -A docs/CONFIG_EXAMPLE_GENERATION.md -A docs/DEPLOYMENT_FIX_CHECKLIST.md -A docs/DEPLOYMENT_MIGRATION.md -M docs/DEVENV_LEARNINGS.md -A docs/DOCS_UPDATE_SUMMARY.md - M docs/HOW_TO_USE_GROUPS.md - M docs/SECRETS_V2.md -M docs/deploy.md -M nix/flake/default.nix -M nix/internal/flake/default.nix -M nix/stack/core/default.nix - M nix/stack/core/options/devshell.nix -M nix/stack/core/options/variables.nix - M nix/stack/default.nix -M nix/stack/deployment/fly/module.nix -MM nix/stack/devshell/core.nix - M nix/stack/devshell/files.nix -A nix/stack/devshell/gc-roots.nix -A nix/stack/devshell/profile.nix - A nix/stack/files/default.nix - M nix/stack/infra/codegen.nix - D nix/stack/infra/modules/aws-secrets/index.ts - M nix/stack/infra/modules/aws-secrets/module.nix - M nix/stack/infra/options.nix - M nix/stack/infra/templates/infra.tmpl.ts - M nix/stack/infra/templates/kms-key.ts -M nix/stack/secrets/default.nix -M nix/stack/secrets/lib.nix -A nix/stack/secrets/lib/age-key-cmd.nix -A nix/stack/secrets/lib/age-key-cmd.sh - M packages/gen/env/ARCHITECTURE.md -M packages/gen/env/README.md -D packages/gen/env/src/generated/docs/dev.ts -D packages/gen/env/src/generated/docs/index.ts -M packages/gen/env/src/generated/index.ts -M packages/gen/env/src/generated/web/dev.ts -M packages/gen/env/src/generated/web/index.ts -D packages/gen/env/src/generated/web/prod.ts -M packages/gen/env/src/loader.ts -M packages/infra/README.md - M packages/infra/modules/aws-secrets.ts -MM packages/infra/package.json - M packages/infra/src/index.ts - M packages/infra/src/resources/kms-key.ts -A packages/infra/turbo.json -M process-compose.yaml -A session-ses_3633.md -M turbo.json -?? nix/stack/infra/modules/aws-secrets/module/ -?? nix/stack/infra/templates/iam-role.ts -?? packages/infra/modules/aws-secrets/ -?? packages/infra/src/resources/iam-role.ts - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.3s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/packages/infra/modules/aws-secrets/index.ts" -} -``` -**Output:** -``` - -00001| import { AccountId } from "alchemy/aws"; -00002| import { GitHubOIDCProvider } from "alchemy/aws/oidc"; -00003| import Infra from "@stack/infra"; -00004| import { IamRole } from "@stack/infra/resources/iam-role"; -00005| import { KmsAlias } from "@stack/infra/resources/kms-alias"; -00006| import { KmsKey } from "@stack/infra/resources/kms-key"; -00007| import { -00008| buildAssumeRolePolicy, -00009| buildKmsKeyPolicy, -00010| buildRoleKmsInlinePolicy, -00011| githubActionsProviderArn, -00012| } from "./policies"; -00013| -00014| const infra = new Infra("aws-secrets"); -00015| const inputs = infra.inputs(process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES); -00016| -00017| const accountId = await AccountId(); -00018| const assumeRolePolicy = buildAssumeRolePolicy(inputs.oidc, accountId) as any; -00019| -00020| const roleTags = { -00021| Project: inputs.projectName, -00022| ManagedBy: "stack-infra", -00023| }; -00024| -00025| const rolePolicyName = `${inputs.iam.roleName}-kms-policy`; -00026| -00027| const role = await IamRole(infra.id("role"), { -00028| roleName: inputs.iam.roleName, -00029| assumeRolePolicy, -00030| description: `IAM role for ${inputs.projectName} secrets access`, -00031| tags: roleTags, -00032| managedPolicyArns: inputs.iam.additionalPolicies ?? [], -00033| }); -00034| -00035| let resolvedOidcProviderArn = githubActionsProviderArn(accountId); -00036| -00037| if (inputs.oidc.provider === "github-actions") { -00038| const oidcProvider = await GitHubOIDCProvider(infra.id("oidc"), { -00039| adopt: true, -00040| owner: inputs.oidc.githubActions.org, -00041| repository: inputs.oidc.githubActions.repo, -00042| roleArn: role.arn, -00043| } as any); -00044| resolvedOidcProviderArn = oidcProvider.providerArn; -00045| } -00046| -00047| const kmsKey = await KmsKey(infra.id("kms-key"), { -00048| aliasName: `alias/${inputs.kms.alias}`, -00049| description: `KMS key for encrypting ${inputs.projectName} secrets`, -00050| enableKeyRotation: true, -00051| deletionWindowInDays: inputs.kms.deletionWindowDays, -00052| policy: buildKmsKeyPolicy(accountId, role.arn), -00053| tags: { -00054| Name: inputs.kms.alias, -00055| ...roleTags, -00056| }, -00057| }); -00058| -00059| const kmsAlias = await KmsAlias(infra.id("kms-alias"), { -00060| aliasName: `alias/${inputs.kms.alias}`, -00061| targetKeyId: kmsKey.keyId, -00062| }); -00063| -00064| await IamRole(infra.id("role"), { -00065| roleName: inputs.iam.roleName, -00066| assumeRolePolicy, -00067| description: `IAM role for ${inputs.projectName} secrets access`, -00068| tags: roleTags, -00069| managedPolicyArns: inputs.iam.additionalPolicies ?? [], -00070| policies: [ -00071| { -00072| policyName: rolePolicyName, -00073| ...buildRoleKmsInlinePolicy(inputs.kms.alias, kmsKey.arn), -00074| }, -00075| ], -00076| }); -00077| -00078| export default { -00079| roleArn: role.arn, -00080| roleName: role.roleName, -00081| kmsKeyArn: kmsKey.arn, -00082| kmsKeyId: kmsKey.keyId, -00083| kmsAliasName: kmsAlias.aliasName, -00084| oidcProviderArn: resolvedOidcProviderArn, -00085| }; -00086| - -(End of file - total 86 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.3s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module/index.ts" -} -``` -**Output:** -``` - -00001| import { AccountId } from "alchemy/aws"; -00002| import { GitHubOIDCProvider } from "alchemy/aws/oidc"; -00003| import Infra from "@stack/infra"; -00004| import { IamRole } from "@stack/infra/resources/iam-role"; -00005| import { KmsAlias } from "@stack/infra/resources/kms-alias"; -00006| import { KmsKey } from "@stack/infra/resources/kms-key"; -00007| import { -00008| buildAssumeRolePolicy, -00009| buildKmsKeyPolicy, -00010| buildRoleKmsInlinePolicy, -00011| githubActionsProviderArn, -00012| } from "./policies"; -00013| -00014| const infra = new Infra("aws-secrets"); -00015| const inputs = infra.inputs(process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES); -00016| -00017| const accountId = await AccountId(); -00018| const assumeRolePolicy = buildAssumeRolePolicy(inputs.oidc, accountId) as any; -00019| -00020| const roleTags = { -00021| Project: inputs.projectName, -00022| ManagedBy: "stack-infra", -00023| }; -00024| -00025| const rolePolicyName = `${inputs.iam.roleName}-kms-policy`; -00026| -00027| const role = await IamRole(infra.id("role"), { -00028| roleName: inputs.iam.roleName, -00029| assumeRolePolicy, -00030| description: `IAM role for ${inputs.projectName} secrets access`, -00031| tags: roleTags, -00032| managedPolicyArns: inputs.iam.additionalPolicies ?? [], -00033| }); -00034| -00035| let resolvedOidcProviderArn = githubActionsProviderArn(accountId); -00036| -00037| if (inputs.oidc.provider === "github-actions") { -00038| const oidcProvider = await GitHubOIDCProvider(infra.id("oidc"), { -00039| adopt: true, -00040| owner: inputs.oidc.githubActions.org, -00041| repository: inputs.oidc.githubActions.repo, -00042| roleArn: role.arn, -00043| } as any); -00044| resolvedOidcProviderArn = oidcProvider.providerArn; -00045| } -00046| -00047| const kmsKey = await KmsKey(infra.id("kms-key"), { -00048| aliasName: `alias/${inputs.kms.alias}`, -00049| description: `KMS key for encrypting ${inputs.projectName} secrets`, -00050| enableKeyRotation: true, -00051| deletionWindowInDays: inputs.kms.deletionWindowDays, -00052| policy: buildKmsKeyPolicy(accountId, role.arn), -00053| tags: { -00054| Name: inputs.kms.alias, -00055| ...roleTags, -00056| }, -00057| }); -00058| -00059| const kmsAlias = await KmsAlias(infra.id("kms-alias"), { -00060| aliasName: `alias/${inputs.kms.alias}`, -00061| targetKeyId: kmsKey.keyId, -00062| }); -00063| -00064| await IamRole(infra.id("role"), { -00065| roleName: inputs.iam.roleName, -00066| assumeRolePolicy, -00067| description: `IAM role for ${inputs.projectName} secrets access`, -00068| tags: roleTags, -00069| managedPolicyArns: inputs.iam.additionalPolicies ?? [], -00070| policies: [ -00071| { -00072| policyName: rolePolicyName, -00073| ...buildRoleKmsInlinePolicy(inputs.kms.alias, kmsKey.arn), -00074| }, -00075| ], -00076| }); -00077| -00078| export default { -00079| roleArn: role.arn, -00080| roleName: role.roleName, -00081| kmsKeyArn: kmsKey.arn, -00082| kmsKeyId: kmsKey.keyId, -00083| kmsAliasName: kmsAlias.aliasName, -00084| oidcProviderArn: resolvedOidcProviderArn, -00085| }; -00086| - -(End of file - total 86 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.5s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "bunx alchemy deploy --adopt true", - "workdir": "/Users/cm/git/darkmatter/stack/packages/infra", - "timeout": 120000, - "description": "Retest deploy with explicit adopt true" -} -``` -**Output:** -``` -Alchemy (v0.77.5) -App: stack-infra -Phase: up -Stage: cm -[updating] aws-secrets-role Updating Resource... -[updated] aws-secrets-role Updated Resource -[skipped] aws-secrets-oidc Skipped Resource (no changes) -[skipped] aws-secrets-kms-key Skipped Resource (no changes) -[skipped] aws-secrets-kms-alias Skipped Resource (no changes) -[updating] aws-secrets-role Updating Resource... -[updated] aws-secrets-role Updated Resource -161 | ); -162 | } -163 | } -164 | -165 | // Future: fly, vercel, aws -166 | throw new Error(`Unsupported host "${app.host}"`); - ^ -error: Unsupported host "fly" - at deployApp (/Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:166:13) - at deployApp (/Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:94:3) - at /Users/cm/git/darkmatter/stack/packages/infra/modules/deployment.ts:175:26 - -Bun v1.3.3 (macOS arm64) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 128.9s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "bunx alchemy deploy --adopt --app aws-secrets", - "workdir": "/Users/cm/git/darkmatter/stack/packages/infra", - "timeout": 120000, - "description": "Try deploying only aws-secrets app" -} -``` -**Output:** -``` -found root dir: /Users/cm/git/darkmatter/stack - - - -bash tool terminated command after exceeding timeout 120000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.7s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git diff --name-only -- nix/stack/infra/codegen.nix nix/stack/infra/modules/aws-secrets/module.nix nix/stack/infra/modules/aws-secrets/module/index.ts nix/stack/infra/modules/aws-secrets/module/policies.ts nix/stack/infra/templates/kms-key.ts nix/stack/infra/templates/iam-role.ts packages/infra/modules/aws-secrets.ts packages/infra/modules/aws-secrets/index.ts packages/infra/modules/aws-secrets/policies.ts packages/infra/src/resources/kms-key.ts packages/infra/src/resources/iam-role.ts", - "workdir": "/Users/cm/git/darkmatter/stack", - "description": "List files changed for infra adopt fix" -} -``` -**Output:** -``` -nix/stack/infra/codegen.nix -nix/stack/infra/modules/aws-secrets/module.nix -nix/stack/infra/templates/kms-key.ts -packages/infra/modules/aws-secrets.ts -packages/infra/src/resources/kms-key.ts - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "git status --short -- nix/stack/infra/modules/aws-secrets nix/stack/infra/templates packages/infra/modules/aws-secrets packages/infra/src/resources", - "workdir": "/Users/cm/git/darkmatter/stack", - "description": "Show status for adopt-related files" -} -``` -**Output:** -``` - D nix/stack/infra/modules/aws-secrets/index.ts - M nix/stack/infra/modules/aws-secrets/module.nix - M nix/stack/infra/templates/infra.tmpl.ts - M nix/stack/infra/templates/kms-key.ts - M packages/infra/src/resources/kms-key.ts -?? nix/stack/infra/modules/aws-secrets/module/ -?? nix/stack/infra/templates/iam-role.ts -?? packages/infra/modules/aws-secrets/ -?? packages/infra/src/resources/iam-role.ts - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.9s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 24, - "limit": 45 -} -``` -**Output:** -``` - -00025| pkgs, -00026| ... -00027| }: -00028| let -00029| cfg = config.stack.infra; -00030| projectName = config.stack.name or "my-project"; -00031| outputDir = cfg.output-dir; -00032| stateDir = config.stack.dirs.state; -00033| moduleIds = builtins.attrNames cfg.modules; -00034| sortedModuleIds = lib.sort (a: b: a < b) moduleIds; -00035| -00036| modulePathIsDirectory = -00037| id: -00038| let -00039| modPath = cfg.modules.${id}.path; -00040| dirTry = builtins.tryEval (builtins.readDir modPath); -00041| in -00042| dirTry.success; -00043| -00044| moduleImportPath = -00045| id: if modulePathIsDirectory id then "./modules/${id}/index.ts" else "./modules/${id}.ts"; -00046| -00047| # ============================================================================ -00048| # Module bunDeps handling -00049| # -00050| # External flakes can provide pre-validated bunDeps. When provided, -00051| # dependencies are already validated at the flake's eval time. -00052| # ============================================================================ -00053| -00054| # Modules that provide their own bunDeps -00055| modulesWithBunDeps = lib.filterAttrs (_: mod: mod.bunDeps != null) cfg.modules; -00056| -00057| # Modules without bunDeps (deps need runtime or package-level validation) -00058| modulesWithoutBunDeps = lib.filterAttrs (_: mod: mod.bunDeps == null) cfg.modules; -00059| -00060| # All module bunDeps as a list -00061| allModuleBunDeps = lib.mapAttrsToList (_: mod: mod.bunDeps) modulesWithBunDeps; -00062| -00063| # Check if all modules provide bunDeps (fully validated at flake level) -00064| allModulesHaveBunDeps = -00065| builtins.length (builtins.attrNames modulesWithoutBunDeps) == 0 && builtins.length moduleIds > 0; -00066| -00067| # Check if any module provides bunDeps -00068| anyModuleHasBunDeps = builtins.length (builtins.attrNames modulesWithBunDeps) > 0; -00069| - -(File has more lines. Use 'offset' parameter to read beyond line 69) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.6s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 236, - "limit": 45 -} -``` -**Output:** -``` - -00237| ) moduleIds; -00238| in -00239| '' -00240| // Generated by stack — do not edit manually. -00241| // TypeScript interfaces for infra module inputs. -00242| -00243| ${interfaces} -00244| ''; -00245| -00246| # ============================================================================ -00247| # Generated: alchemy.run.ts (orchestrator) -00248| # -00249| # Uses @gen/alchemy for app creation (shared state store config) instead -00250| # of inline alchemy boilerplate. -00251| # ============================================================================ -00252| alchemyRunTs = -00253| let -00254| # Dynamic imports for each module -00255| moduleImports = lib.concatMapStringsSep "\n" ( -00256| id: -00257| let -00258| mod = cfg.modules.${id}; -00259| syncKeys = builtins.filter (k: (mod.outputs.${k}.sync or false)) (builtins.attrNames mod.outputs); -00260| in -00261| '' -00262| const ${ -00263| builtins.replaceStrings [ "-" ] [ "_" ] id -00264| }Outputs = (await import("${moduleImportPath id}")).default; -00265| '' -00266| ) moduleIds; -00267| -00268| # syncAll argument -00269| syncAllArg = lib.concatMapStringsSep "\n" ( -00270| id: -00271| let -00272| mod = cfg.modules.${id}; -00273| syncKeys = builtins.filter (k: (mod.outputs.${k}.sync or false)) (builtins.attrNames mod.outputs); -00274| varName = builtins.replaceStrings [ "-" ] [ "_" ] id; -00275| in -00276| '' -00277| "${id}": { -00278| outputs: ${varName}Outputs, -00279| syncKeys: ${builtins.toJSON syncKeys}, -00280| },'' -00281| ) moduleIds; - -(File has more lines. Use 'offset' parameter to read beyond line 281) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.9s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 430, - "limit": 70 -} -``` -**Output:** -``` - -00431| forceConsistentCasingInFileNames = true; -00432| resolveJsonModule = true; -00433| declaration = true; -00434| declarationMap = true; -00435| outDir = "./dist"; -00436| rootDir = "."; -00437| }; -00438| include = [ -00439| "src/**/*.ts" -00440| "modules/**/*.ts" -00441| "alchemy.run.ts" -00442| ]; -00443| exclude = [ -00444| "node_modules" -00445| "dist" -00446| ".alchemy" -00447| ]; -00448| }; -00449| -00450| # ============================================================================ -00451| # Module .ts file entries (copied from paths) -00452| # ============================================================================ -00453| moduleFileEntries = lib.foldlAttrs ( -00454| acc: id: mod: -00455| let -00456| dirTry = builtins.tryEval (builtins.readDir mod.path); -00457| in -00458| if dirTry.success then -00459| let -00460| entries = dirTry.value; -00461| tsFiles = lib.filterAttrs (name: kind: kind == "regular" && lib.hasSuffix ".ts" name) entries; -00462| copied = lib.mapAttrs' ( -00463| name: _kind: -00464| lib.nameValuePair "${outputDir}/modules/${id}/${name}" { -00465| text = builtins.readFile (mod.path + "/${name}"); -00466| mode = "0644"; -00467| description = "Infra module: ${mod.name} (${name})"; -00468| source = "infra"; -00469| } -00470| ) tsFiles; -00471| in -00472| acc // copied -00473| else -00474| acc -00475| // { -00476| "${outputDir}/modules/${id}.ts" = { -00477| text = builtins.readFile mod.path; -00478| mode = "0644"; -00479| description = "Infra module: ${mod.name}"; -00480| source = "infra"; -00481| }; -00482| } -00483| ) { } cfg.modules; -00484| -00485| in -00486| { -00487| config = lib.mkIf cfg.enable { -00488| # ========================================================================== -00489| # File generation -00490| # ========================================================================== -00491| stack.files.entries = { -00492| # README -00493| "${outputDir}/README.md" = { -00494| text = readmeMd; -00495| mode = "0644"; -00496| description = "Generated README for infra package"; -00497| source = "infra"; -00498| }; -00499| -00500| # Infra class library - -(File has more lines. Use 'offset' parameter to read beyond line 500) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.6s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/packages/infra/src/resources/iam-role.ts", - "offset": 1, - "limit": 220 -} -``` -**Output:** -``` - -00002| import { Resource } from "alchemy"; -00003| -00004| export interface IamInlinePolicy { -00005| policyName: string; -00006| policyDocument: unknown; -00007| } -00008| -00009| export interface IamRoleProps { -00010| roleName: string; -00011| assumeRolePolicy: unknown; -00012| description?: string; -00013| tags?: Record; -00014| managedPolicyArns?: string[]; -00015| policies?: IamInlinePolicy[]; -00016| destroyOnDelete?: boolean; -00017| } -00018| -00019| export interface IamRole extends IamRoleProps { -00020| arn: string; -00021| adopted?: boolean; -00022| } -00023| -00024| function asPolicyJson(policy: unknown): string { -00025| return typeof policy === "string" ? policy : JSON.stringify(policy); -00026| } -00027| -00028| export const IamRole = Resource( -00029| "stack::IamRole", -00030| async function ( -00031| this: Context, -00032| _id: string, -00033| props: IamRoleProps, -00034| ): Promise { -00035| const { -00036| IAMClient, -00037| CreateRoleCommand, -00038| GetRoleCommand, -00039| UpdateAssumeRolePolicyCommand, -00040| UpdateRoleDescriptionCommand, -00041| TagRoleCommand, -00042| ListAttachedRolePoliciesCommand, -00043| AttachRolePolicyCommand, -00044| PutRolePolicyCommand, -00045| } = await import("@aws-sdk/client-iam"); -00046| -00047| const client = new IAMClient({}); -00048| -00049| if (this.phase === "delete") { -00050| return this.destroy(); -00051| } -00052| -00053| let adopted = this.output?.adopted ?? false; -00054| -00055| const getExistingRole = async () => { -00056| try { -00057| const existing = await client.send(new GetRoleCommand({ RoleName: props.roleName })); -00058| return existing.Role; -00059| } catch (err: any) { -00060| if (err.name === "NoSuchEntityException") { -00061| return undefined; -00062| } -00063| throw err; -00064| } -00065| }; -00066| -00067| let role = await getExistingRole(); -00068| -00069| if (!role) { -00070| const created = await client.send( -00071| new CreateRoleCommand({ -00072| RoleName: props.roleName, -00073| AssumeRolePolicyDocument: asPolicyJson(props.assumeRolePolicy), -00074| Description: props.description, -00075| Tags: Object.entries(props.tags ?? {}).map(([Key, Value]) => ({ Key, Value })), -00076| }), -00077| ); -00078| role = created.Role; -00079| } else { -00080| adopted = true; -00081| } -00082| -00083| await client.send( -00084| new UpdateAssumeRolePolicyCommand({ -00085| RoleName: props.roleName, -00086| PolicyDocument: asPolicyJson(props.assumeRolePolicy), -00087| }), -00088| ); -00089| -00090| if (props.description) { -00091| await client.send( -00092| new UpdateRoleDescriptionCommand({ -00093| RoleName: props.roleName, -00094| Description: props.description, -00095| }), -00096| ); -00097| } -00098| -00099| if (props.tags && Object.keys(props.tags).length > 0) { -00100| await client.send( -00101| new TagRoleCommand({ -00102| RoleName: props.roleName, -00103| Tags: Object.entries(props.tags).map(([Key, Value]) => ({ Key, Value })), -00104| }), -00105| ); -00106| } -00107| -00108| const managedPolicyArns = props.managedPolicyArns ?? []; -00109| if (managedPolicyArns.length > 0) { -00110| const attached = await client.send( -00111| new ListAttachedRolePoliciesCommand({ -00112| RoleName: props.roleName, -00113| }), -00114| ); -00115| const attachedArns = new Set((attached.AttachedPolicies ?? []).map((policy) => policy.PolicyArn)); -00116| -00117| for (const arn of managedPolicyArns) { -00118| if (!attachedArns.has(arn)) { -00119| await client.send( -00120| new AttachRolePolicyCommand({ -00121| RoleName: props.roleName, -00122| PolicyArn: arn, -00123| }), -00124| ); -00125| } -00126| } -00127| } -00128| -00129| for (const policy of props.policies ?? []) { -00130| await client.send( -00131| new PutRolePolicyCommand({ -00132| RoleName: props.roleName, -00133| PolicyName: policy.policyName, -00134| PolicyDocument: asPolicyJson(policy.policyDocument), -00135| }), -00136| ); -00137| } -00138| -00139| const finalRole = await client.send(new GetRoleCommand({ RoleName: props.roleName })); -00140| const finalArn = finalRole.Role?.Arn ?? role?.Arn; -00141| -00142| if (!finalArn) { -00143| throw new Error(`Unable to resolve ARN for IAM role ${props.roleName}`); -00144| } -00145| -00146| return { -00147| ...props, -00148| arn: finalArn, -00149| adopted, -00150| }; -00151| }, -00152| ); -00153| - -(End of file - total 153 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.9s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/packages/infra/src/resources/kms-key.ts", - "offset": 1, - "limit": 220 -} -``` -**Output:** -``` - -00002| // Generated by stack — static library code. -00003| import type { Context } from "alchemy"; -00004| import { Resource } from "alchemy"; -00005| -00006| export interface KmsKeyProps { -00007| /** Existing alias name used to adopt an already-created key */ -00008| aliasName?: string; -00009| /** Description of the key */ -00010| description?: string; -00011| /** Enable automatic key rotation (default: true) */ -00012| enableKeyRotation?: boolean; -00013| /** Deletion window in days (default: 30, range: 7-30) */ -00014| deletionWindowInDays?: number; -00015| /** Key policy JSON document */ -00016| policy?: string; -00017| /** Resource tags */ -00018| tags?: Record; -00019| } -00020| -00021| export interface KmsKey extends KmsKeyProps { -00022| /** ARN of the KMS key */ -00023| arn: string; -00024| /** Key ID */ -00025| keyId: string; -00026| /** True when key was adopted from an existing alias */ -00027| adopted?: boolean; -00028| } -00029| -00030| export const KmsKey = Resource( -00031| "kms::Key", -00032| async function ( -00033| this: Context, -00034| _id: string, -00035| props: KmsKeyProps, -00036| ): Promise { -00037| const { -00038| KMSClient, -00039| CreateKeyCommand, -00040| DescribeKeyCommand, -00041| EnableKeyRotationCommand, -00042| DisableKeyRotationCommand, -00043| PutKeyPolicyCommand, -00044| ScheduleKeyDeletionCommand, -00045| TagResourceCommand, -00046| ListAliasesCommand, -00047| } = await import("@aws-sdk/client-kms"); -00048| const client = new KMSClient({}); -00049| -00050| if (this.phase === "delete") { -00051| if (this.output?.adopted) { -00052| return this.destroy(); -00053| } -00054| if (this.output?.keyId) { -00055| try { -00056| await client.send( -00057| new ScheduleKeyDeletionCommand({ -00058| KeyId: this.output.keyId, -00059| PendingWindowInDays: props.deletionWindowInDays ?? 30, -00060| }) -00061| ); -00062| } catch (err: any) { -00063| if (err.name !== "NotFoundException") throw err; -00064| } -00065| } -00066| return this.destroy(); -00067| } -00068| -00069| let keyId: string | undefined; -00070| let adopted = this.output?.adopted ?? false; -00071| -00072| if (this.phase === "create") { -00073| const aliasName = props.aliasName?.startsWith("alias/") -00074| ? props.aliasName -00075| : props.aliasName -00076| ? `alias/${props.aliasName}` -00077| : undefined; -00078| -00079| if (aliasName) { -00080| let marker: string | undefined; -00081| do { -00082| const listed = await client.send(new ListAliasesCommand({ Marker: marker })); -00083| const existing = (listed.Aliases ?? []).find((item) => item.AliasName === aliasName); -00084| if (existing?.TargetKeyId) { -00085| keyId = existing.TargetKeyId; -00086| adopted = true; -00087| break; -00088| } -00089| marker = listed.NextMarker; -00090| } while (marker); -00091| } -00092| -00093| if (!keyId) { -00094| const result = await client.send( -00095| new CreateKeyCommand({ -00096| Description: props.description, -00097| KeyUsage: "ENCRYPT_DECRYPT", -00098| Origin: "AWS_KMS", -00099| Tags: [ -00100| ...Object.entries(props.tags ?? {}).map(([Key, Value]) => ({ TagKey: Key, TagValue: Value })), -00101| { TagKey: "alchemy_stage", TagValue: this.stage }, -00102| { TagKey: "alchemy_resource", TagValue: this.id }, -00103| ], -00104| ...(props.policy ? { Policy: props.policy } : {}), -00105| }) -00106| ); -00107| keyId = result.KeyMetadata!.KeyId!; -00108| } -00109| } else { -00110| keyId = this.output!.keyId; -00111| } -00112| -00113| if (!keyId) { -00114| throw new Error("Unable to resolve KMS key id"); -00115| } -00116| -00117| // Enable/disable key rotation -00118| if (props.enableKeyRotation !== false) { -00119| await client.send(new EnableKeyRotationCommand({ KeyId: keyId })); -00120| } else { -00121| await client.send(new DisableKeyRotationCommand({ KeyId: keyId })); -00122| } -00123| -00124| // Update policy if provided -00125| if (props.policy) { -00126| await client.send( -00127| new PutKeyPolicyCommand({ -00128| KeyId: keyId, -00129| PolicyName: "default", -00130| Policy: props.policy, -00131| }) -00132| ); -00133| } -00134| -00135| // Update tags -00136| const tags = { -00137| ...props.tags, -00138| alchemy_stage: this.stage, -00139| alchemy_resource: this.id, -00140| }; -00141| await client.send( -00142| new TagResourceCommand({ -00143| KeyId: keyId, -00144| Tags: Object.entries(tags).map(([TagKey, TagValue]) => ({ TagKey, TagValue })), -00145| }) -00146| ); -00147| -00148| // Describe to get full info -00149| const desc = await client.send(new DescribeKeyCommand({ KeyId: keyId })); -00150| -00151| return { -00152| ...props, -00153| arn: desc.KeyMetadata!.Arn!, -00154| keyId: desc.KeyMetadata!.KeyId!, -00155| adopted, -00156| }; -00157| } -00158| ); -00159| - -(End of file - total 159 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.9s) - -_Thinking:_ - - - -Implemented. I fixed the adopt failure path and refactored the AWS secrets infra module into smaller pieces. - -**What I changed** -- Made AWS role creation adopt-safe by replacing direct `alchemy/aws Role(...)` usage with a custom idempotent resource that does `get-or-create` and then updates policy/tags/attachments: - - `packages/infra/src/resources/iam-role.ts:28` - - template source: `nix/stack/infra/templates/iam-role.ts:28` -- Updated the AWS secrets module to use that resource (and kept logic cleaner by extracting policy builders): - - `packages/infra/modules/aws-secrets/index.ts:1` - - `packages/infra/modules/aws-secrets/policies.ts:1` - - `packages/infra/modules/aws-secrets.ts:1` (thin re-export wrapper) - - mirrored source-of-truth under Nix: - - `nix/stack/infra/modules/aws-secrets/module/index.ts:1` - - `nix/stack/infra/modules/aws-secrets/module/policies.ts:1` -- Made KMS key resource adopt-aware via alias lookup, so existing keys can be adopted instead of always created: - - `packages/infra/src/resources/kms-key.ts:6` - - template source: `nix/stack/infra/templates/kms-key.ts:6` -- Simplified infra module composition by adding directory-based module generation support (so modules can be split into multiple files): - - `nix/stack/infra/codegen.nix:36` (directory detection/import path) - - `nix/stack/infra/codegen.nix:453` (copy all `*.ts` from module dirs) - - switched aws-secrets module path to a directory: - - `nix/stack/infra/modules/aws-secrets/module.nix:226` - -**Validation** -- Reproduced before state: `--adopt` still failed on `Role with name ... already exists`. -- After changes, `bunx alchemy deploy --adopt true` in `packages/infra` now successfully updates/skips aws-secrets resources instead of failing on IAM role creation. -- Current deploy now fails later on an unrelated existing issue: `Unsupported host "fly"` from `packages/infra/modules/deployment.ts:166`. - -**Important note** -- `infra:deploy` through devshell is still blocked in this workspace by an existing Nix option typo (`stack.infra.storage-bakend`) unrelated to this fix. I validated directly via `bunx alchemy deploy ...` in `packages/infra`. - -If you want, next I can tackle the `host: "fly"` branch in deployment so full deploy completes end-to-end. - ---- - -## User - -❯❯ nix develop -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -error: - … while evaluating the attribute 'packages' - at /Users/cm/git/darkmatter/stack/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /Users/cm/git/darkmatter/stack/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the option `stack.outputs': - - … while evaluating definitions from `/nix/store/fzwa0z2bmq58sxir22nmlgsqq1zdvbf5-source/nix/stack/devshell/scripts.nix': - - … while evaluating the option `stack.scripts': - - … while evaluating definitions from `/nix/store/fzwa0z2bmq58sxir22nmlgsqq1zdvbf5-source/nix/stack/files': - - … while evaluating the option `stack.files.entries': - - … while evaluating definitions from `/nix/store/fzwa0z2bmq58sxir22nmlgsqq1zdvbf5-source/nix/stack/infra/codegen.nix': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: cannot read directory "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/index.ts": Not a directory - ---- - -## Assistant (Build · gpt-5.3-codex · 11.7s) - -_Thinking:_ - - - -``` -Tool: apply_patch - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix\n@@\n- modulePathIsDirectory =\n- id:\n- let\n- modPath = cfg.modules.${id}.path;\n- dirTry = builtins.tryEval (builtins.readDir modPath);\n- in\n- dirTry.success;\n+ modulePathIsDirectory =\n+ id:\n+ let\n+ modPath = cfg.modules.${id}.path;\n+ in\n+ builtins.pathExists (modPath + \"/index.ts\");\n@@\n moduleFileEntries = lib.foldlAttrs (\n acc: id: mod:\n- let\n- dirTry = builtins.tryEval (builtins.readDir mod.path);\n- in\n- if dirTry.success then\n+ if modulePathIsDirectory id then\n let\n- entries = dirTry.value;\n+ entries = builtins.readDir mod.path;\n tsFiles = lib.filterAttrs (name: kind: kind == \"regular\" && lib.hasSuffix \".ts\" name) entries;\n copied = lib.mapAttrs' (\n name: _kind:\n*** End Patch" -} -``` -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/codegen.nix -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 60.6s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 120000, - "description": "Verify nix develop evaluates after codegen fix" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/vnwyz82g6c0mrphy9m6hmln99ydhbsw4-vendor-env.drv'... -building '/nix/store/0lrxfbh14hw39l5zhp90bvqlm1xwa0ya-go-cache-env.drv'... -building '/nix/store/bz73aihsnnh7grc2d6jj5wwl1x19nvnm-stack-0.1.0.drv'... -building '/nix/store/w17h7r8fk6qs4dw1pqanxjzqcjnwb72l-infra-pull-outputs.drv'... -building '/nix/store/qnc53wy528580ri6jymimfc5z56b5p15-README.md.drv'... -building '/nix/store/6fi2818krmwh4x0sifa8i6cpsf9ml2ml-alchemy.run.ts.drv'... -building '/nix/store/bkd0qjwg0q6f9nrnal05scc69g3g81h4-database.ts.drv'... -building '/nix/store/ygv34plrfagy8v1nxnn6p7zqvvc1cz4b-iam-role.ts.drv'... -building '/nix/store/r7y2ksq5mxn6szxcj3hw7rilnrcj5sh9-index.ts.drv'... -building '/nix/store/wxq5n2nfbmwin2r57h300w72856fp737-index.ts.drv'... -building '/nix/store/kvg1i78iyrvw0r4iaqjczypv5yyvh648-infra-inputs.json.drv'... -building '/nix/store/f7sky8n2f7b91q9bjdk2rh55bs1k4smp-kms-key.ts.drv'... -building '/nix/store/5slss52mm6rz643a41476km4qj4xr9q7-policies.ts.drv'... -building '/nix/store/wf7v6zfna945d24gyd2432gzz4syzd56-types.ts.drv'... -building '/nix/store/j6mh29c2s4k921r6ryh4xbi2hjch3s3r-stack-config.json.drv'... -building '/nix/store/3h0ifrx2wqnr6a3blmndk5mzdw8q3rk1-process-compose.yaml.drv'... -building '/nix/store/y68gm6y5la3cp6z1iyf75mljw8iq4qkc-check-files-drift.drv'... -building '/nix/store/ds7dd9b4n7zgxsbmw4asl0v099wmdvpc-write-files.drv'... -building '/nix/store/yy2b24c6idk5hh48yjkfi7nc7bbrah2i-check-files.drv'... -building '/nix/store/2rhvb943syd7c6qramacbs9kvr0i8cr4-write-files.drv'... -building '/nix/store/ja2vn5kk8h6saja67f5x3gc5jhmkb2zp-stack-scripts.drv'... -building '/nix/store/m3lizyspxqhjzn9xbccfnpjqf86lqafh-stack-profile.drv'... -building '/nix/store/h3x5f1rwnb476r51chzj3cdhfx2zzj8f-stack-generate-bin.drv'... -building '/nix/store/n276vvsf76n08yjbg1zkgham3rnc5k5m-stack-gc-update.drv'... -building '/nix/store/v9991mrw2nnp2sas5wap8x2s521iapvd-stack-shellhook.drv'... -building '/nix/store/s1dawcvc4wwy11qfl7lcq0f1rhj0hcxp-stack-stack-env.drv'... -Config validated for stack -✓ Stack initialized -warning: unknown experimental feature 'build-time-fetch-tree' -warning: unknown experimental feature 'parallel-eval' -warning: unknown experimental feature 'external-builders' -warning: unknown setting 'eval-cores' -warning: unknown setting 'external-builders' -warning: unknown setting 'lazy-trees' -Checking if CA is reachable... OK -Checking if root cert is installed... OK -Checking if fingerprint matches... OK -Checking if device certificate exists... OK -Checking if device certificate is valid... OK - -All checks passed! -📜 stack scripts loaded - remove packages/infra/modules/aws-secrets.ts (stale) - write .gitignore (block, updated) - write .stack/state/infra-inputs.json - write packages/infra/README.md - write packages/infra/alchemy.run.ts - write packages/infra/modules/database.ts - write packages/infra/src/index.ts - write packages/infra/src/types.ts - write process-compose.yaml -> /nix/store/hf4j1580xwlcsiabb7ncqmyilc0bg2lv-process-compose.yaml -files: 8 written, 57 unchanged, 1 removed -Stack development environment - -📦 Stack Ports (project: stack) - Stable port: 6400 - - Apps: - docs: 5738 -> https://docs.stack.lan - stack-go: 5797 - web: 5775 -> https://stack.stack.lan - - Services: - Minio: 6498 - Minio Console: 6436 - PostgreSQL: 6404 - Redis: 6494 - - Tip: Set STACKPANEL_QUIET=1 to hide this message - - → .stack/bin: 219 binaries -Caddy config directories created at /Users/cm/.config/caddy -Caddy config directories created at /Users/cm/.config/caddy -✅ Stack Nix module system initialized -stack core initialized - | | | - __| __| _' | __| | / __ \ _' | __ \ _ \ | - \__ \ | ( | ( < | | ( | | | __/ | - ____/ \__| \__,_| \___| _|\_\ .__/ \__,_| _| _| \___| _| - _| - -╭────────────────────────────────────────────────────────────────────────╮ -│ │ -│ stackpanel Shell │ -│ Your reproducible development environment is ready │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Status │ -│ Agent ● localhost:9876 │ -│ Services ○ postgres ○ redis ○ minio ○ minio_console │ -│ Shell ⚠ stale (config changed, reload shell) │ -│ Studio http://localhost:3000/studio?project=stackpanel │ -│ │ -│ Environment │ -│ Node 22.22.0 • Bun 1.3.3 • Go 1.25.6 • Python 3.12.12 │ -│ PostgreSQL 17.7 • Redis 8.2.3 • Docker 28.5.2 │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Quick Start │ -│ dev  Start all development services │ -│ dev stop  Stop all services │ -│ sp status  Open interactive dashboard │ -│ sp commands  List all available commands │ -│ │ -│ Shortcuts │ -│ sp  = stackpanel │ -│ spx = run devshell commands │ -│ x  = same as spx │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ ⚠ Action Required │ -│ ! Shell is stale (config changed) → exit && direnv allow │ -│ │ -│ ──────────────────────────────────────────────────────────────────── │ -│ Resources │ -│ Docs https://stackpanel.dev/docs │ -│ │ -╰────────────────────────────────────────────────────────────────────────╯ - -═══════════════════════════════════════════════════════════════ -Shell hook completed at 2026-02-28 19:41:08 -Log saved to: /Users/cm/git/darkmatter/stack/.stack/state/shell.log -═══════════════════════════════════════════════════════════════ - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.9s) - -_Thinking:_ - - - -Nice catch — that error came from my directory detection logic in infra codegen. - -I fixed it by changing the detection from `readDir` probing to a safe `pathExists "/index.ts"` check, so file-backed modules (like `.../database/index.ts`) are no longer treated as directories. - -- Fixed in `nix/stack/infra/codegen.nix:36` -- Also adjusted the copy logic to only `readDir` when the module is actually directory-backed in `nix/stack/infra/codegen.nix:453` - -I verified with: - -- `nix develop --impure -c true` → succeeds now, and infra files regenerate correctly. - -You should be able to run `nix develop` again without that “cannot read directory …/index.ts” error. - ---- - -## User - -`nix develop` is hanging on "performing worker daemon op: 7" - its printing this log repeatedly in verbose mode - ---- - -## Assistant (Build · gpt-5.3-codex · 9.8s) - -_Thinking:_ - - - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "performing worker daemon op: 7|daemon op: 7|nix develop hangs|nix develop", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.md*" -} -``` -**Output:** -``` -Found 62 matches -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/secrets/master-keys.mdx: - Line 36: 2. Re-enter the devshell: exit && nix develop --impure - Line 55: nix develop --impure - Line 152: 1. **Clone and enter devshell**: `nix develop --impure` - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/secrets/index.mdx: - Line 19: nix develop --impure - Line 55: nix develop --impure - Line 152: 1. Clone the repo and enter the devshell (`nix develop --impure`) -- a local AGE key is auto-generated - -/Users/cm/git/darkmatter/stack/docs/DEPLOYMENT_FIX_CHECKLIST.md: - Line 114: nix develop --impure - Line 124: - [ ] `nix develop --impure` runs without errors - -/Users/cm/git/darkmatter/stack/docs/DOCS_UPDATE_SUMMARY.md: - Line 143: 1. Test `nix develop` with fresh config to verify no errors - -/Users/cm/git/darkmatter/stack/docs/DEPLOYMENT_MIGRATION.md: - Line 265: 4. **Rebuild your devshell** with `nix develop --impure` or `direnv reload` - -/Users/cm/git/darkmatter/stack/nix/stack/secrets/README.md: - Line 10: nix develop --impure - Line 36: nix develop --impure - -/Users/cm/git/darkmatter/stack/.ruler/architecture.md: - Line 346: 7. **Devshell builds**: Enter `nix develop --impure` to verify the Nix evaluation still succeeds - -/Users/cm/git/darkmatter/stack/AGENTS.md: - Line 398: - To enter the devshell: `nix develop --impure` - Line 400: - After you finish a task, ALWAYS try entering the devshell either by using the `devshell` script or `nix develop --impure` - Line 613: nix develop --impure # or: direnv allow (with .envrc) - Line 661: - **Primary entrypoint**: `nix develop --impure` (or `direnv allow`) - -/Users/cm/git/darkmatter/stack/CLAUDE.md: - Line 397: - To enter the devshell: `nix develop --impure` - Line 399: - After you finish a task, ALWAYS try entering the devshell either by using the `devshell` script or `nix develop --impure` - Line 612: nix develop --impure # or: direnv allow (with .envrc) - Line 660: - **Primary entrypoint**: `nix develop --impure` (or `direnv allow`) - -/Users/cm/git/darkmatter/stack/WARP.md: - Line 397: - To enter the devshell: `nix develop --impure` - Line 399: - After you finish a task, ALWAYS try entering the devshell either by using the `devshell` script or `nix develop --impure` - Line 612: nix develop --impure # or: direnv allow (with .envrc) - Line 660: - **Primary entrypoint**: `nix develop --impure` (or `direnv allow`) - -/Users/cm/git/darkmatter/stack/.ruler/stack.md: - Line 117: nix develop --impure # or: direnv allow (with .envrc) - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/ide/vscode.mdx: - Line 193: nix develop --impure - Line 205: 1. Reload the devshell (`direnv reload` or re-enter `nix develop`) - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/dev-environment/direnv.mdx: - Line 10: Direnv automatically activates your Stack dev environment when you `cd` into your project directory—and deactivates it when you leave. No manual `nix develop` required. - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/dev-environment/shell-hooks-files.mdx: - Line 11: Shell hooks run every time you enter the dev environment—whether through `direnv`, `nix develop`, or the Stack agent reloading your shell. They're useful for one-time setup, printing status information, or ensuring local state is correct. - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/dev-environment/environment-variables.mdx: - Line 152: Shell entry (direnv or nix develop) - Line 162: Variables set during Nix evaluation are **baked in at eval time**. If you change your config, you need to reload the shell (re-enter direnv or re-run `nix develop`) for the new values to take effect. - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/dev-environment/index.mdx: - Line 56: Enter the shell with `nix develop --impure` (or let direnv do it automatically), and you'll have `node`, `bun`, `jq`, and `aws` on your PATH, a `dev` command that starts your servers, and `NODE_ENV` set to `"development"`. - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/cli/preflight.mdx: - Line 24: After running this, you can use 'nix develop' (without --impure) and direnv - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/packages.mdx: - Line 63: nix develop - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/reference/devshell.mdx: - Line 124: Use `nix develop --ignore-environment --impure` with `--keep` flags - -/Users/cm/git/darkmatter/stack/nix/internal/flake/templates/minimal/README.md: - Line 21: nix develop --impure - Line 87: | `nix develop --impure` | Enter shell manually | - -/Users/cm/git/darkmatter/stack/nix/internal/flake/templates/default/README.md: - Line 19: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/internal/flake/templates/README.md: - Line 27: direnv allow # or: nix develop --impure - -/Users/cm/git/darkmatter/stack/docs/DEVENV_REMOVAL_PLAN.md: - Line 94: | `nix/stack/ide/devshell.sh` | 29,40,56,62,64,72,73 | `devenv.yaml`, `devenv enterShell` | Remove devenv mode, keep nix develop mode | - Line 140: - **Key invariant**: After Phase 1, `nix develop --impure` must produce a shell with all packages, env vars, hooks, and starship working - -/Users/cm/git/darkmatter/stack/nix/flake/templates/minimal/README.md: - Line 21: nix develop --impure - Line 87: | `nix develop --impure` | Enter shell manually | - -/Users/cm/git/darkmatter/stack/nix/flake/templates/default/README.md: - Line 19: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/flake/templates/README.md: - Line 27: direnv allow # or: nix develop --impure - -/Users/cm/git/darkmatter/stack/packages/docs-content/content/guides/devshell.mdx: - Line 17: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/packages/README.md: - Line 60: nix develop - -/Users/cm/git/darkmatter/stack/nix/stack/modules/_template/README.mdx: - Line 810: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/infra/PLAN.md: - Line 306: 3. Next `nix develop` -> outputs available at `config.stack.infra.outputs.*` - -/Users/cm/git/darkmatter/stack/nix/internal/README.md: - Line 11: nix develop --impure - Line 63: ### Standard Setup (`nix develop`) - -/Users/cm/git/darkmatter/stack/nix/README.md: - Line 120: nix develop --impure - -/Users/cm/git/darkmatter/stack/docs/MODULE_BOILERPLATE_INJECTION.md: - Line 180: **On next shell entry** (`nix develop --impure`): - -/Users/cm/git/darkmatter/stack/apps/docs/content/docs/quick-start.mdx: - Line 62: For users who prefer `devenv shell` over `nix develop`. - Line 145: nix develop --impure - Line 337: nix develop --impure - -/Users/cm/git/darkmatter/stack/README.md: - Line 63: nix develop --impure - Line 190: nix develop --impure - -/Users/cm/git/darkmatter/stack/README.tmpl.md: - Line 63: nix develop --impure - -/Users/cm/git/darkmatter/stack/.ruler/development.md: - Line 3: - To enter the devshell: `nix develop --impure` - Line 5: - After you finish a task, ALWAYS try entering the devshell either by using the `devshell` script or `nix develop --impure` - -(Some paths were inaccessible and skipped) -``` -``` - -``` -Tool: grep - -**Input:** -```json -{ - "pattern": "stack init|STACKPANEL_CONFIG_JSON|nix eval|nix develop --impure", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` -**Output:** -``` -Found 37 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix: - Line 274: 1. Enter devshell: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix: - Line 175: # Regenerate by entering the devshell: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/core/lib/evalconfig.nix: - Line 14: # 4. env fallbacks - STACKPANEL_CONFIG_JSON / STACKPANEL_STATE_DIR / STACKPANEL_ROOT / PWD - Line 18: # nix eval --json -f evalconfig.nix --argstr root /path/to/project - Line 21: # nix eval --impure --json -f evalconfig.nix - Line 69: # Config JSON path: arg > STACKPANEL_CONFIG_JSON env - Line 70: envConfigJson = builtins.getEnv "STACKPANEL_CONFIG_JSON"; - -/Users/cm/git/darkmatter/stack/nix/stack/db/default.nix: - Line 32: # nix eval --json -f nix/stack/db '.schemas' --apply builtins.attrNames - Line 35: # nix eval --raw -f nix/stack/db '.render.users' - Line 38: # nix eval --json -f nix/stack/db '.protoFiles' - -/Users/cm/git/darkmatter/stack/nix/stack/services/aws.nix: - Line 36: # Use fallback for standalone evaluation (docs generation, nix eval, etc.) - -/Users/cm/git/darkmatter/stack/nix/stack/network/network.nix: - Line 33: # Use fallback for standalone evaluation (docs generation, nix eval, etc.) - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo.test.nix: - Line 5: # Run with: nix eval -f nix/stack/modules/turbo.test.nix - -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/module.nix: - Line 408: echo "Run: nix develop --impure" >&2 - -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/module.nix: - Line 65: # Regenerate by entering the devshell: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/lib/ide.nix: - Line 208: # Fall back to devshell script or nix develop --impure if no cache - Line 216: echo "⚠️ devshell: cached env is stale (run 'nix develop --impure' to refresh)" >&2 - Line 227: exec nix develop --impure - Line 239: echo "⚠️ devshell: cached env is stale (run 'nix develop --impure' to refresh)" >&2 - -/Users/cm/git/darkmatter/stack/nix/stack/docs/readme.nix: - Line 115: nix develop --impure - -/Users/cm/git/darkmatter/stack/nix/stack/ide/ide.nix: - Line 44: # Use fallback for standalone evaluation (docs generation, nix eval, etc.) - -/Users/cm/git/darkmatter/stack/nix/stack/devshell/direnv.nix: - Line 77: # To regenerate: nix develop --impure -c stack generate - Line 140: # nix develop --impure -c stack generate - -/Users/cm/git/darkmatter/stack/nix/stack/devshell/clean.nix: - Line 81: if cfg.impure then "nix develop --impure -c devshell-regen" else "nix develop -c devshell-regen" - -/Users/cm/git/darkmatter/stack/nix/stack/core/schema.nix: - Line 15: # nix eval --json -f nix/stack/core/schema.nix jsonSchema - Line 18: # nix eval --json -f ... jsonSchema | bun x quicktype -s schema -o types.ts --lang typescript - - -/Users/cm/git/darkmatter/stack/nix/stack/core/state.nix: - Line 37: # Use fallback for standalone evaluation (docs generation, nix eval, etc.) - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/panels.nix: - Line 22: # The panels are exposed via nix eval for the agent/web UI to consume. - Line 355: # Computed/serializable panels for nix eval - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/extensions.nix: - Line 39: # The extensions are exposed via nix eval for the agent/web UI to consume. - Line 480: # Expose computed/serializable extensions for nix eval - -/Users/cm/git/darkmatter/stack/nix/stack/core/cli.nix: - Line 46: # Use fallback for standalone evaluation (docs generation, nix eval, etc.) - Line 59: # This avoids slow nix eval at runtime by pre-computing during shell entry - Line 234: # Path to the source Nix config file (for nix eval/import) - Line 237: STACKPANEL_CONFIG_JSON = "${configFile}"; - -/Users/cm/git/darkmatter/stack/nix/stack/core/lib/envvars.nix: - Line 125: STACKPANEL_CONFIG_JSON = mkEnvVar { - Line 126: name = "STACKPANEL_CONFIG_JSON"; -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/.stack/state/shellhook.sh" -} -``` -**Output:** -``` - -00001| # ================================================================ -00002| # Stack Shell Hook (wrapper) -00003| # Generated by: nix/flake/default.nix -00004| # ================================================================ -00005| -00006| __stackpanel_shell_hook_main() { -00007| # Export environment variables (includes GOPATH, GOROOT from devenv languages.*) -00008| export AWS_CONFIG_FILE=/nix/store/13yqr93hm27784x31l0vmf6aaa82vik1-aws-config -00009| export AWS_REGION=us-west-2 -00010| export AWS_SHARED_CREDENTIALS_FILE=/dev/null -00011| export CHAMBER_KMS_KEY_ALIAS=alias/stack-secrets -00012| export DATABASE_URL='postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres' -00013| export GOROOT=/nix/store/rwzizip34lp556kmagcfw0fszch22x0k-go-1.25.6/share/go/ -00014| export GOTOOLCHAIN=local -00015| export MINIO_ACCESS_KEY=minioadmin -00016| export MINIO_CONSOLE_ADDRESS=:6436 -00017| export MINIO_ENDPOINT=http://localhost:6498 -00018| export MINIO_ROOT_PASSWORD=minioadmin -00019| export MINIO_ROOT_USER=minioadmin -00020| export MINIO_SECRET_KEY=minioadmin -00021| export PC_PORT_NUM=6490 -00022| export PGDATA='$HOME/.local/share/devservices/postgres/data' -00023| export PGDATABASE=postgres -00024| export PGHOST='$HOME/.local/share/devservices/postgres' -00025| export PGPORT=6404 -00026| export PGUSER=postgres -00027| export PORT_DOCS=6400 -00028| export PORT_STACKPANEL_GO=6401 -00029| export PORT_WEB=6402 -00030| export POSTGRES_URL='postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres' -00031| export REDIS_HOST=localhost -00032| export REDIS_PORT=6494 -00033| export REDIS_SOCKET='$HOME/.local/share/devservices/redis/redis.sock' -00034| export REDIS_URL=redis://localhost:6494 -00035| export S3_ENDPOINT=http://localhost:6498 -00036| export STACKPANEL_ALIAS_FUNC='# Internal helper shared by shortcuts -00037| _stackpanel_commands_runner() { -00038| if [ $# -eq 0 ]; then -00039| stack commands list -00040| else -00041| stack commands "$@" -00042| fi -00043| -00044| } -00045| -00046| # Stack command shortcut: x -00047| x() { -00048| _stackpanel_commands_runner "$@" -00049| } -00050| -00051| # Short alias for the stack CLI -00052| alias sp="stack" -00053| -00054| # Explicit commands runner shortcut -00055| spx() { -00056| _stackpanel_commands_runner "$@" -00057| } -00058| ' -00059| export STACKPANEL_CLEAN_IMPURE=0 -00060| export STACKPANEL_CLEAN_MODE=0 -00061| export STACKPANEL_CONFIG_JSON=/nix/store/0spc4rfa1lm9vzclkww00qmbq69xkv73-stack-config.json -00062| export STACKPANEL_INFRA_INPUTS=.stack/state/infra-inputs.json -00063| export STACKPANEL_NIX_CONFIG='$STACKPANEL_ROOT/.stack/config.nix' -00064| export STACKPANEL_REMOTE_BUILDER_HOST=100.102.113.26 -00065| export STACKPANEL_ROOT_DIR_NAME=.stack -00066| export STACKPANEL_ROOT_MARKER=.stack-root -00067| export STACKPANEL_SERVICES_CONFIG='[{"key":"MINIO","name":"Minio","port":6498},{"key":"MINIO_CONSOLE","name":"Minio Console","port":6436},{"key":"POSTGRES","name":"PostgreSQL","port":6404},{"key":"REDIS","name":"Redis","port":6494}]' -00068| export STACKPANEL_STABLE_PORT=6400 -00069| export URL_DOCS=https://docs.stack.lan -00070| export URL_WEB=https://stack.stack.lan -00071| export __STACKPANEL_CLEAN_ENV='' -00072| -00073| # Add language bin directories to PATH (replaces devenv's enterShell PATH modifications) -00074| # This handles languages.go ($GOPATH/bin), languages.rust ($CARGO_HOME/bin), etc. -00075| if [[ -n "${GOPATH:-}" ]]; then -00076| export PATH="$GOPATH/bin:$PATH" -00077| fi -00078| if [[ -n "${CARGO_HOME:-}" ]]; then -00079| export PATH="$CARGO_HOME/bin:$PATH" -00080| fi -00081| -00082| # Stack hooks (all output automatically goes to stderr) -00083| # Clean up stale git hooks that reference garbage-collected nix store paths -00084| _cleanup_stale_git_hooks() { -00085| local git_dir hooks_dir -00086| git_dir="$(git rev-parse --git-dir 2>/dev/null)" || return 0 -00087| hooks_dir="$git_dir/hooks" -00088| -00089| [[ -d "$hooks_dir" ]] || return 0 -00090| -00091| local hook_files=("pre-commit" "pre-push" "commit-msg" "prepare-commit-msg") -00092| -00093| for hook in "${hook_files[@]}"; do -00094| local hook_path="$hooks_dir/$hook" -00095| [[ -f "$hook_path" ]] || continue -00096| -00097| # Check if hook references a nix store path -00098| if grep -q '/nix/store/' "$hook_path" 2>/dev/null; then -00099| # Extract nix store paths and check if they exist -00100| local store_paths -00101| store_paths=$(grep -oE '/nix/store/[a-z0-9]+-[^/"]+' "$hook_path" 2>/dev/null | head -5) -00102| -00103| local any_missing=false -00104| while IFS= read -r store_path; do -00105| [[ -z "$store_path" ]] && continue -00106| if [[ ! -e "$store_path" ]]; then -00107| any_missing=true -00108| break -00109| fi -00110| done <<< "$store_paths" -00111| -00112| if [[ "$any_missing" == "true" ]]; then -00113| rm -f "$hook_path" -00114| echo "Removed stale git hook: $hook (referenced garbage-collected nix store path)" >&2 -00115| fi -00116| fi -00117| done -00118| } -00119| _cleanup_stale_git_hooks -00120| -00121| -00122| export AWS_CONFIG_FILE=/nix/store/13yqr93hm27784x31l0vmf6aaa82vik1-aws-config -00123| export AWS_REGION=us-west-2 -00124| export AWS_SHARED_CREDENTIALS_FILE=/dev/null -00125| export CHAMBER_KMS_KEY_ALIAS=alias/stack-secrets -00126| export DATABASE_URL='postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres' -00127| export GOROOT=/nix/store/rwzizip34lp556kmagcfw0fszch22x0k-go-1.25.6/share/go/ -00128| export GOTOOLCHAIN=local -00129| export MINIO_ACCESS_KEY=minioadmin -00130| export MINIO_CONSOLE_ADDRESS=:6436 -00131| export MINIO_ENDPOINT=http://localhost:6498 -00132| export MINIO_ROOT_PASSWORD=minioadmin -00133| export MINIO_ROOT_USER=minioadmin -00134| export MINIO_SECRET_KEY=minioadmin -00135| export PC_PORT_NUM=6490 -00136| export PGDATA='$HOME/.local/share/devservices/postgres/data' -00137| export PGDATABASE=postgres -00138| export PGHOST='$HOME/.local/share/devservices/postgres' -00139| export PGPORT=6404 -00140| export PGUSER=postgres -00141| export PORT_DOCS=6400 -00142| export PORT_STACKPANEL_GO=6401 -00143| export PORT_WEB=6402 -00144| export POSTGRES_URL='postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres' -00145| export REDIS_HOST=localhost -00146| export REDIS_PORT=6494 -00147| export REDIS_SOCKET='$HOME/.local/share/devservices/redis/redis.sock' -00148| export REDIS_URL=redis://localhost:6494 -00149| export S3_ENDPOINT=http://localhost:6498 -00150| export STACKPANEL_ALIAS_FUNC='# Internal helper shared by shortcuts -00151| _stackpanel_commands_runner() { -00152| if [ $# -eq 0 ]; then -00153| stack commands list -00154| else -00155| stack commands "$@" -00156| fi -00157| -00158| } -00159| -00160| # Stack command shortcut: x -00161| x() { -00162| _stackpanel_commands_runner "$@" -00163| } -00164| -00165| # Short alias for the stack CLI -00166| alias sp="stack" -00167| -00168| # Explicit commands runner shortcut -00169| spx() { -00170| _stackpanel_commands_runner "$@" -00171| } -00172| ' -00173| export STACKPANEL_CLEAN_IMPURE=0 -00174| export STACKPANEL_CLEAN_MODE=0 -00175| export STACKPANEL_CONFIG_JSON=/nix/store/0spc4rfa1lm9vzclkww00qmbq69xkv73-stack-config.json -00176| export STACKPANEL_INFRA_INPUTS=.stack/state/infra-inputs.json -00177| export STACKPANEL_NIX_CONFIG='$STACKPANEL_ROOT/.stack/config.nix' -00178| export STACKPANEL_REMOTE_BUILDER_HOST=100.102.113.26 -00179| export STACKPANEL_ROOT_DIR_NAME=.stack -00180| export STACKPANEL_ROOT_MARKER=.stack-root -00181| export STACKPANEL_SERVICES_CONFIG='[{"key":"MINIO","name":"Minio","port":6498},{"key":"MINIO_CONSOLE","name":"Minio Console","port":6436},{"key":"POSTGRES","name":"PostgreSQL","port":6404},{"key":"REDIS","name":"Redis","port":6494}]' -00182| export STACKPANEL_STABLE_PORT=6400 -00183| export URL_DOCS=https://docs.stack.lan -00184| export URL_WEB=https://stack.stack.lan -00185| export __STACKPANEL_CLEAN_ENV='' -00186| -00187| # Unset conflicting aliases -00188| unalias dev 2>/dev/null || true -00189| -00190| -00191| set -euo pipefail -00192| -00193| # Stack path utilities -00194| stackpanel_find_root() { -00195| local dir="$PWD" -00196| local marker=".stack-root" -00197| -00198| # First, try to find the marker file by walking up the directory tree -00199| while [[ "$dir" != "/" ]]; do -00200| if [[ -f "$dir/$marker" ]]; then -00201| cat "$dir/$marker" -00202| return 0 -00203| fi -00204| dir="$(dirname "$dir")" -00205| done -00206| -00207| # Fallback 1: check if STACKPANEL_ROOT is already set -00208| if [[ -n "${STACKPANEL_ROOT:-}" ]]; then -00209| echo "$STACKPANEL_ROOT" -00210| return 0 -00211| fi -00212| -00213| # Fallback 2: use git repository root if available -00214| # This handles running `nix develop` from subdirectories before marker exists -00215| if command -v git >/dev/null 2>&1; then -00216| local git_root -00217| git_root="$(git rev-parse --show-toplevel 2>/dev/null)" || true -00218| if [[ -n "$git_root" ]]; then -00219| echo "$git_root" -00220| return 0 -00221| fi -00222| fi -00223| -00224| # Fallback 3: look for flake.nix by walking up from PWD -00225| dir="$PWD" -00226| while [[ "$dir" != "/" ]]; do -00227| if [[ -f "$dir/flake.nix" ]]; then -00228| echo "$dir" -00229| return 0 -00230| fi -00231| dir="$(dirname "$dir")" -00232| done -00233| -00234| echo "Error: Could not find stack root (no $marker, git repo, or flake.nix found)" >&2 -00235| return 1 -00236| } -00237| -00238| stackpanel_resolve_paths() { -00239| local root="${1:-$(stackpanel_find_root)}" -00240| if [[ -z "$root" ]]; then -00241| return 1 -00242| fi -00243| if [[ ! -d "$root" ]]; then -00244| echo "Error: Resolved stack root is not a directory: $root" -00245| echo "You may need to run on your stack root dir:" -00246| echo -00247| echo " echo \"\$PWD\" > .stack-root" -00248| echo -00249| return 1 -00250| fi -00251| export STACKPANEL_ROOT="$root" -00252| export STACKPANEL_ROOT_DIR="$root/.stack" -00253| export STACKPANEL_STATE_DIR="$root/.stack/state" -00254| export STACKPANEL_GEN_DIR="$root/.stack/gen" -00255| } -00256| -00257| -00258| -00259| # If stack.root was provided, prefer it; otherwise resolve via marker walking -00260| if [[ -n "${STACKPANEL_ROOT:-}" ]]; then -00261| stackpanel_resolve_paths "$STACKPANEL_ROOT" -00262| else -00263| stackpanel_resolve_paths -00264| fi -00265| -00266| mkdir -p "$STACKPANEL_STATE_DIR" "$STACKPANEL_GEN_DIR" -00267| -00268| # Shell logging disabled by default (requires bash for process substitution) -00269| # Set STACKPANEL_SHELL_LOG before entering shell to enable -00270| # if [[ -z "${STACKPANEL_SHELL_LOG:-}" ]]; then -00271| # export STACKPANEL_SHELL_LOG="$STACKPANEL_STATE_DIR/shell.log" -00272| # fi -00273| -00274| # Ensure marker exists at repo root -00275| if [[ ! -f "$STACKPANEL_ROOT/.stack-root" ]]; then -00276| echo "$STACKPANEL_ROOT" > "$STACKPANEL_ROOT/.stack-root" -00277| fi -00278| -00279| # Compute shell freshness hash from config files -00280| # This is used to detect when the shell is stale (config changed but shell not reloaded) -00281| _sp_compute_shell_hash() { -00282| local files=( -00283| "$STACKPANEL_ROOT/flake.nix" -00284| "$STACKPANEL_ROOT/flake.lock" -00285| "$STACKPANEL_ROOT/.stack/config.nix" -00286| "$STACKPANEL_ROOT/devenv.nix" -00287| "$STACKPANEL_ROOT/devenv.yaml" -00288| ) -00289| local hash_input="" -00290| for f in "${files[@]}"; do -00291| if [[ -f "$f" ]]; then -00292| hash_input+="$(cat "$f" 2>/dev/null)" -00293| fi -00294| done -00295| echo -n "$hash_input" | md5sum | cut -d' ' -f1 -00296| } -00297| export STACKPANEL_SHELL_HASH="$(_sp_compute_shell_hash)" -00298| export STACKPANEL_SHELL_HASH_TIME="$(date +%s)" -00299| -00300| # Restore default interactive shell behavior (disable strict error handling) -00301| set +euo pipefail -00302| -00303| -00304| # Warn if using STACKPANEL_CONFIG_OVERRIDE (prefer config.local.nix) -00305| if [[ -n "${STACKPANEL_CONFIG_OVERRIDE:-}" ]]; then -00306| echo "⚠️ STACKPANEL_CONFIG_OVERRIDE is set - config.local.nix is preferred for local development" >&2 -00307| fi -00308| -00309| # Generate stack files via CLI -00310| # Read config from nix store, replace $STACKPANEL_ROOT with actual value -00311| export STACKPANEL_STATE_FILE="$STACKPANEL_STATE_DIR/stack.json" -00312| _sp_config=$(cat /nix/store/0spc4rfa1lm9vzclkww00qmbq69xkv73-stack-config.json | sed "s|\\\$STACKPANEL_ROOT|$STACKPANEL_ROOT|g") -00313| echo "$_sp_config" | /nix/store/q3dl70gg26y206cyw1lw2z1dnfnm8pfx-stack-0.1.0/bin/stack hook -00314| -00315| # Write the state file for Go agent/CLI consumption -00316| mkdir -p "$STACKPANEL_STATE_DIR" -00317| echo "$_sp_config" > "$STACKPANEL_STATE_FILE" -00318| -00319| -00320| ( -00321| # syntax: bash -00322| # ========================================================================== -00323| # sp_get - Resolve a config value with precedence -00324| # -00325| # Precedence (first non-empty wins): -00326| # 1. Environment variable: STACKPANEL_ -00327| # 2. CLI query: stack config get -00328| # 3. Default: passed as second argument -00329| # -00330| # Usage: -00331| # STATE_DIR=$(sp_get "paths.state" ".stack/state") -00332| # KEYS_DIR=$(sp_get "paths.keys" ".stack/state/keys") -00333| # -00334| # ========================================================================== -00335| sp_get() { -00336| local path="$1" -00337| local default -00338| default="${2:-}" -00339| -00340| # Convert path to env var name: paths.local-key -> STACKPANEL_PATHS_LOCAL_KEY -00341| local env_var -00342| env_var="STACKPANEL_$(echo "$path" | tr '[:lower:].-' '[:upper:]__')" -00343| -00344| # Try env var first (indirect expansion) -00345| local env_val -00346| env_val="${!env_var:-}" -00347| if [[ -n "$env_val" ]]; then -00348| echo "$env_val" -00349| return 0 -00350| fi -00351| -00352| # Try CLI query (if stack is available) -00353| # NOTE: Disabled until 'stack config get' subcommand is implemented. -00354| # Currently 'stack config' only has 'check' and 'sync', so -00355| # 'stack config get ' returns Cobra help text (exit 0) -00356| # which gets captured as the value, causing shell entry failures. -00357| # if command -v stack &>/dev/null; then -00358| # local cli_val -00359| # if cli_val=$(stack config get "$path" 2>/dev/null) && [[ -n "$cli_val" ]]; then -00360| # echo "$cli_val" -00361| # return 0 -00362| # fi -00363| # fi -00364| -00365| # Fall back to default -00366| echo "$default" -00367| } -00368| -00369| -00370| STATE_DIR=$(sp_get "paths.state" ".stack/state") -00371| KEYS_DIR=$(sp_get "paths.keys" ".stack/state/keys") -00372| LOCAL_KEY=$(sp_get "paths.local-key" ".stack/state/keys/local.txt") -00373| LOCAL_PUB=$(sp_get "paths.local-pub" ".stack/state/keys/local.pub") -00374| RECIPIENTS_DIR=$(sp_get "secrets.recipients-dir" ".stack/secrets/recipients") -00375| ARCHIVE_DIR=$(sp_get "secrets.recipients-archive-dir" ".stack/secrets/recipients/.archive") -00376| -00377| # ── Step 1: Generate local key if it doesn't exist (first time only) ── -00378| if [[ ! -f "$LOCAL_KEY" ]]; then -00379| echo "Generating local AGE key..." >&2 -00380| -00381| mkdir -p "$KEYS_DIR" -00382| chmod 700 "$KEYS_DIR" -00383| -00384| /nix/store/av8lj7gcdw8jnyrqm7ww66lmflcmm3yz-age-1.3.1/bin/age-keygen -o "$LOCAL_KEY" 2>/dev/null -00385| chmod 600 "$LOCAL_KEY" -00386| -00387| PUBLIC_KEY=$(/nix/store/av8lj7gcdw8jnyrqm7ww66lmflcmm3yz-age-1.3.1/bin/age-keygen -y "$LOCAL_KEY") -00388| echo "$PUBLIC_KEY" > "$LOCAL_PUB" -00389| -00390| echo "" >&2 -00391| echo "Local AGE key generated:" >&2 -00392| echo " Private: $LOCAL_KEY" >&2 -00393| echo " Public: $PUBLIC_KEY" >&2 -00394| echo "" >&2 -00395| echo "To join the team, run: secrets:join" >&2 -00396| fi -00397| -00398| # ── Step 2: Rebuild recipients/.sops.yaml ── -00399| # Collects ALL public keys from recipients/ (flat + group subdirs). -00400| # Supports both *.age.pub (native AGE) and *.ed25519.pub (SSH, converted via ssh-to-age). -00401| mkdir -p "$RECIPIENTS_DIR" -00402| RECIPIENTS_SOPS_YAML="$RECIPIENTS_DIR/.sops.yaml" -00403| -00404| ALL_PUBS="" -00405| if [[ -d "$RECIPIENTS_DIR" ]]; then -00406| # Helper: collect AGE public keys from a directory -00407| collect_pubs_from_dir() { -00408| local dir="$1" -00409| # Native AGE keys (*.age.pub) -00410| for pub_file in "$dir"/*.age.pub; do -00411| [[ -f "$pub_file" ]] || continue -00412| local PUB_KEY -00413| PUB_KEY=$(cat "$pub_file" | tr -d '[:space:]') -00414| if [[ -n "$PUB_KEY" ]]; then -00415| ALL_PUBS="$ALL_PUBS $PUB_KEY" -00416| fi -00417| done -00418| # SSH ED25519 keys (*.ed25519.pub) — converted to AGE via ssh-to-age -00419| for pub_file in "$dir"/*.ed25519.pub; do -00420| [[ -f "$pub_file" ]] || continue -00421| if command -v ssh-to-age &>/dev/null; then -00422| local AGE_KEY -00423| AGE_KEY=$(ssh-to-age < "$pub_file" 2>/dev/null) || true -00424| if [[ -n "$AGE_KEY" && "$AGE_KEY" == age1* ]]; then -00425| ALL_PUBS="$ALL_PUBS $AGE_KEY" -00426| else -00427| echo "Warning: Could not convert $(basename "$pub_file") to AGE key" >&2 -00428| fi -00429| else -00430| echo "Warning: ssh-to-age not found, skipping $(basename "$pub_file")" >&2 -00431| fi -00432| done -00433| # Reject RSA keys with clear error -00434| for pub_file in "$dir"/*.rsa.pub; do -00435| [[ -f "$pub_file" ]] || continue -00436| echo "Error: RSA keys are not supported by age. Remove $(basename "$pub_file")" >&2 -00437| done -00438| } -00439| -00440| # Flat recipient keys -00441| collect_pubs_from_dir "$RECIPIENTS_DIR" -00442| -00443| # Group subdirectory keys (team/, admins/, etc.) -00444| for group_dir in "$RECIPIENTS_DIR"/*/; do -00445| [[ -d "$group_dir" ]] || continue -00446| [[ "$(basename "$group_dir")" == ".archive" ]] && continue -00447| collect_pubs_from_dir "$group_dir" -00448| done -00449| fi -00450| -00451| # Trim leading space and deduplicate -00452| ALL_PUBS=$(echo "$ALL_PUBS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/^ //;s/ $//') -00453| -00454| # Check if regeneration is needed -00455| NEEDS_REGEN=false -00456| if [[ ! -f "$RECIPIENTS_SOPS_YAML" ]]; then -00457| NEEDS_REGEN=true -00458| elif grep -q "age: \[\]" "$RECIPIENTS_SOPS_YAML" 2>/dev/null; then -00459| NEEDS_REGEN=true -00460| else -00461| EXISTING_PUBS=$(grep -oE 'age1[a-z0-9]+' "$RECIPIENTS_SOPS_YAML" 2>/dev/null | sort -u | tr '\n' ' ' | sed 's/ $//') -00462| EXPECTED_PUBS=$(echo "$ALL_PUBS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/ $//') -00463| if [[ "$EXISTING_PUBS" != "$EXPECTED_PUBS" ]]; then -00464| NEEDS_REGEN=true -00465| fi -00466| fi -00467| -00468| if [[ "$NEEDS_REGEN" == "true" && -n "$ALL_PUBS" ]]; then -00469| AGE_LINES="" -00470| for PUB_KEY in $ALL_PUBS; do -00471| AGE_LINES="$AGE_LINES -00472| - $PUB_KEY" -00473| done -00474| -00475| cat > "$RECIPIENTS_SOPS_YAML" << SOPSEOF -00476| # SOPS configuration for group encryption keys -00477| # -00478| # Auto-generated by stack from .stack/secrets/recipients/**/*.{age,ed25519}.pub -00479| # DO NOT EDIT - changes will be overwritten on devshell entry. -00480| # To add recipients, add .age.pub or .ed25519.pub files to a recipient group -00481| # directory (e.g., recipients/team/) and re-enter the devshell. -00482| -00483| creation_rules: -00484| - path_regex: \.enc\.age$ -00485| key_groups: -00486| - age:$AGE_LINES -00487| SOPSEOF -00488| echo "Generated $RECIPIENTS_SOPS_YAML with $(echo "$ALL_PUBS" | wc -w | tr -d ' ') recipient(s)" >&2 -00489| fi -00490| -00491| # ── Step 3: Check decrypt access ── -00492| # Verify the user's public key is listed as a recipient in the first -00493| # available .enc.age file. The .enc.age files are SOPS-encrypted JSON, -00494| # so we grep for the pubkey in the "recipient" fields rather than -00495| # attempting raw AGE decryption (which would fail on SOPS JSON format). -00496| PUBLIC_KEY=$(cat "$LOCAL_PUB" 2>/dev/null | tr -d '[:space:]') -00497| FIRST_ENC_AGE="" -00498| for enc_file in "$RECIPIENTS_DIR"/*.enc.age; do -00499| [[ -f "$enc_file" ]] || continue -00500| FIRST_ENC_AGE="$enc_file" -00501| break -00502| done -00503| if [[ -n "$FIRST_ENC_AGE" && -n "$PUBLIC_KEY" ]]; then -00504| if ! grep -q "$PUBLIC_KEY" "$FIRST_ENC_AGE" 2>/dev/null; then -00505| echo "" >&2 -00506| echo "╭─ Secrets ──────────────────────────────────────────────────╮" >&2 -00507| echo "│ Your key is not yet registered as a team recipient. │" >&2 -00508| echo "│ │" >&2 -00509| echo "│ To join the team, run: │" >&2 -00510| echo "│ secrets:join │" >&2 -00511| echo "│ │" >&2 -00512| echo "│ This will add your public key to recipients/team/ and │" >&2 -00513| echo "│ push it. A teammate or CI will rekey to grant access. │" >&2 -00514| echo "╰────────────────────────────────────────────────────────────╯" >&2 -00515| fi -00516| fi -00517| -00518| ) -00519| -00520| -00521| ( -00522| # ========================================================================== -00523| # sp_get - Resolve a config value with precedence -00524| # -00525| # Precedence (first non-empty wins): -00526| # 1. Environment variable: STACKPANEL_ -00527| # 2. CLI query: stack config get -00528| # 3. Default: passed as second argument -00529| # -00530| # Usage: -00531| # STATE_DIR=$(sp_get "paths.state" ".stack/state") -00532| # KEYS_DIR=$(sp_get "paths.keys" ".stack/state/keys") -00533| # -00534| # ========================================================================== -00535| sp_get() { -00536| local path="$1" -00537| local default -00538| default="${2:-}" -00539| -00540| # Convert path to env var name: paths.local-key -> STACKPANEL_PATHS_LOCAL_KEY -00541| local env_var -00542| env_var="STACKPANEL_$(echo "$path" | tr '[:lower:].-' '[:upper:]__')" -00543| -00544| # Try env var first (indirect expansion) -00545| local env_val -00546| env_val="${!env_var:-}" -00547| if [[ -n "$env_val" ]]; then -00548| echo "$env_val" -00549| return 0 -00550| fi -00551| -00552| # Try CLI query (if stack is available) -00553| # NOTE: Disabled until 'stack config get' subcommand is implemented. -00554| # Currently 'stack config' only has 'check' and 'sync', so -00555| # 'stack config get ' returns Cobra help text (exit 0) -00556| # which gets captured as the value, causing shell entry failures. -00557| # if command -v stack &>/dev/null; then -00558| # local cli_val -00559| # if cli_val=$(stack config get "$path" 2>/dev/null) && [[ -n "$cli_val" ]]; then -00560| # echo "$cli_val" -00561| # return 0 -00562| # fi -00563| # fi -00564| -00565| # Fall back to default -00566| echo "$default" -00567| } -00568| -00569| -00570| VARS_DIR=$(sp_get "secrets.vars-dir" ".stack/secrets/vars") -00571| GROUPS_JSON='{"dev":"age1783kahlc7yxv2md9vtpx4wq899csvqxty03fatcs5s7lqfh5334s6p7r0l","prod":"age1tvczw6y7g4v0ma7cn05adrnst9jnnsh9j8ge0t0flls8ucq5yg9qe37jhe"}' -00572| -00573| mkdir -p "$VARS_DIR" -00574| -00575| # Build .sops.yaml from the Nix-defined group public keys. -00576| # Each group YAML file is encrypted to that group's AGE key. -00577| # Plaintext values use unencrypted_comment_regex to mark with "# safe". -00578| SOPS_YAML="$VARS_DIR/.sops.yaml" -00579| -00580| # Build expected content -00581| EXPECTED="# Auto-generated from config.nix — DO NOT EDIT. -00582| # Group public keys are defined in stack.secrets.groups..age-pub. -00583| # To change a group key, update config.nix and re-enter the devshell. -00584| # -00585| # Values preceded by a '# safe' comment are stored in plaintext. -00586| # All other values are encrypted. -00587| creation_rules:" -00588| -00589| # Add a creation rule per group -00590| for GROUP_NAME in $(echo "$GROUPS_JSON" | jq -r 'keys[]' | sort); do -00591| # Skip "common" — it gets a special rule below with ALL group keys -00592| [[ "$GROUP_NAME" == "common" ]] && continue -00593| AGE_PUB=$(echo "$GROUPS_JSON" | jq -r --arg g "$GROUP_NAME" '.[$g]') -00594| EXPECTED="${EXPECTED} -00595| - path_regex: ^${GROUP_NAME}\\.sops\\.yaml$ -00596| unencrypted_comment_regex: \"^# safe$\" -00597| key_groups: -00598| - age: -00599| - ${AGE_PUB}" -00600| done -00601| -00602| # Add a creation rule for common.sops.yaml encrypted to ALL group keys. -00603| # Anyone with any group's private key can decrypt common. -00604| ALL_AGE_PUBS="" -00605| for GROUP_NAME in $(echo "$GROUPS_JSON" | jq -r 'keys[]' | sort); do -00606| [[ "$GROUP_NAME" == "common" ]] && continue -00607| AGE_PUB=$(echo "$GROUPS_JSON" | jq -r --arg g "$GROUP_NAME" '.[$g]') -00608| ALL_AGE_PUBS="${ALL_AGE_PUBS} -00609| - ${AGE_PUB}" -00610| done -00611| if [[ -n "$ALL_AGE_PUBS" ]]; then -00612| EXPECTED="${EXPECTED} -00613| - path_regex: ^common\\.sops\\.yaml$ -00614| unencrypted_comment_regex: \"^# safe$\" -00615| key_groups: -00616| - age:${ALL_AGE_PUBS}" -00617| fi -00618| -00619| # Only rewrite if content changed -00620| if [[ -f "$SOPS_YAML" ]]; then -00621| CURRENT=$(cat "$SOPS_YAML") -00622| else -00623| CURRENT="" -00624| fi -00625| -00626| if [[ "$EXPECTED" != "$CURRENT" ]]; then -00627| echo "$EXPECTED" > "$SOPS_YAML" -00628| echo "Generated $SOPS_YAML from config.nix group keys" >&2 -00629| fi -00630| -00631| ) -00632| -00633| -00634| ( -00635| SECRETS_STATE_DIR=".stack/secrets/state" -00636| mkdir -p "$SECRETS_STATE_DIR" -00637| MANIFEST_FILE="$SECRETS_STATE_DIR/manifest.json" -00638| MANIFEST_CONTENT='{"groups":{"common":{"initialized":false,"keyedTo":"all-group-keys"},"dev":{"initialized":true}},"keysByGroup":{"dev":["openrouter-api-key","test-api-key"]}}' -00639| # Only rewrite if content changed -00640| if [[ ! -f "$MANIFEST_FILE" ]] || [[ "$(cat "$MANIFEST_FILE" 2>/dev/null)" != "$MANIFEST_CONTENT" ]]; then -00641| echo "$MANIFEST_CONTENT" | /nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin/jq '.' > "$MANIFEST_FILE" -00642| fi -00643| ) -00644| -00645| -00646| # Linux builder detection for container builds -00647| __stackpanel_check_linux_builder() { -00648| # Skip on Linux -00649| if [[ "$(uname)" != "Darwin" ]]; then -00650| return 0 -00651| fi -00652| -00653| local builder_status -00654| builder_status="$(/nix/store/jvni9vclw84l9hhy5ls2hir580xhm48a-check-linux-builder)" -00655| -00656| case "$builder_status" in -00657| native|determinate|external) -00658| # Native or Determinate Nix builder available -00659| export STACKPANEL_LINUX_BUILDER="$builder_status" -00660| ;; -00661| remote) -00662| # Remote builder configured -00663| export STACKPANEL_LINUX_BUILDER="remote" -00664| ;; -00665| none) -00666| export STACKPANEL_LINUX_BUILDER="none" -00667| echo "" >&2 -00668| echo "⚠️ No Linux builder configured for container builds" >&2 -00669| echo "" >&2 -00670| echo "Options to enable Linux builds on macOS:" >&2 -00671| echo "" >&2 -00672| echo " 1. Determinate Nix Native Builder (recommended):" >&2 -00673| echo " - Log in to FlakeHub: determinate-nixd auth login" >&2 -00674| echo " - Request access at: support@determinate.systems" >&2 -00675| echo " - Restart daemon: sudo launchctl kickstart -k system/systems.determinate.nix-daemon" >&2 -00676| echo "" >&2 -00677| echo " 2. Remote Builder (auto-configure):" >&2 -00678| echo " Run: configure-linux-builder" >&2 -00679| echo "" >&2 -00680| -00681| echo " 3. Manual remote builder setup:" >&2 -00682| echo " See: https://nixcademy.com/posts/macos-linux-builder/" >&2 -00683| echo "" >&2 -00684| -00685| ;; -00686| esac -00687| } -00688| __stackpanel_check_linux_builder -00689| -00690| -00691| if [[ "false" == "true" ]]; then -00692| if [[ -n "darkmatter" ]]; then -00693| export CACHIX_CACHE_NAME="darkmatter" -00694| fi -00695| -00696| if [[ -n "" ]]; then -00697| if [[ -f "" ]]; then -00698| export CACHIX_AUTH_TOKEN="$(cat "")" -00699| fi -00700| fi -00701| fi -00702| -00703| -00704| eval "$STACKPANEL_ALIAS_FUNC" -00705| -00706| # Set environment variables for Minio -00707| export MINIO_ROOT_USER="minioadmin" -00708| export MINIO_ROOT_PASSWORD="minioadmin" -00709| export MINIO_ENDPOINT="http://localhost:6498" -00710| export MINIO_ACCESS_KEY="minioadmin" -00711| export MINIO_SECRET_KEY="minioadmin" -00712| export S3_ENDPOINT="http://localhost:6498" -00713| -00714| -00715| # Set environment variables for PostgreSQL -00716| export PGDATA="$HOME/.local/share/devservices/postgres/data" -00717| export PGHOST="$HOME/.local/share/devservices/postgres" -00718| export PGPORT="6404" -00719| export PGDATABASE="postgres" -00720| export PGUSER="postgres" -00721| export DATABASE_URL="postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres" -00722| export POSTGRES_URL="postgresql://postgres@localhost:6404/postgres?host=$HOME/.local/share/devservices/postgres" -00723| -00724| -00725| # Set environment variables for Redis -00726| export REDIS_URL="redis://localhost:6494" -00727| export REDIS_HOST="localhost" -00728| export REDIS_PORT="6494" -00729| export REDIS_SOCKET="$HOME/.local/share/devservices/redis/redis.sock" -00730| -00731| -00732| # syntax: bash -00733| # Set the config path for starship -00734| # Use STACKPANEL_STATE_DIR (native) or DEVENV_STATE (devenv) -00735| _starship_state_dir="${STACKPANEL_STATE_DIR:-${DEVENV_STATE:-$PWD/.stack/state}}" -00736| mkdir -p "$_starship_state_dir" -00737| -00738| if [[ -n "/nix/store/v5jf98cyrj8jryvl24pvgw3xrjgyr45a-starship-config/starship.toml" ]]; then -00739| export STARSHIP_CONFIG="$_starship_state_dir/starship.toml" -00740| install -m 644 /nix/store/v5jf98cyrj8jryvl24pvgw3xrjgyr45a-starship-config/starship.toml "$_starship_state_dir/starship.toml" -00741| else -00742| unset STARSHIP_CONFIG -00743| fi -00744| -00745| # Only initialize starship here if we're in a direct `devenv shell` (bash) -00746| # When using direnv, the user's shell rc file handles starship init -00747| if [[ -z "${DIRENV_IN_ENVRC:-}" ]]; then -00748| eval "$(starship init bash)" -00749| fi -00750| -00751| -00752| # JavaScript toolchain: auto-install bun dependencies -00753| source /nix/store/xhv8fg110hqb2g744jvq129gzv496gpm-stack-bun-install -00754| -00755| -00756| # JavaScript toolchain: add node_modules/.bin to PATH -00757| export PATH="$STACKPANEL_ROOT/node_modules/.bin:$PATH" -00758| -00759| -00760| # Go toolchain: set GOPATH and add $GOPATH/bin to PATH -00761| export GOPATH="${STACKPANEL_STATE_DIR:-.stack/state}/go" -00762| export PATH="$GOPATH/bin:$PATH" -00763| -00764| -00765| -00766| -00767| -00768| -00769| -00770| -00771| -00772| -00773| -00774| -00775| -00776| -00777| # Set AWS_CONFIG_FILE using STACKPANEL_STATE_DIR (absolute, works in Docker too) -00778| export AWS_CONFIG_FILE="$STACKPANEL_STATE_DIR/aws/config" -00779| -00780| -00781| # Export cert paths for the credential-process script -00782| export AWS_CERT_PATH="$STACKPANEL_STATE_DIR/step/device-root.chain.crt" -00783| export AWS_KEY_PATH="$STACKPANEL_STATE_DIR/step/device.key" -00784| -00785| # Generate AWS config with credential_process for auto-refresh -00786| if [[ -f "$AWS_CERT_PATH" && -f "$AWS_KEY_PATH" ]]; then -00787| -00788| mkdir -p "$STACKPANEL_STATE_DIR/aws" -00789| /nix/store/73drp5smbs9l6bcl6ccl6b5x63c7s3c9-aws-generate-config/bin/aws-generate-config "$AWS_CONFIG_FILE" 2>/dev/null || true -00790| -00791| else -00792| : # Step CA cert not found, skipping AWS config generation -00793| -00794| fi -00795| -00796| -00797| -00798| -00799| -00800| -00801| # Interactive Step CA cert setup -00802| /nix/store/gbl1d00q0qpfim9fwmbrj7m3293s66gz-step-cert-setup-prompt/bin/step-cert-setup-prompt -00803| -00804| -00805| -00806| echo "📜 stack scripts loaded" -00807| -00808| -00809| # ================================================================ -00810| # SOPS AGE key command -00811| # -00812| # Instead of eagerly loading all keys at shell entry, we set -00813| # SOPS_AGE_KEY_CMD to a script that lazily retrieves keys on demand. -00814| # This avoids slow AWS/SSM calls on every shell entry and supports -00815| # pluggable key backends via the group key-cmd option. -00816| # ================================================================ -00817| export SOPS_AGE_KEY_CMD="/nix/store/8xna4j4knn20vf5svirrzxgdk11ijma7-sops-age-keys/bin/sops-age-keys" -00818| -00819| -00820| -00821| /nix/store/sxlhyhff0wssy2ajniqki2nzwjvxqkw6-write-files/bin/write-files -00822| -00823| -00824| echo "Stack development environment" -00825| -00826| -00827| # Display port information -00828| if [[ -z "${STACKPANEL_QUIET:-}" ]]; then -00829| echo "" >&2 -00830| echo "📦 Stack Ports (project: stack)" >&2 -00831| echo " Stable port: 6400" >&2 -00832| echo "" >&2 -00833| echo " Apps:" >&2 -00834| echo " docs: 5738 -> https://docs.stack.lan" >&2 -00835| echo " stack-go: 5797" >&2 -00836| echo " web: 5775 -> https://stack.stack.lan" >&2 -00837| -00838| -00839| echo "" >&2 -00840| echo " Services:" >&2 -00841| echo " Minio: 6498" >&2 -00842| echo " Minio Console: 6436" >&2 -00843| echo " PostgreSQL: 6404" >&2 -00844| echo " Redis: 6494" >&2 -00845| -00846| -00847| echo "" >&2 -00848| echo " Tip: Set STACKPANEL_QUIET=1 to hide this message" >&2 -00849| echo "" >&2 -00850| fi -00851| -00852| -00853| -00854| /nix/store/dq0aysv78iwa2yyh72nl6a0533m7aw4b-stack-gc-update -00855| -00856| -00857| if [[ -n "${STACKPANEL_STATE_DIR:-}" ]]; then -00858| -00859| ln -sfn "/nix/store/2rkgvs5b8g0d3sqgcca96bwz9f914yry-stack-profile" "$STACKPANEL_STATE_DIR/profile" -00860| fi -00861| -00862| -00863| /nix/store/f9kqq7zhm3qqn0i6x818yqch84ky73kw-stack-generate-bin -00864| -00865| # Register Caddy site for docs -00866| # Generate Step CA certificate for docs.stack.lan if needed -00867| _step_cert="$HOME/.step/certs/docs.stack.lan.crt" -00868| _step_key="$HOME/.step/certs/docs.stack.lan.key" -00869| if [ ! -f "$_step_cert" ] || [ ! -f "$_step_key" ]; then -00870| step ca certificate "docs.stack.lan" "$_step_cert" "$_step_key" \ -00871| --ca-url "https://ca.internal:443" \ -00872| --provisioner "Authentik" \ -00873| --not-after 720h \ -00874| --force 2>/dev/null || true -00875| fi -00876| -00877| /nix/store/jkccqyrsa8wjs7rcsxh3kfxc0djb4iwf-caddy-add-site/bin/caddy-add-site "docs.stack.lan" "localhost:6400" --project "stack" --tls-cert "$HOME/.step/certs/docs.stack.lan.crt" --tls-key "$HOME/.step/certs/docs.stack.lan.key" 2>/dev/null || true -00878| # Register Caddy site for web -00879| # Generate Step CA certificate for stack.stack.lan if needed -00880| _step_cert="$HOME/.step/certs/stack.stack.lan.crt" -00881| _step_key="$HOME/.step/certs/stack.stack.lan.key" -00882| if [ ! -f "$_step_cert" ] || [ ! -f "$_step_key" ]; then -00883| step ca certificate "stack.stack.lan" "$_step_cert" "$_step_key" \ -00884| --ca-url "https://ca.internal:443" \ -00885| --provisioner "Authentik" \ -00886| --not-after 720h \ -00887| --force 2>/dev/null || true -00888| fi -00889| -00890| /nix/store/jkccqyrsa8wjs7rcsxh3kfxc0djb4iwf-caddy-add-site/bin/caddy-add-site "stack.stack.lan" "localhost:6402" --project "stack" --tls-cert "$HOME/.step/certs/stack.stack.lan.crt" --tls-key "$HOME/.step/certs/stack.stack.lan.key" 2>/dev/null || true -00891| -00892| -00893| echo "✅ Stack Nix module system initialized" -00894| -00895| -00896| -00897| eval "$STACKPANEL_ALIAS_FUNC" -00898| -00899| -00900| -00901| # Check if direnv is active and show clean mode status -00902| if [[ -n "${DIRENV_DIR:-}" ]]; then -00903| if [[ -n "${__STACKPANEL_CLEAN_ENV:-}" ]]; then -00904| echo "🧹 Direnv: Clean mode active (21 preserved vars)" >&2 -00905| elif [[ "" == "true" ]]; then -00906| echo "💡 Direnv: Clean mode available (add 'use_stackpanel_clean' to .envrc.local)" >&2 -00907| fi -00908| fi -00909| -00910| -00911| echo "stack core initialized" -00912| # Display MOTD if enabled -00913| if command -v stack &> /dev/null; then -00914| stack motd -00915| fi -00916| -00917| # Log completion -00918| if [[ -z "${DIRENV_IN_ENVRC:-}" ]]; then -00919| echo "" -00920| echo "═══════════════════════════════════════════════════════════════" -00921| echo "Shell hook completed at $(date '+%Y-%m-%d %H:%M:%S')" -00922| echo "Log saved to: ${STACKPANEL_STATE_DIR:-$PWD/.stack/state}/shell.log" -00923| echo "═══════════════════════════════════════════════════════════════" -00924| fi -00925| -00926| } -00927| -00928| # Run the hook with all output to stderr (so direnv doesn't capture/evaluate it) -00929| # Optionally tee to log file if state dir exists -00930| if [[ -d "${STACKPANEL_STATE_DIR:-.stack/state}" ]] 2>/dev/null; then -00931| CLICOLOR_FORCE=1 \ -00932| FORCE_COLOR=3 \ -00933| COLORTERM=truecolor \ -00934| __stackpanel_shell_hook_main 2> \ -00935| >(tee -a "${STACKPANEL_STATE_DIR:-.stack/state}/shell.log" >&2) \ -00936| || status=$? -00937| else -00938| __stackpanel_shell_hook_main >&2 \ -00939| || status=$? -00940| fi -00941| if [[ $status -ne 0 ]]; then -00942| echo "❌ Stack shell hook exited with status $status" >&2 -00943| exit $status -00944| fi -00945| - -(End of file - total 945 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 49.8s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -vvvv -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 45000, - "description": "Reproduce verbose nix develop hang" -} -``` -**Output:** -``` -Nix (Determinate Nix 3.15.2) 2.33.1 -executor using 24 threads -evaluating file '«nix-internal»/derivation-internal.nix' -evaluating derivation 'git+file:///Users/cm/git/darkmatter/stack#devShells.aarch64-darwin.default'... -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -using cache entry 'gitLastModified:{"rev":"0d9b21517fc69d2104da23018f5994fbec51f019"}' -> '{"lastModified":1772336252}' -got tree '/Users/cm/git/darkmatter/stack/' from 'git+file:///Users/cm/git/darkmatter/stack' -evaluating file '/Users/cm/git/darkmatter/stack/flake.nix' -evaluating file '/nix/store/481fmmyjvkm668fjs3j7q3b9fbgkqvi6-source/flake.nix' -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -experimental feature 'flakes' is now stable -experimental feature 'nix-command' is now stable -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -negotiated feature 'queryActiveBuilds' -performing daemon worker op: 19 -old lock file: { - "nodes": { - "agenix": { - "inputs": { - "darwin": "darwin", - "home-manager": "home-manager", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems" - }, - "locked": { - "lastModified": 1770165109, - "narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=", - "owner": "ryantm", - "repo": "agenix", - "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", - "type": "github" - }, - "original": { - "owner": "ryantm", - "repo": "agenix", - "type": "github" - } - }, - "agenix-rekey": { - "inputs": { - "devshell": "devshell", - "flake-parts": "flake-parts", - "nixpkgs": [ - "nixpkgs" - ], - "pre-commit-hooks": "pre-commit-hooks", - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1759699908, - "narHash": "sha256-kYVGY8sAfqwpNch706Fy2+/b+xbtfidhXSnzvthAhIQ=", - "owner": "oddlama", - "repo": "agenix-rekey", - "rev": "42362b12f59978aabf3ec3334834ce2f3662013d", - "type": "github" - }, - "original": { - "owner": "oddlama", - "repo": "agenix-rekey", - "type": "github" - } - }, - "bun2nix": { - "inputs": { - "flake-parts": "flake-parts_2", - "import-tree": "import-tree", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems_2", - "treefmt-nix": "treefmt-nix_2" - }, - "locked": { - "lastModified": 1769533009, - "narHash": "sha256-nyA8w4lpwRtDI07pyje3kiyWVwBLa7YYcXrJtoVbJzA=", - "owner": "nix-community", - "repo": "bun2nix", - "rev": "52e294e3a6078280f89bdcc0d644d2100cd615f8", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "bun2nix", - "type": "github" - } - }, - "cachix": { - "inputs": { - "devenv": [ - "devenv" - ], - "flake-compat": [ - "devenv", - "flake-compat" - ], - "git-hooks": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760971495, - "narHash": "sha256-IwnNtbNVrlZIHh7h4Wz6VP0Furxg9Hh0ycighvL5cZc=", - "owner": "cachix", - "repo": "cachix", - "rev": "c5bfd933d1033672f51a863c47303fc0e093c2d2", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "darwin": { - "inputs": { - "nixpkgs": [ - "agenix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1744478979, - "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=", - "owner": "lnl7", - "repo": "nix-darwin", - "rev": "43975d782b418ebf4969e9ccba82466728c2851b", - "type": "github" - }, - "original": { - "owner": "lnl7", - "ref": "master", - "repo": "nix-darwin", - "type": "github" - } - }, - "devenv": { - "inputs": { - "cachix": "cachix", - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts_3", - "git-hooks": "git-hooks", - "nix": "nix", - "nixd": "nixd", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1770587236, - "narHash": "sha256-W0fShP9zAG7VXKMzFr/A/ZvCc/fBJoGUXocXngkRFlg=", - "owner": "cachix", - "repo": "devenv", - "rev": "d15f117eb9aee15223c8fbccd88ccb4dcc2a1103", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "devshell": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1728330715, - "narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=", - "owner": "numtide", - "repo": "devshell", - "rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1767039857, - "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", - "owner": "NixOS", - "repo": "flake-compat", - "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_3": { - "inputs": { - "nixpkgs-lib": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760948891, - "narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-root": { - "locked": { - "lastModified": 1723604017, - "narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=", - "owner": "srid", - "repo": "flake-root", - "rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e", - "type": "github" - }, - "original": { - "owner": "srid", - "repo": "flake-root", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "revCount": 102, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/numtide/flake-utils/0.1.102%2Brev-11707dc2f618dd54ca8739b309ec4fc024de578b/0193276d-5b8f-7dbc-acf1-41cb7b54ad2e/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/numtide/flake-utils/0.1.102" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "gitignore": "gitignore_2", - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760663237, - "narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "git-hooks_2": { - "inputs": { - "flake-compat": "flake-compat_3", - "gitignore": "gitignore_3", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1769939035, - "narHash": "sha256-Fok2AmefgVA0+eprw2NDwqKkPGEI5wvR+twiZagBvrg=", - "rev": "a8ca480175326551d6c4121498316261cbb5b260", - "revCount": 1161, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1161%2Brev-a8ca480175326551d6c4121498316261cbb5b260/019c1a4c-1020-7b2d-9a32-2386362b04c5/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/cachix/git-hooks.nix/0.1.1149" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_2": { - "inputs": { - "nixpkgs": [ - "devenv", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_3": { - "inputs": { - "nixpkgs": [ - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gomod2nix": { - "inputs": { - "flake-utils": [ - "flake-utils" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1770585520, - "narHash": "sha256-yBz9Ozd5Wb56i3e3cHZ8WcbzCQ9RlVaiW18qDYA/AzA=", - "owner": "nix-community", - "repo": "gomod2nix", - "rev": "1201ddd1279c35497754f016ef33d5e060f3da8d", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "gomod2nix", - "type": "github" - } - }, - "home-manager": { - "inputs": { - "nixpkgs": [ - "agenix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1745494811, - "narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, - "import-tree": { - "locked": { - "lastModified": 1763762820, - "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", - "owner": "vic", - "repo": "import-tree", - "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", - "type": "github" - }, - "original": { - "owner": "vic", - "repo": "import-tree", - "type": "github" - } - }, - "nix": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-parts": [ - "devenv", - "flake-parts" - ], - "git-hooks-nix": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-23-11": [ - "devenv" - ], - "nixpkgs-regression": [ - "devenv" - ] - }, - "locked": { - "lastModified": 1770395975, - "narHash": "sha256-zg0AEZn8d4rqIIsw5XrkVL5p1y6fBj2L57awfUg+gNA=", - "owner": "cachix", - "repo": "nix", - "rev": "ccb6019ce2bd11f5de5fe4617c0079d8cb1ed057", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "devenv-2.32", - "repo": "nix", - "type": "github" - } - }, - "nix2container": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1767430085, - "narHash": "sha256-SiXJ6xv4pS2MDUqfj0/mmG746cGeJrMQGmoFgHLS25Y=", - "owner": "nlewo", - "repo": "nix2container", - "rev": "66f4b8a47e92aa744ec43acbb5e9185078983909", - "type": "github" - }, - "original": { - "owner": "nlewo", - "repo": "nix2container", - "type": "github" - } - }, - "nixd": { - "inputs": { - "flake-parts": [ - "devenv", - "flake-parts" - ], - "flake-root": "flake-root", - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "treefmt-nix": "treefmt-nix_3" - }, - "locked": { - "lastModified": 1763964548, - "narHash": "sha256-JTRoaEWvPsVIMFJWeS4G2isPo15wqXY/otsiHPN0zww=", - "owner": "nix-community", - "repo": "nixd", - "rev": "d4bf15e56540422e2acc7bc26b20b0a0934e3f5e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixd", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1770464364, - "narHash": "sha256-z5NJPSBwsLf/OfD8WTmh79tlSU8XgIbwmk6qB1/TFzY=", - "rev": "23d72dabcb3b12469f57b37170fcbc1789bd7457", - "revCount": 907408, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2511.907408%2Brev-23d72dabcb3b12469f57b37170fcbc1789bd7457/019c3bd0-8a76-71e4-8c0e-ad25a770ebdf/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2511.904620" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1761765539, - "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1735882644, - "narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "a5a961387e75ae44cc20f0a57ae463da5e959656", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "process-compose-flake": { - "locked": { - "lastModified": 1767863885, - "narHash": "sha256-XXekPAxzbv1DmHFo3Elmj/vDnvWc1V0jdDUvM0/Wf7k=", - "owner": "Platonic-Systems", - "repo": "process-compose-flake", - "rev": "99bea96cf269cfd235833ebdf645b567069fd398", - "type": "github" - }, - "original": { - "owner": "Platonic-Systems", - "repo": "process-compose-flake", - "type": "github" - } - }, - "root": { - "inputs": { - "agenix": "agenix", - "agenix-rekey": "agenix-rekey", - "bun2nix": "bun2nix", - "devenv": "devenv", - "flake-utils": "flake-utils", - "git-hooks": "git-hooks_2", - "gomod2nix": "gomod2nix", - "nix2container": "nix2container", - "nixpkgs": "nixpkgs", - "process-compose-flake": "process-compose-flake", - "stack-root": "stack-root", - "treefmt-nix": "treefmt-nix_4" - } - }, - "stack-root": { - "flake": false, - "locked": { - "path": "./.stack-root", - "type": "path" - }, - "original": { - "path": "./.stack-root", - "type": "path" - }, - "parent": [] - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1735135567, - "narHash": "sha256-8T3K5amndEavxnludPyfj3Z1IkcFdRpR23q+T0BVeZE=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "9e09d30a644c57257715902efbb3adc56c79cf28", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "treefmt-nix_2": { - "inputs": { - "nixpkgs": [ - "bun2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "treefmt-nix_3": { - "inputs": { - "nixpkgs": [ - "devenv", - "nixd", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1734704479, - "narHash": "sha256-MMi74+WckoyEWBRcg/oaGRvXC9BVVxDZNRMpL+72wBI=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "65712f5af67234dad91a5a4baee986a8b62dbf8f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "treefmt-nix_4": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1770228511, - "narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=", - "rev": "337a4fe074be1042a35086f15481d763b8ddc0e7", - "revCount": 541, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/numtide/treefmt-nix/0.1.541%2Brev-337a4fe074be1042a35086f15481d763b8ddc0e7/019c2c5b-3760-7c08-93c8-51a13f123486/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/numtide/treefmt-nix/0.1.512" - } - } - }, - "root": "root", - "version": 7 -} -computing lock file node '' -computing input 'agenix' -keeping existing input 'agenix' -computing lock file node 'agenix' -computing input 'agenix/darwin' -keeping existing input 'agenix/darwin' -computing lock file node 'agenix/darwin' -computing input 'agenix/darwin/nixpkgs' -input 'agenix/darwin/nixpkgs' follows 'agenix/nixpkgs' -computing input 'agenix/home-manager' -keeping existing input 'agenix/home-manager' -computing lock file node 'agenix/home-manager' -computing input 'agenix/home-manager/nixpkgs' -input 'agenix/home-manager/nixpkgs' follows 'agenix/nixpkgs' -computing input 'agenix/nixpkgs' -input 'agenix/nixpkgs' follows 'nixpkgs' -computing input 'agenix/systems' -keeping existing input 'agenix/systems' -computing lock file node 'agenix/systems' -computing input 'agenix-rekey' -keeping existing input 'agenix-rekey' -computing lock file node 'agenix-rekey' -computing input 'agenix-rekey/devshell' -keeping existing input 'agenix-rekey/devshell' -computing lock file node 'agenix-rekey/devshell' -computing input 'agenix-rekey/devshell/nixpkgs' -input 'agenix-rekey/devshell/nixpkgs' follows 'agenix-rekey/nixpkgs' -computing input 'agenix-rekey/flake-parts' -keeping existing input 'agenix-rekey/flake-parts' -computing lock file node 'agenix-rekey/flake-parts' -computing input 'agenix-rekey/flake-parts/nixpkgs-lib' -input 'agenix-rekey/flake-parts/nixpkgs-lib' follows 'agenix-rekey/nixpkgs' -computing input 'agenix-rekey/nixpkgs' -input 'agenix-rekey/nixpkgs' follows 'nixpkgs' -computing input 'agenix-rekey/pre-commit-hooks' -keeping existing input 'agenix-rekey/pre-commit-hooks' -computing lock file node 'agenix-rekey/pre-commit-hooks' -computing input 'agenix-rekey/pre-commit-hooks/flake-compat' -keeping existing input 'agenix-rekey/pre-commit-hooks/flake-compat' -computing lock file node 'agenix-rekey/pre-commit-hooks/flake-compat' -computing input 'agenix-rekey/pre-commit-hooks/gitignore' -keeping existing input 'agenix-rekey/pre-commit-hooks/gitignore' -computing lock file node 'agenix-rekey/pre-commit-hooks/gitignore' -computing input 'agenix-rekey/pre-commit-hooks/gitignore/nixpkgs' -input 'agenix-rekey/pre-commit-hooks/gitignore/nixpkgs' follows 'agenix-rekey/pre-commit-hooks/nixpkgs' -computing input 'agenix-rekey/pre-commit-hooks/nixpkgs' -input 'agenix-rekey/pre-commit-hooks/nixpkgs' follows 'agenix-rekey/nixpkgs' -computing input 'agenix-rekey/treefmt-nix' -keeping existing input 'agenix-rekey/treefmt-nix' -computing lock file node 'agenix-rekey/treefmt-nix' -computing input 'agenix-rekey/treefmt-nix/nixpkgs' -input 'agenix-rekey/treefmt-nix/nixpkgs' follows 'agenix-rekey/nixpkgs' -computing input 'bun2nix' -keeping existing input 'bun2nix' -computing lock file node 'bun2nix' -computing input 'bun2nix/flake-parts' -keeping existing input 'bun2nix/flake-parts' -computing lock file node 'bun2nix/flake-parts' -computing input 'bun2nix/flake-parts/nixpkgs-lib' -keeping existing input 'bun2nix/flake-parts/nixpkgs-lib' -computing lock file node 'bun2nix/flake-parts/nixpkgs-lib' -computing input 'bun2nix/import-tree' -keeping existing input 'bun2nix/import-tree' -computing lock file node 'bun2nix/import-tree' -computing input 'bun2nix/nixpkgs' -input 'bun2nix/nixpkgs' follows 'nixpkgs' -computing input 'bun2nix/systems' -keeping existing input 'bun2nix/systems' -computing lock file node 'bun2nix/systems' -computing input 'bun2nix/treefmt-nix' -keeping existing input 'bun2nix/treefmt-nix' -computing lock file node 'bun2nix/treefmt-nix' -computing input 'bun2nix/treefmt-nix/nixpkgs' -input 'bun2nix/treefmt-nix/nixpkgs' follows 'bun2nix/nixpkgs' -computing input 'devenv' -keeping existing input 'devenv' -computing lock file node 'devenv' -computing input 'devenv/cachix' -keeping existing input 'devenv/cachix' -computing lock file node 'devenv/cachix' -computing input 'devenv/cachix/devenv' -input 'devenv/cachix/devenv' follows 'devenv' -computing input 'devenv/cachix/flake-compat' -input 'devenv/cachix/flake-compat' follows 'devenv/flake-compat' -computing input 'devenv/cachix/git-hooks' -input 'devenv/cachix/git-hooks' follows 'devenv/git-hooks' -computing input 'devenv/cachix/nixpkgs' -input 'devenv/cachix/nixpkgs' follows 'devenv/nixpkgs' -computing input 'devenv/flake-compat' -keeping existing input 'devenv/flake-compat' -computing lock file node 'devenv/flake-compat' -computing input 'devenv/flake-parts' -keeping existing input 'devenv/flake-parts' -computing lock file node 'devenv/flake-parts' -computing input 'devenv/flake-parts/nixpkgs-lib' -input 'devenv/flake-parts/nixpkgs-lib' follows 'devenv/nixpkgs' -computing input 'devenv/git-hooks' -keeping existing input 'devenv/git-hooks' -computing lock file node 'devenv/git-hooks' -computing input 'devenv/git-hooks/flake-compat' -input 'devenv/git-hooks/flake-compat' follows 'devenv/flake-compat' -computing input 'devenv/git-hooks/gitignore' -keeping existing input 'devenv/git-hooks/gitignore' -computing lock file node 'devenv/git-hooks/gitignore' -computing input 'devenv/git-hooks/gitignore/nixpkgs' -input 'devenv/git-hooks/gitignore/nixpkgs' follows 'devenv/git-hooks/nixpkgs' -computing input 'devenv/git-hooks/nixpkgs' -input 'devenv/git-hooks/nixpkgs' follows 'devenv/nixpkgs' -computing input 'devenv/nix' -keeping existing input 'devenv/nix' -computing lock file node 'devenv/nix' -computing input 'devenv/nix/flake-compat' -input 'devenv/nix/flake-compat' follows 'devenv/flake-compat' -computing input 'devenv/nix/flake-parts' -input 'devenv/nix/flake-parts' follows 'devenv/flake-parts' -computing input 'devenv/nix/git-hooks-nix' -input 'devenv/nix/git-hooks-nix' follows 'devenv/git-hooks' -computing input 'devenv/nix/nixpkgs' -input 'devenv/nix/nixpkgs' follows 'devenv/nixpkgs' -computing input 'devenv/nix/nixpkgs-23-11' -input 'devenv/nix/nixpkgs-23-11' follows 'devenv' -computing input 'devenv/nix/nixpkgs-regression' -input 'devenv/nix/nixpkgs-regression' follows 'devenv' -computing input 'devenv/nixd' -keeping existing input 'devenv/nixd' -computing lock file node 'devenv/nixd' -computing input 'devenv/nixd/flake-parts' -input 'devenv/nixd/flake-parts' follows 'devenv/flake-parts' -computing input 'devenv/nixd/flake-root' -keeping existing input 'devenv/nixd/flake-root' -computing lock file node 'devenv/nixd/flake-root' -computing input 'devenv/nixd/nixpkgs' -input 'devenv/nixd/nixpkgs' follows 'devenv/nixpkgs' -computing input 'devenv/nixd/treefmt-nix' -keeping existing input 'devenv/nixd/treefmt-nix' -computing lock file node 'devenv/nixd/treefmt-nix' -computing input 'devenv/nixd/treefmt-nix/nixpkgs' -input 'devenv/nixd/treefmt-nix/nixpkgs' follows 'devenv/nixd/nixpkgs' -computing input 'devenv/nixpkgs' -input 'devenv/nixpkgs' follows 'nixpkgs' -computing input 'flake-utils' -keeping existing input 'flake-utils' -computing lock file node 'flake-utils' -computing input 'flake-utils/systems' -keeping existing input 'flake-utils/systems' -computing lock file node 'flake-utils/systems' -computing input 'git-hooks' -keeping existing input 'git-hooks' -computing lock file node 'git-hooks' -computing input 'git-hooks/flake-compat' -keeping existing input 'git-hooks/flake-compat' -computing lock file node 'git-hooks/flake-compat' -computing input 'git-hooks/gitignore' -keeping existing input 'git-hooks/gitignore' -computing lock file node 'git-hooks/gitignore' -computing input 'git-hooks/gitignore/nixpkgs' -input 'git-hooks/gitignore/nixpkgs' follows 'git-hooks/nixpkgs' -computing input 'git-hooks/nixpkgs' -input 'git-hooks/nixpkgs' follows 'nixpkgs' -computing input 'gomod2nix' -keeping existing input 'gomod2nix' -computing lock file node 'gomod2nix' -computing input 'gomod2nix/flake-utils' -input 'gomod2nix/flake-utils' follows 'flake-utils' -computing input 'gomod2nix/nixpkgs' -input 'gomod2nix/nixpkgs' follows 'nixpkgs' -computing input 'nix2container' -keeping existing input 'nix2container' -computing lock file node 'nix2container' -computing input 'nix2container/nixpkgs' -input 'nix2container/nixpkgs' follows 'nixpkgs' -computing input 'nixpkgs' -keeping existing input 'nixpkgs' -computing lock file node 'nixpkgs' -computing input 'process-compose-flake' -keeping existing input 'process-compose-flake' -computing lock file node 'process-compose-flake' -computing input 'stack-root' -keeping existing input 'stack-root' -computing lock file node 'stack-root' -computing input 'treefmt-nix' -keeping existing input 'treefmt-nix' -computing lock file node 'treefmt-nix' -computing input 'treefmt-nix/nixpkgs' -input 'treefmt-nix/nixpkgs' follows 'nixpkgs' -new lock file: { - "nodes": { - "agenix": { - "inputs": { - "darwin": "darwin", - "home-manager": "home-manager", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems" - }, - "locked": { - "lastModified": 1770165109, - "narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=", - "owner": "ryantm", - "repo": "agenix", - "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", - "type": "github" - }, - "original": { - "owner": "ryantm", - "repo": "agenix", - "type": "github" - } - }, - "agenix-rekey": { - "inputs": { - "devshell": "devshell", - "flake-parts": "flake-parts", - "nixpkgs": [ - "nixpkgs" - ], - "pre-commit-hooks": "pre-commit-hooks", - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1759699908, - "narHash": "sha256-kYVGY8sAfqwpNch706Fy2+/b+xbtfidhXSnzvthAhIQ=", - "owner": "oddlama", - "repo": "agenix-rekey", - "rev": "42362b12f59978aabf3ec3334834ce2f3662013d", - "type": "github" - }, - "original": { - "owner": "oddlama", - "repo": "agenix-rekey", - "type": "github" - } - }, - "bun2nix": { - "inputs": { - "flake-parts": "flake-parts_2", - "import-tree": "import-tree", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems_2", - "treefmt-nix": "treefmt-nix_2" - }, - "locked": { - "lastModified": 1769533009, - "narHash": "sha256-nyA8w4lpwRtDI07pyje3kiyWVwBLa7YYcXrJtoVbJzA=", - "owner": "nix-community", - "repo": "bun2nix", - "rev": "52e294e3a6078280f89bdcc0d644d2100cd615f8", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "bun2nix", - "type": "github" - } - }, - "cachix": { - "inputs": { - "devenv": [ - "devenv" - ], - "flake-compat": [ - "devenv", - "flake-compat" - ], - "git-hooks": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760971495, - "narHash": "sha256-IwnNtbNVrlZIHh7h4Wz6VP0Furxg9Hh0ycighvL5cZc=", - "owner": "cachix", - "repo": "cachix", - "rev": "c5bfd933d1033672f51a863c47303fc0e093c2d2", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "darwin": { - "inputs": { - "nixpkgs": [ - "agenix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1744478979, - "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=", - "owner": "lnl7", - "repo": "nix-darwin", - "rev": "43975d782b418ebf4969e9ccba82466728c2851b", - "type": "github" - }, - "original": { - "owner": "lnl7", - "ref": "master", - "repo": "nix-darwin", - "type": "github" - } - }, - "devenv": { - "inputs": { - "cachix": "cachix", - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts_3", - "git-hooks": "git-hooks", - "nix": "nix", - "nixd": "nixd", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1770587236, - "narHash": "sha256-W0fShP9zAG7VXKMzFr/A/ZvCc/fBJoGUXocXngkRFlg=", - "owner": "cachix", - "repo": "devenv", - "rev": "d15f117eb9aee15223c8fbccd88ccb4dcc2a1103", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "devshell": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1728330715, - "narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=", - "owner": "numtide", - "repo": "devshell", - "rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1767039857, - "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", - "owner": "NixOS", - "repo": "flake-compat", - "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_3": { - "inputs": { - "nixpkgs-lib": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760948891, - "narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-root": { - "locked": { - "lastModified": 1723604017, - "narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=", - "owner": "srid", - "repo": "flake-root", - "rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e", - "type": "github" - }, - "original": { - "owner": "srid", - "repo": "flake-root", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "revCount": 102, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/numtide/flake-utils/0.1.102%2Brev-11707dc2f618dd54ca8739b309ec4fc024de578b/0193276d-5b8f-7dbc-acf1-41cb7b54ad2e/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/numtide/flake-utils/0.1.102" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "gitignore": "gitignore_2", - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1760663237, - "narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "git-hooks_2": { - "inputs": { - "flake-compat": "flake-compat_3", - "gitignore": "gitignore_3", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1769939035, - "narHash": "sha256-Fok2AmefgVA0+eprw2NDwqKkPGEI5wvR+twiZagBvrg=", - "rev": "a8ca480175326551d6c4121498316261cbb5b260", - "revCount": 1161, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1161%2Brev-a8ca480175326551d6c4121498316261cbb5b260/019c1a4c-1020-7b2d-9a32-2386362b04c5/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/cachix/git-hooks.nix/0.1.1149" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_2": { - "inputs": { - "nixpkgs": [ - "devenv", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_3": { - "inputs": { - "nixpkgs": [ - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gomod2nix": { - "inputs": { - "flake-utils": [ - "flake-utils" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1770585520, - "narHash": "sha256-yBz9Ozd5Wb56i3e3cHZ8WcbzCQ9RlVaiW18qDYA/AzA=", - "owner": "nix-community", - "repo": "gomod2nix", - "rev": "1201ddd1279c35497754f016ef33d5e060f3da8d", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "gomod2nix", - "type": "github" - } - }, - "home-manager": { - "inputs": { - "nixpkgs": [ - "agenix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1745494811, - "narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, - "import-tree": { - "locked": { - "lastModified": 1763762820, - "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", - "owner": "vic", - "repo": "import-tree", - "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", - "type": "github" - }, - "original": { - "owner": "vic", - "repo": "import-tree", - "type": "github" - } - }, - "nix": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-parts": [ - "devenv", - "flake-parts" - ], - "git-hooks-nix": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-23-11": [ - "devenv" - ], - "nixpkgs-regression": [ - "devenv" - ] - }, - "locked": { - "lastModified": 1770395975, - "narHash": "sha256-zg0AEZn8d4rqIIsw5XrkVL5p1y6fBj2L57awfUg+gNA=", - "owner": "cachix", - "repo": "nix", - "rev": "ccb6019ce2bd11f5de5fe4617c0079d8cb1ed057", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "devenv-2.32", - "repo": "nix", - "type": "github" - } - }, - "nix2container": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1767430085, - "narHash": "sha256-SiXJ6xv4pS2MDUqfj0/mmG746cGeJrMQGmoFgHLS25Y=", - "owner": "nlewo", - "repo": "nix2container", - "rev": "66f4b8a47e92aa744ec43acbb5e9185078983909", - "type": "github" - }, - "original": { - "owner": "nlewo", - "repo": "nix2container", - "type": "github" - } - }, - "nixd": { - "inputs": { - "flake-parts": [ - "devenv", - "flake-parts" - ], - "flake-root": "flake-root", - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "treefmt-nix": "treefmt-nix_3" - }, - "locked": { - "lastModified": 1763964548, - "narHash": "sha256-JTRoaEWvPsVIMFJWeS4G2isPo15wqXY/otsiHPN0zww=", - "owner": "nix-community", - "repo": "nixd", - "rev": "d4bf15e56540422e2acc7bc26b20b0a0934e3f5e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixd", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1770464364, - "narHash": "sha256-z5NJPSBwsLf/OfD8WTmh79tlSU8XgIbwmk6qB1/TFzY=", - "rev": "23d72dabcb3b12469f57b37170fcbc1789bd7457", - "revCount": 907408, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2511.907408%2Brev-23d72dabcb3b12469f57b37170fcbc1789bd7457/019c3bd0-8a76-71e4-8c0e-ad25a770ebdf/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2511.904620" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1761765539, - "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1735882644, - "narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "a5a961387e75ae44cc20f0a57ae463da5e959656", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "process-compose-flake": { - "locked": { - "lastModified": 1767863885, - "narHash": "sha256-XXekPAxzbv1DmHFo3Elmj/vDnvWc1V0jdDUvM0/Wf7k=", - "owner": "Platonic-Systems", - "repo": "process-compose-flake", - "rev": "99bea96cf269cfd235833ebdf645b567069fd398", - "type": "github" - }, - "original": { - "owner": "Platonic-Systems", - "repo": "process-compose-flake", - "type": "github" - } - }, - "root": { - "inputs": { - "agenix": "agenix", - "agenix-rekey": "agenix-rekey", - "bun2nix": "bun2nix", - "devenv": "devenv", - "flake-utils": "flake-utils", - "git-hooks": "git-hooks_2", - "gomod2nix": "gomod2nix", - "nix2container": "nix2container", - "nixpkgs": "nixpkgs", - "process-compose-flake": "process-compose-flake", - "stack-root": "stack-root", - "treefmt-nix": "treefmt-nix_4" - } - }, - "stack-root": { - "flake": false, - "locked": { - "path": "./.stack-root", - "type": "path" - }, - "original": { - "path": "./.stack-root", - "type": "path" - }, - "parent": [] - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "agenix-rekey", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1735135567, - "narHash": "sha256-8T3K5amndEavxnludPyfj3Z1IkcFdRpR23q+T0BVeZE=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "9e09d30a644c57257715902efbb3adc56c79cf28", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "treefmt-nix_2": { - "inputs": { - "nixpkgs": [ - "bun2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", - "type": "github" - }, - "original": { - "owner": "numtide", - -...6610966 bytes truncated... - -The tool call succeeded but the output was truncated. Full output saved to: /Users/cm/.local/share/opencode/tool-output/tool_ca7eb6748001fcEYydHCaG2i6x -Use the Task tool to have explore agent process this file with Grep and Read (with offset/limit). Do NOT read the full file yourself - delegate to save context. -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.6s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ls -la \"/nix/store/xhv8fg110hqb2g744jvq129gzv496gpm-stack-bun-install\"", - "description": "Inspect bun install script store path" -} -``` -**Output:** -``` --r-xr-xr-x 1 root wheel 1082 Dec 31 1969 /nix/store/xhv8fg110hqb2g744jvq129gzv496gpm-stack-bun-install - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/nix/store/xhv8fg110hqb2g744jvq129gzv496gpm-stack-bun-install" -} -``` -**Output:** -``` - -00001| #!/nix/store/i2yfb9hqscbws8nqw7hkdq811x5fyc1n-bash-5.3p3/bin/bash -00002| _stackpanel_bun_install() { -00003| local lock_file -00004| if [ -f bun.lock ]; then -00005| lock_file="bun.lock" -00006| elif [ -f bun.lockb ]; then -00007| lock_file="bun.lockb" -00008| else -00009| echo "No bun lockfile found. Run 'bun install' to create one." >&2 -00010| return 0 -00011| fi -00012| -00013| local ACTUAL_CHECKSUM="1.3.3:$(/nix/store/2f69ldgafp35wfvcwngj69q2s3yr2bz4-nix-2.31.2+1/bin/nix-hash --type sha256 "$lock_file")" -00014| local CHECKSUM_FILE="node_modules/bun.lock.checksum" -00015| -00016| local EXPECTED_CHECKSUM="" -00017| if [ -f "$CHECKSUM_FILE" ]; then -00018| read -r EXPECTED_CHECKSUM < "$CHECKSUM_FILE" -00019| fi -00020| -00021| if [ "$ACTUAL_CHECKSUM" != "$EXPECTED_CHECKSUM" ]; then -00022| echo "Installing bun dependencies..." >&2 -00023| if /nix/store/krnblifyy6lw95z4qzy8h065gcrdh18p-bun-1.3.3/bin/bun install ; then -00024| echo "$ACTUAL_CHECKSUM" > "$CHECKSUM_FILE" -00025| else -00026| echo "bun install failed. Run 'bun install' manually." >&2 -00027| fi -00028| fi -00029| } -00030| -00031| if [ ! -f package.json ]; then -00032| echo "No package.json found. Run 'bun init' to create one." >&2 -00033| else -00034| _stackpanel_bun_install -00035| fi -00036| -00037| - -(End of file - total 37 lines) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.2s) - -_Thinking:_ - - - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/nix/store/sxlhyhff0wssy2ajniqki2nzwjvxqkw6-write-files/bin/write-files" -} -``` -**Output:** -``` - -00001| #!/nix/store/i2yfb9hqscbws8nqw7hkdq811x5fyc1n-bash-5.3p3/bin/bash -00002| set -o errexit -00003| set -o nounset -00004| set -o pipefail -00005| -00006| export PATH="/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin:/nix/store/fcjd369g57hxibl4iyggvdhfvwzz5mj9-jq-1.8.1-bin/bin:/nix/store/335yjafsn3nbzspn2qjq63a35g2ps7rc-gawk-5.3.2/bin:$PATH" -00007| -00008| set -euo pipefail -00009| -00010| # ── Parse flags ────────────────────────────────────────────────────── -00011| FORCE=0 -00012| VERBOSE=0 -00013| for arg in "$@"; do -00014| case "$arg" in -00015| --force|-f) FORCE=1 ;; -00016| --verbose|-v) VERBOSE=1 ;; -00017| --help|-h) -00018| echo "Usage: write-files [--force] [--verbose]" -00019| echo " --force Skip hash checks, rewrite all files" -00020| echo " --verbose Show per-file skip/write details" -00021| exit 0 -00022| ;; -00023| esac -00024| done -00025| -00026| # Determine repo root -00027| ROOT="${STACKPANEL_ROOT:-}" -00028| if [[ -z "$ROOT" ]]; then -00029| echo "ERROR: STACKPANEL_ROOT is not set. (Stack core should set it.)" >&2 -00030| exit 1 -00031| fi -00032| -00033| cd "$ROOT" -00034| -00035| STATE_DIR="${STACKPANEL_STATE_DIR:-$ROOT/.stack/state}" -00036| MANIFEST_FILE="$STATE_DIR/.files-manifest" -00037| -00038| # ── Manifest fast path ────────────────────────────────────────────── -00039| # If the manifest hash matches, nothing changed — skip everything. -00040| MANIFEST_HASH="041ffc0c57fba04f2edece2b26ba657cb2db27e2fda4e334ab19124a3f29a7db" -00041| if [[ "$FORCE" == "0" ]] && [[ -f "$MANIFEST_FILE" ]] && [[ "$(cat "$MANIFEST_FILE")" == "$MANIFEST_HASH" ]]; then -00042| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00043| echo "files: all 65 files unchanged (skipping)" -00044| fi -00045| exit 0 -00046| fi -00047| -00048| # ── Cleanup stale files from previous generation ───────────────────── -00049| OLD_MANIFEST="$STATE_DIR/files.json" -00050| REMOVED_COUNT=0 -00051| if [[ -f "$OLD_MANIFEST" ]]; then -00052| CURRENT_PATHS='[".envrc.local",".gitignore",".stack/gen/direnv-wrapper.sh",".stack/gen/vscode/devshell-loader.sh",".stack/gen/zed/devshell-loader.sh",".stack/secrets/README.md",".stack/state/infra-inputs.json",".tasks/bin/alchemy:ensure",".tasks/bin/build",".tasks/bin/clean",".tasks/bin/db:migrate",".tasks/bin/db:push",".tasks/bin/db:studio",".tasks/bin/dev",".tasks/bin/format",".tasks/bin/generate:proto",".tasks/bin/generate:types",".tasks/bin/lint",".tasks/bin/test",".tasks/bin/test:coverage",".tasks/bin/test:watch",".tasks/bin/typecheck",".vscode/settings.json",".zed/settings.json","apps/docs/.oxlintrc.json","apps/web/.oxlintrc.json","apps/web/.tasks/bin/container-build","apps/web/.tasks/bin/container-push","apps/web/.tasks/bin/deploy","apps/web/fly.toml","devshell","packages/gen/env/README.md","packages/gen/env/data/shared/vars.yaml","packages/gen/env/package.json","packages/gen/env/src/entrypoints/docs.ts","packages/gen/env/src/entrypoints/index.ts","packages/gen/env/src/entrypoints/stack-go.ts","packages/gen/env/src/entrypoints/web.ts","packages/gen/env/src/generated/index.ts","packages/gen/env/src/generated/stack-go/dev.ts","packages/gen/env/src/generated/stack-go/index.ts","packages/gen/env/src/generated/web/dev.ts","packages/gen/env/src/generated/web/index.ts","packages/gen/env/src/index.ts","packages/gen/env/tsconfig.json","packages/infra/README.md","packages/infra/alchemy.run.ts","packages/infra/modules/aws-secrets/index.ts","packages/infra/modules/aws-secrets/policies.ts","packages/infra/modules/database.ts","packages/infra/modules/deployment.ts","packages/infra/package.json","packages/infra/src/index.ts","packages/infra/src/resources/iam-role.ts","packages/infra/src/resources/kms-alias.ts","packages/infra/src/resources/kms-key.ts","packages/infra/src/types.ts","packages/infra/sst.config.ts","packages/infra/tsconfig.json","packages/infra/turbo.json","packages/scripts/entrypoints/docs.sh","packages/scripts/entrypo... -00053| -00054| # Extract old file entries from the manifest (path + managed mode + markers) -00055| OLD_ENTRIES=$(jq -r '.files[] | "\(.path)\t\(.managed // "full")\t\(.commentPrefix // "#")\t\(.blockLabel // "stack")"' "$OLD_MANIFEST" 2>/dev/null) || OLD_ENTRIES="" -00056| -00057| while IFS=$'\t' read -r old_path old_managed old_comment_prefix old_block_label; do -00058| [[ -z "$old_path" ]] && continue -00059| # Check if this path is still in the current file set -00060| if ! echo "$CURRENT_PATHS" | jq -e --arg p "$old_path" 'index($p) != null' >/dev/null 2>&1; then -00061| # This file is stale -00062| if [[ "$old_managed" == "block" ]]; then -00063| # Block-managed: strip the managed block instead of deleting -00064| if [[ -f "$old_path" ]]; then -00065| _begin="$old_comment_prefix ── BEGIN $old_block_label ──" -00066| _end="$old_comment_prefix ── END $old_block_label ──" -00067| if grep -qF "$_begin" "$old_path"; then -00068| # Remove the block (and any single blank line immediately before it) -00069| /nix/store/335yjafsn3nbzspn2qjq63a35g2ps7rc-gawk-5.3.2/bin/awk -v begin="$_begin" -v end="$_end" ' -00070| $0 == begin { skip=1; if (prev_blank) { prev_blank=0 }; next } -00071| skip && $0 == end { skip=0; next } -00072| skip { next } -00073| /^[[:space:]]*$/ { prev_blank=1; prev_line=$0; next } -00074| { if (prev_blank) { print prev_line; prev_blank=0 }; print } -00075| END { if (prev_blank) print prev_line } -00076| ' "$old_path" > "$old_path.sp-tmp" && mv "$old_path.sp-tmp" "$old_path" -00077| echo " remove $old_path (stale block stripped)" -00078| REMOVED_COUNT=$((REMOVED_COUNT + 1)) -00079| # If the file is now empty (or only whitespace), remove it -00080| if [[ ! -s "$old_path" ]] || ! grep -q '[^[:space:]]' "$old_path"; then -00081| rm -f "$old_path" -00082| echo " remove $old_path (empty after block removal)" -00083| fi -00084| fi -00085| fi -00086| else -00087| # Full-managed: delete the entire file -00088| if [[ -e "$old_path" || -L "$old_path" ]]; then -00089| rm -f "$old_path" 2>/dev/null || true -00090| echo " remove $old_path (stale)" -00091| REMOVED_COUNT=$((REMOVED_COUNT + 1)) -00092| -00093| # Clean up empty parent directories (up to repo root) -00094| dir=$(dirname "$old_path") -00095| while [[ "$dir" != "." && "$dir" != "/" ]]; do -00096| if [[ -d "$dir" ]] && [[ -z "$(ls -A "$dir" 2>/dev/null)" ]]; then -00097| rmdir "$dir" 2>/dev/null || true -00098| else -00099| break -00100| fi -00101| dir=$(dirname "$dir") -00102| done -00103| fi -00104| fi -00105| fi -00106| done <<< "$OLD_ENTRIES" -00107| fi -00108| -00109| # ── Write current files (with hash check) ─────────────────────────── -00110| WRITTEN_COUNT=0 -00111| UNCHANGED_COUNT=0 -00112| -00113| # .envrc.local (text) -00114| _src=/nix/store/m1isn7psb7z6p21kidawsj8jywfalm20-envrc.local -00115| _dst=.envrc.local -00116| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00117| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00118| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00119| echo " skip .envrc.local (unchanged)" -00120| fi -00121| else -00122| mkdir -p "$(dirname "$_dst")" -00123| cat "$_src" > "$_dst" -00124| chmod 0644 "$_dst" -00125| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00126| echo " write .envrc.local" -00127| fi -00128| -00129| # .gitignore (line-set, block-managed) -00130| _src=/nix/store/3hchjcdcc1mrwdwa3r9fihhl27fc6r8c-gitignore -00131| _dst=.gitignore -00132| _begin_marker='# ── BEGIN stack ──' -00133| _end_marker='# ── END stack ──' -00134| _no_edit='# DO NOT EDIT between these markers — managed by stack' -00135| -00136| # Build the full managed block (markers + content) -00137| _block_content="$(printf '%s\n%s\n%s%s' "$_begin_marker" "$_no_edit" "$(cat "$_src")" "$_end_marker")" -00138| -00139| mkdir -p "$(dirname "$_dst")" -00140| -00141| if [[ ! -f "$_dst" ]]; then -00142| # File doesn't exist — create with just the managed block -00143| printf '%s\n' "$_block_content" > "$_dst" -00144| -00145| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00146| echo " write .gitignore (block, new file)" -00147| elif ! grep -qF "$_begin_marker" "$_dst"; then -00148| # File exists but no managed block — append -00149| # Add a blank line separator if file doesn't end with one -00150| if [[ -s "$_dst" ]] && [[ "$(tail -c1 "$_dst")" != "" ]]; then -00151| printf '\n' >> "$_dst" -00152| fi -00153| printf '\n%s\n' "$_block_content" >> "$_dst" -00154| -00155| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00156| echo " write .gitignore (block, appended)" -00157| else -00158| # File exists with managed block — extract current block and compare -00159| _current_block=$(/nix/store/335yjafsn3nbzspn2qjq63a35g2ps7rc-gawk-5.3.2/bin/awk -v begin="$_begin_marker" -v end="$_end_marker" ' -00160| $0 == begin { found=1 } -00161| found { block = block $0 "\n" } -00162| $0 == end { found=0 } -00163| END { printf "%s", block } -00164| ' "$_dst") -00165| -00166| if [[ "$FORCE" == "0" ]] && [[ "$_current_block" == "$_block_content"$'\n' ]]; then -00167| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00168| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00169| echo " skip .gitignore (block unchanged)" -00170| fi -00171| else -00172| # Replace block between markers (inclusive) using awk -00173| /nix/store/335yjafsn3nbzspn2qjq63a35g2ps7rc-gawk-5.3.2/bin/awk -v begin="$_begin_marker" -v end="$_end_marker" -v replacement="$_block_content" ' -00174| $0 == begin { skip=1; if (!printed) { print replacement; printed=1 }; next } -00175| skip && $0 == end { skip=0; next } -00176| !skip { print } -00177| ' "$_dst" > "$_dst.sp-tmp" && mv "$_dst.sp-tmp" "$_dst" -00178| -00179| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00180| echo " write .gitignore (block, updated)" -00181| fi -00182| fi -00183| -00184| # .stack/gen/direnv-wrapper.sh (text) -00185| _src=/nix/store/3h1dh5aj8agghlrs4acsrdkps5vb5cij-direnv-wrapper.sh -00186| _dst=.stack/gen/direnv-wrapper.sh -00187| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00188| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00189| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00190| echo " skip .stack/gen/direnv-wrapper.sh (unchanged)" -00191| fi -00192| else -00193| mkdir -p "$(dirname "$_dst")" -00194| cat "$_src" > "$_dst" -00195| chmod 0755 "$_dst" -00196| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00197| echo " write .stack/gen/direnv-wrapper.sh" -00198| fi -00199| -00200| # .stack/gen/vscode/devshell-loader.sh (derivation) -00201| _src=/nix/store/1jjiqm5f9q9vqkyxgmk95lh6xxqhrg0z-devshell-loader.sh -00202| _dst=.stack/gen/vscode/devshell-loader.sh -00203| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00204| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00205| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00206| echo " skip .stack/gen/vscode/devshell-loader.sh (unchanged)" -00207| fi -00208| else -00209| mkdir -p "$(dirname "$_dst")" -00210| cat "$_src" > "$_dst" -00211| chmod 0755 "$_dst" -00212| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00213| echo " write .stack/gen/vscode/devshell-loader.sh" -00214| fi -00215| -00216| # .stack/gen/zed/devshell-loader.sh (derivation) -00217| _src=/nix/store/1jjiqm5f9q9vqkyxgmk95lh6xxqhrg0z-devshell-loader.sh -00218| _dst=.stack/gen/zed/devshell-loader.sh -00219| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00220| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00221| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00222| echo " skip .stack/gen/zed/devshell-loader.sh (unchanged)" -00223| fi -00224| else -00225| mkdir -p "$(dirname "$_dst")" -00226| cat "$_src" > "$_dst" -00227| chmod 0755 "$_dst" -00228| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00229| echo " write .stack/gen/zed/devshell-loader.sh" -00230| fi -00231| -00232| # .stack/secrets/README.md (text) -00233| _src=/nix/store/l2v5qk2pi9qkphaay3am4fsr70gl6q3h-README.md -00234| _dst=.stack/secrets/README.md -00235| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00236| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00237| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00238| echo " skip .stack/secrets/README.md (unchanged)" -00239| fi -00240| else -00241| mkdir -p "$(dirname "$_dst")" -00242| cat "$_src" > "$_dst" -00243| -00244| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00245| echo " write .stack/secrets/README.md" -00246| fi -00247| -00248| # .stack/state/infra-inputs.json (text) -00249| _src=/nix/store/gclyknwaf9shg4w1hsrj2pwgvkksfsng-infra-inputs.json -00250| _dst=.stack/state/infra-inputs.json -00251| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00252| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00253| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00254| echo " skip .stack/state/infra-inputs.json (unchanged)" -00255| fi -00256| else -00257| mkdir -p "$(dirname "$_dst")" -00258| cat "$_src" > "$_dst" -00259| chmod 0600 "$_dst" -00260| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00261| echo " write .stack/state/infra-inputs.json" -00262| fi -00263| -00264| # .tasks/bin/alchemy:ensure (symlink) -00265| mkdir -p "$(dirname .tasks/bin/alchemy:ensure)" -00266| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/alchemy:ensure ]] && [[ "$(readlink .tasks/bin/alchemy:ensure)" == /nix/store/5p6w3gs8ifnmrxbjya9bp6yb6ad9wvv1-alchemy-ensure/bin/alchemy:ensure ]]; then -00267| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00268| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00269| echo " skip .tasks/bin/alchemy:ensure (symlink unchanged)" -00270| fi -00271| else -00272| rm -f .tasks/bin/alchemy:ensure 2>/dev/null || true -00273| ln -s /nix/store/5p6w3gs8ifnmrxbjya9bp6yb6ad9wvv1-alchemy-ensure/bin/alchemy:ensure .tasks/bin/alchemy:ensure -00274| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00275| echo " write .tasks/bin/alchemy:ensure -> /nix/store/5p6w3gs8ifnmrxbjya9bp6yb6ad9wvv1-alchemy-ensure/bin/alchemy:ensure" -00276| fi -00277| -00278| # .tasks/bin/build (symlink) -00279| mkdir -p "$(dirname .tasks/bin/build)" -00280| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/build ]] && [[ "$(readlink .tasks/bin/build)" == /nix/store/sl9yplndq70sacwgwkr0dl4arxh6iv7h-build/bin/build ]]; then -00281| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00282| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00283| echo " skip .tasks/bin/build (symlink unchanged)" -00284| fi -00285| else -00286| rm -f .tasks/bin/build 2>/dev/null || true -00287| ln -s /nix/store/sl9yplndq70sacwgwkr0dl4arxh6iv7h-build/bin/build .tasks/bin/build -00288| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00289| echo " write .tasks/bin/build -> /nix/store/sl9yplndq70sacwgwkr0dl4arxh6iv7h-build/bin/build" -00290| fi -00291| -00292| # .tasks/bin/clean (symlink) -00293| mkdir -p "$(dirname .tasks/bin/clean)" -00294| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/clean ]] && [[ "$(readlink .tasks/bin/clean)" == /nix/store/iizmggfx5iq7ypak5dp139kzl63sb316-clean/bin/clean ]]; then -00295| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00296| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00297| echo " skip .tasks/bin/clean (symlink unchanged)" -00298| fi -00299| else -00300| rm -f .tasks/bin/clean 2>/dev/null || true -00301| ln -s /nix/store/iizmggfx5iq7ypak5dp139kzl63sb316-clean/bin/clean .tasks/bin/clean -00302| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00303| echo " write .tasks/bin/clean -> /nix/store/iizmggfx5iq7ypak5dp139kzl63sb316-clean/bin/clean" -00304| fi -00305| -00306| # .tasks/bin/db:migrate (symlink) -00307| mkdir -p "$(dirname .tasks/bin/db:migrate)" -00308| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/db:migrate ]] && [[ "$(readlink .tasks/bin/db:migrate)" == /nix/store/2p6bsy4xgksbj4lf87czp7vichllkqxv-db-migrate/bin/db:migrate ]]; then -00309| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00310| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00311| echo " skip .tasks/bin/db:migrate (symlink unchanged)" -00312| fi -00313| else -00314| rm -f .tasks/bin/db:migrate 2>/dev/null || true -00315| ln -s /nix/store/2p6bsy4xgksbj4lf87czp7vichllkqxv-db-migrate/bin/db:migrate .tasks/bin/db:migrate -00316| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00317| echo " write .tasks/bin/db:migrate -> /nix/store/2p6bsy4xgksbj4lf87czp7vichllkqxv-db-migrate/bin/db:migrate" -00318| fi -00319| -00320| # .tasks/bin/db:push (symlink) -00321| mkdir -p "$(dirname .tasks/bin/db:push)" -00322| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/db:push ]] && [[ "$(readlink .tasks/bin/db:push)" == /nix/store/57rlvb992za4cr59jnpcnk8aa4lkgila-db-push/bin/db:push ]]; then -00323| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00324| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00325| echo " skip .tasks/bin/db:push (symlink unchanged)" -00326| fi -00327| else -00328| rm -f .tasks/bin/db:push 2>/dev/null || true -00329| ln -s /nix/store/57rlvb992za4cr59jnpcnk8aa4lkgila-db-push/bin/db:push .tasks/bin/db:push -00330| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00331| echo " write .tasks/bin/db:push -> /nix/store/57rlvb992za4cr59jnpcnk8aa4lkgila-db-push/bin/db:push" -00332| fi -00333| -00334| # .tasks/bin/db:studio (symlink) -00335| mkdir -p "$(dirname .tasks/bin/db:studio)" -00336| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/db:studio ]] && [[ "$(readlink .tasks/bin/db:studio)" == /nix/store/f2jfsg44xkznkr9d60idaxkyng4yqcgi-db-studio/bin/db:studio ]]; then -00337| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00338| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00339| echo " skip .tasks/bin/db:studio (symlink unchanged)" -00340| fi -00341| else -00342| rm -f .tasks/bin/db:studio 2>/dev/null || true -00343| ln -s /nix/store/f2jfsg44xkznkr9d60idaxkyng4yqcgi-db-studio/bin/db:studio .tasks/bin/db:studio -00344| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00345| echo " write .tasks/bin/db:studio -> /nix/store/f2jfsg44xkznkr9d60idaxkyng4yqcgi-db-studio/bin/db:studio" -00346| fi -00347| -00348| # .tasks/bin/dev (symlink) -00349| mkdir -p "$(dirname .tasks/bin/dev)" -00350| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/dev ]] && [[ "$(readlink .tasks/bin/dev)" == /nix/store/7kjn4hzllprj9rbv6h8wfmh30j711ykn-dev/bin/dev ]]; then -00351| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00352| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00353| echo " skip .tasks/bin/dev (symlink unchanged)" -00354| fi -00355| else -00356| rm -f .tasks/bin/dev 2>/dev/null || true -00357| ln -s /nix/store/7kjn4hzllprj9rbv6h8wfmh30j711ykn-dev/bin/dev .tasks/bin/dev -00358| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00359| echo " write .tasks/bin/dev -> /nix/store/7kjn4hzllprj9rbv6h8wfmh30j711ykn-dev/bin/dev" -00360| fi -00361| -00362| # .tasks/bin/format (symlink) -00363| mkdir -p "$(dirname .tasks/bin/format)" -00364| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/format ]] && [[ "$(readlink .tasks/bin/format)" == /nix/store/6ghzc9ybbxdc1551iif7hxy7q2wl6g16-format/bin/format ]]; then -00365| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00366| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00367| echo " skip .tasks/bin/format (symlink unchanged)" -00368| fi -00369| else -00370| rm -f .tasks/bin/format 2>/dev/null || true -00371| ln -s /nix/store/6ghzc9ybbxdc1551iif7hxy7q2wl6g16-format/bin/format .tasks/bin/format -00372| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00373| echo " write .tasks/bin/format -> /nix/store/6ghzc9ybbxdc1551iif7hxy7q2wl6g16-format/bin/format" -00374| fi -00375| -00376| # .tasks/bin/generate:proto (symlink) -00377| mkdir -p "$(dirname .tasks/bin/generate:proto)" -00378| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/generate:proto ]] && [[ "$(readlink .tasks/bin/generate:proto)" == /nix/store/m2rg47kx32br5sldlgvbar4w69g0xrph-generate-proto/bin/generate:proto ]]; then -00379| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00380| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00381| echo " skip .tasks/bin/generate:proto (symlink unchanged)" -00382| fi -00383| else -00384| rm -f .tasks/bin/generate:proto 2>/dev/null || true -00385| ln -s /nix/store/m2rg47kx32br5sldlgvbar4w69g0xrph-generate-proto/bin/generate:proto .tasks/bin/generate:proto -00386| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00387| echo " write .tasks/bin/generate:proto -> /nix/store/m2rg47kx32br5sldlgvbar4w69g0xrph-generate-proto/bin/generate:proto" -00388| fi -00389| -00390| # .tasks/bin/generate:types (symlink) -00391| mkdir -p "$(dirname .tasks/bin/generate:types)" -00392| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/generate:types ]] && [[ "$(readlink .tasks/bin/generate:types)" == /nix/store/5ag4fq73fl8wkdapyjvhkisgv1q5i4rg-generate-types/bin/generate:types ]]; then -00393| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00394| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00395| echo " skip .tasks/bin/generate:types (symlink unchanged)" -00396| fi -00397| else -00398| rm -f .tasks/bin/generate:types 2>/dev/null || true -00399| ln -s /nix/store/5ag4fq73fl8wkdapyjvhkisgv1q5i4rg-generate-types/bin/generate:types .tasks/bin/generate:types -00400| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00401| echo " write .tasks/bin/generate:types -> /nix/store/5ag4fq73fl8wkdapyjvhkisgv1q5i4rg-generate-types/bin/generate:types" -00402| fi -00403| -00404| # .tasks/bin/lint (symlink) -00405| mkdir -p "$(dirname .tasks/bin/lint)" -00406| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/lint ]] && [[ "$(readlink .tasks/bin/lint)" == /nix/store/xahhm84qf4qy410zznlxql1nsgql0v7x-lint/bin/lint ]]; then -00407| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00408| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00409| echo " skip .tasks/bin/lint (symlink unchanged)" -00410| fi -00411| else -00412| rm -f .tasks/bin/lint 2>/dev/null || true -00413| ln -s /nix/store/xahhm84qf4qy410zznlxql1nsgql0v7x-lint/bin/lint .tasks/bin/lint -00414| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00415| echo " write .tasks/bin/lint -> /nix/store/xahhm84qf4qy410zznlxql1nsgql0v7x-lint/bin/lint" -00416| fi -00417| -00418| # .tasks/bin/test (symlink) -00419| mkdir -p "$(dirname .tasks/bin/test)" -00420| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/test ]] && [[ "$(readlink .tasks/bin/test)" == /nix/store/1662k0y9ml0ax3mpah336lsknjcj14qb-test/bin/test ]]; then -00421| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00422| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00423| echo " skip .tasks/bin/test (symlink unchanged)" -00424| fi -00425| else -00426| rm -f .tasks/bin/test 2>/dev/null || true -00427| ln -s /nix/store/1662k0y9ml0ax3mpah336lsknjcj14qb-test/bin/test .tasks/bin/test -00428| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00429| echo " write .tasks/bin/test -> /nix/store/1662k0y9ml0ax3mpah336lsknjcj14qb-test/bin/test" -00430| fi -00431| -00432| # .tasks/bin/test:coverage (symlink) -00433| mkdir -p "$(dirname .tasks/bin/test:coverage)" -00434| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/test:coverage ]] && [[ "$(readlink .tasks/bin/test:coverage)" == /nix/store/c4znmzff2c3mkf7llrlhppxy4ym0sajg-test-coverage/bin/test:coverage ]]; then -00435| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00436| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00437| echo " skip .tasks/bin/test:coverage (symlink unchanged)" -00438| fi -00439| else -00440| rm -f .tasks/bin/test:coverage 2>/dev/null || true -00441| ln -s /nix/store/c4znmzff2c3mkf7llrlhppxy4ym0sajg-test-coverage/bin/test:coverage .tasks/bin/test:coverage -00442| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00443| echo " write .tasks/bin/test:coverage -> /nix/store/c4znmzff2c3mkf7llrlhppxy4ym0sajg-test-coverage/bin/test:coverage" -00444| fi -00445| -00446| # .tasks/bin/test:watch (symlink) -00447| mkdir -p "$(dirname .tasks/bin/test:watch)" -00448| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/test:watch ]] && [[ "$(readlink .tasks/bin/test:watch)" == /nix/store/zk3kiprrx8fmh5p7wv2zp3px9mf7iaii-test-watch/bin/test:watch ]]; then -00449| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00450| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00451| echo " skip .tasks/bin/test:watch (symlink unchanged)" -00452| fi -00453| else -00454| rm -f .tasks/bin/test:watch 2>/dev/null || true -00455| ln -s /nix/store/zk3kiprrx8fmh5p7wv2zp3px9mf7iaii-test-watch/bin/test:watch .tasks/bin/test:watch -00456| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00457| echo " write .tasks/bin/test:watch -> /nix/store/zk3kiprrx8fmh5p7wv2zp3px9mf7iaii-test-watch/bin/test:watch" -00458| fi -00459| -00460| # .tasks/bin/typecheck (symlink) -00461| mkdir -p "$(dirname .tasks/bin/typecheck)" -00462| if [[ "$FORCE" == "0" ]] && [[ -L .tasks/bin/typecheck ]] && [[ "$(readlink .tasks/bin/typecheck)" == /nix/store/kbzwwd2gzyjrmp6jz5nm83lwaxl6k2vs-typecheck/bin/typecheck ]]; then -00463| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00464| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00465| echo " skip .tasks/bin/typecheck (symlink unchanged)" -00466| fi -00467| else -00468| rm -f .tasks/bin/typecheck 2>/dev/null || true -00469| ln -s /nix/store/kbzwwd2gzyjrmp6jz5nm83lwaxl6k2vs-typecheck/bin/typecheck .tasks/bin/typecheck -00470| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00471| echo " write .tasks/bin/typecheck -> /nix/store/kbzwwd2gzyjrmp6jz5nm83lwaxl6k2vs-typecheck/bin/typecheck" -00472| fi -00473| -00474| # .vscode/settings.json (derivation) -00475| _src=/nix/store/wa74w8k44yiyl1ni855hidyfrmhsdswf-settings.json -00476| _dst=.vscode/settings.json -00477| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00478| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00479| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00480| echo " skip .vscode/settings.json (unchanged)" -00481| fi -00482| else -00483| mkdir -p "$(dirname "$_dst")" -00484| cat "$_src" > "$_dst" -00485| -00486| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00487| echo " write .vscode/settings.json" -00488| fi -00489| -00490| # .zed/settings.json (derivation) -00491| _src=/nix/store/xfv3cj9bh0z5zkxp32qjm8za1d5q4gy3-zed-settings.json -00492| _dst=.zed/settings.json -00493| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00494| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00495| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00496| echo " skip .zed/settings.json (unchanged)" -00497| fi -00498| else -00499| mkdir -p "$(dirname "$_dst")" -00500| cat "$_src" > "$_dst" -00501| -00502| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00503| echo " write .zed/settings.json" -00504| fi -00505| -00506| # apps/docs/.oxlintrc.json (text) -00507| _src=/nix/store/rwqx0j0jsx39ldg8kxdi54v655bgy0pd-oxlintrc.json -00508| _dst=apps/docs/.oxlintrc.json -00509| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00510| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00511| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00512| echo " skip apps/docs/.oxlintrc.json (unchanged)" -00513| fi -00514| else -00515| mkdir -p "$(dirname "$_dst")" -00516| cat "$_src" > "$_dst" -00517| -00518| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00519| echo " write apps/docs/.oxlintrc.json" -00520| fi -00521| -00522| # apps/web/.oxlintrc.json (text) -00523| _src=/nix/store/j7y29ld6dwq4ii1qqlvn27jvcbw5igqb-oxlintrc.json -00524| _dst=apps/web/.oxlintrc.json -00525| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00526| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00527| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00528| echo " skip apps/web/.oxlintrc.json (unchanged)" -00529| fi -00530| else -00531| mkdir -p "$(dirname "$_dst")" -00532| cat "$_src" > "$_dst" -00533| -00534| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00535| echo " write apps/web/.oxlintrc.json" -00536| fi -00537| -00538| # apps/web/.tasks/bin/container-build (symlink) -00539| mkdir -p "$(dirname apps/web/.tasks/bin/container-build)" -00540| if [[ "$FORCE" == "0" ]] && [[ -L apps/web/.tasks/bin/container-build ]] && [[ "$(readlink apps/web/.tasks/bin/container-build)" == /nix/store/hq5ypj3qldnkd0d4qcbrrnwsj0159ydz-container-build/bin/container-build ]]; then -00541| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00542| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00543| echo " skip apps/web/.tasks/bin/container-build (symlink unchanged)" -00544| fi -00545| else -00546| rm -f apps/web/.tasks/bin/container-build 2>/dev/null || true -00547| ln -s /nix/store/hq5ypj3qldnkd0d4qcbrrnwsj0159ydz-container-build/bin/container-build apps/web/.tasks/bin/container-build -00548| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00549| echo " write apps/web/.tasks/bin/container-build -> /nix/store/hq5ypj3qldnkd0d4qcbrrnwsj0159ydz-container-build/bin/container-build" -00550| fi -00551| -00552| # apps/web/.tasks/bin/container-push (symlink) -00553| mkdir -p "$(dirname apps/web/.tasks/bin/container-push)" -00554| if [[ "$FORCE" == "0" ]] && [[ -L apps/web/.tasks/bin/container-push ]] && [[ "$(readlink apps/web/.tasks/bin/container-push)" == /nix/store/dz49h5bmflcq37wnviqfw0law2z7lb7k-container-push/bin/container-push ]]; then -00555| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00556| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00557| echo " skip apps/web/.tasks/bin/container-push (symlink unchanged)" -00558| fi -00559| else -00560| rm -f apps/web/.tasks/bin/container-push 2>/dev/null || true -00561| ln -s /nix/store/dz49h5bmflcq37wnviqfw0law2z7lb7k-container-push/bin/container-push apps/web/.tasks/bin/container-push -00562| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00563| echo " write apps/web/.tasks/bin/container-push -> /nix/store/dz49h5bmflcq37wnviqfw0law2z7lb7k-container-push/bin/container-push" -00564| fi -00565| -00566| # apps/web/.tasks/bin/deploy (symlink) -00567| mkdir -p "$(dirname apps/web/.tasks/bin/deploy)" -00568| if [[ "$FORCE" == "0" ]] && [[ -L apps/web/.tasks/bin/deploy ]] && [[ "$(readlink apps/web/.tasks/bin/deploy)" == /nix/store/l61kwbrxlj0zl5s58wjdyp76fi4ax16m-deploy/bin/deploy ]]; then -00569| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00570| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00571| echo " skip apps/web/.tasks/bin/deploy (symlink unchanged)" -00572| fi -00573| else -00574| rm -f apps/web/.tasks/bin/deploy 2>/dev/null || true -00575| ln -s /nix/store/l61kwbrxlj0zl5s58wjdyp76fi4ax16m-deploy/bin/deploy apps/web/.tasks/bin/deploy -00576| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00577| echo " write apps/web/.tasks/bin/deploy -> /nix/store/l61kwbrxlj0zl5s58wjdyp76fi4ax16m-deploy/bin/deploy" -00578| fi -00579| -00580| # apps/web/fly.toml (text) -00581| _src=/nix/store/bnizikk1ijfm84zkz9wdsq4lff4yb5xx-fly.toml -00582| _dst=apps/web/fly.toml -00583| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00584| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00585| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00586| echo " skip apps/web/fly.toml (unchanged)" -00587| fi -00588| else -00589| mkdir -p "$(dirname "$_dst")" -00590| cat "$_src" > "$_dst" -00591| -00592| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00593| echo " write apps/web/fly.toml" -00594| fi -00595| -00596| # devshell (text) -00597| _src=/nix/store/mbz9mvhlkbzf8b6f9xacabkj7lmd7a9h-devshell -00598| _dst=devshell -00599| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00600| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00601| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00602| echo " skip devshell (unchanged)" -00603| fi -00604| else -00605| mkdir -p "$(dirname "$_dst")" -00606| cat "$_src" > "$_dst" -00607| chmod 0755 "$_dst" -00608| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00609| echo " write devshell" -00610| fi -00611| -00612| # packages/gen/env/README.md (text) -00613| _src=/nix/store/ssri569jwrw43mw28w0bvdpl0jbfcq82-README.md -00614| _dst=packages/gen/env/README.md -00615| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00616| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00617| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00618| echo " skip packages/gen/env/README.md (unchanged)" -00619| fi -00620| else -00621| mkdir -p "$(dirname "$_dst")" -00622| cat "$_src" > "$_dst" -00623| -00624| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00625| echo " write packages/gen/env/README.md" -00626| fi -00627| -00628| # packages/gen/env/data/shared/vars.yaml (text) -00629| _src=/nix/store/7rwz9gj7pinirz4d0j8fbcrvq8nx57lx-vars.yaml -00630| _dst=packages/gen/env/data/shared/vars.yaml -00631| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00632| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00633| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00634| echo " skip packages/gen/env/data/shared/vars.yaml (unchanged)" -00635| fi -00636| else -00637| mkdir -p "$(dirname "$_dst")" -00638| cat "$_src" > "$_dst" -00639| -00640| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00641| echo " write packages/gen/env/data/shared/vars.yaml" -00642| fi -00643| -00644| # packages/gen/env/package.json (text) -00645| _src=/nix/store/36301jqd8xcyfv6znp3xqbaiqjrl4qhp-package.json -00646| _dst=packages/gen/env/package.json -00647| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00648| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00649| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00650| echo " skip packages/gen/env/package.json (unchanged)" -00651| fi -00652| else -00653| mkdir -p "$(dirname "$_dst")" -00654| cat "$_src" > "$_dst" -00655| -00656| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00657| echo " write packages/gen/env/package.json" -00658| fi -00659| -00660| # packages/gen/env/src/entrypoints/docs.ts (text) -00661| _src=/nix/store/c9kk104hk31m3b8jlzhsm5mnpfi640pv-docs.ts -00662| _dst=packages/gen/env/src/entrypoints/docs.ts -00663| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00664| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00665| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00666| echo " skip packages/gen/env/src/entrypoints/docs.ts (unchanged)" -00667| fi -00668| else -00669| mkdir -p "$(dirname "$_dst")" -00670| cat "$_src" > "$_dst" -00671| -00672| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00673| echo " write packages/gen/env/src/entrypoints/docs.ts" -00674| fi -00675| -00676| # packages/gen/env/src/entrypoints/index.ts (text) -00677| _src=/nix/store/d1yhx27nrwc9v0qq22rsmyiclgx7ksva-index.ts -00678| _dst=packages/gen/env/src/entrypoints/index.ts -00679| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00680| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00681| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00682| echo " skip packages/gen/env/src/entrypoints/index.ts (unchanged)" -00683| fi -00684| else -00685| mkdir -p "$(dirname "$_dst")" -00686| cat "$_src" > "$_dst" -00687| -00688| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00689| echo " write packages/gen/env/src/entrypoints/index.ts" -00690| fi -00691| -00692| # packages/gen/env/src/entrypoints/stack-go.ts (text) -00693| _src=/nix/store/vvax4gykxnk86j99p2q1bd0wxk8m15sn-stack-go.ts -00694| _dst=packages/gen/env/src/entrypoints/stack-go.ts -00695| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00696| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00697| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00698| echo " skip packages/gen/env/src/entrypoints/stack-go.ts (unchanged)" -00699| fi -00700| else -00701| mkdir -p "$(dirname "$_dst")" -00702| cat "$_src" > "$_dst" -00703| -00704| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00705| echo " write packages/gen/env/src/entrypoints/stack-go.ts" -00706| fi -00707| -00708| # packages/gen/env/src/entrypoints/web.ts (text) -00709| _src=/nix/store/jn3rarwardw3p46blgbysski4vcq4dji-web.ts -00710| _dst=packages/gen/env/src/entrypoints/web.ts -00711| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00712| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00713| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00714| echo " skip packages/gen/env/src/entrypoints/web.ts (unchanged)" -00715| fi -00716| else -00717| mkdir -p "$(dirname "$_dst")" -00718| cat "$_src" > "$_dst" -00719| -00720| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00721| echo " write packages/gen/env/src/entrypoints/web.ts" -00722| fi -00723| -00724| # packages/gen/env/src/generated/index.ts (text) -00725| _src=/nix/store/cpka04v1fknizgvgr4bvxgj89vp0fxv1-index.ts -00726| _dst=packages/gen/env/src/generated/index.ts -00727| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00728| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00729| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00730| echo " skip packages/gen/env/src/generated/index.ts (unchanged)" -00731| fi -00732| else -00733| mkdir -p "$(dirname "$_dst")" -00734| cat "$_src" > "$_dst" -00735| -00736| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00737| echo " write packages/gen/env/src/generated/index.ts" -00738| fi -00739| -00740| # packages/gen/env/src/generated/stack-go/dev.ts (text) -00741| _src=/nix/store/blzicmf8f46klkr28mwjv6wkvin0qp09-dev.ts -00742| _dst=packages/gen/env/src/generated/stack-go/dev.ts -00743| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00744| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00745| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00746| echo " skip packages/gen/env/src/generated/stack-go/dev.ts (unchanged)" -00747| fi -00748| else -00749| mkdir -p "$(dirname "$_dst")" -00750| cat "$_src" > "$_dst" -00751| -00752| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00753| echo " write packages/gen/env/src/generated/stack-go/dev.ts" -00754| fi -00755| -00756| # packages/gen/env/src/generated/stack-go/index.ts (text) -00757| _src=/nix/store/xnh2v6irga4pyap5pbgzvzkcgjkf3s2x-index.ts -00758| _dst=packages/gen/env/src/generated/stack-go/index.ts -00759| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00760| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00761| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00762| echo " skip packages/gen/env/src/generated/stack-go/index.ts (unchanged)" -00763| fi -00764| else -00765| mkdir -p "$(dirname "$_dst")" -00766| cat "$_src" > "$_dst" -00767| -00768| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00769| echo " write packages/gen/env/src/generated/stack-go/index.ts" -00770| fi -00771| -00772| # packages/gen/env/src/generated/web/dev.ts (text) -00773| _src=/nix/store/j6gcx3rqqxfakkwd7q7124qask4ig7rq-dev.ts -00774| _dst=packages/gen/env/src/generated/web/dev.ts -00775| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00776| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00777| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00778| echo " skip packages/gen/env/src/generated/web/dev.ts (unchanged)" -00779| fi -00780| else -00781| mkdir -p "$(dirname "$_dst")" -00782| cat "$_src" > "$_dst" -00783| -00784| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00785| echo " write packages/gen/env/src/generated/web/dev.ts" -00786| fi -00787| -00788| # packages/gen/env/src/generated/web/index.ts (text) -00789| _src=/nix/store/xnh2v6irga4pyap5pbgzvzkcgjkf3s2x-index.ts -00790| _dst=packages/gen/env/src/generated/web/index.ts -00791| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00792| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00793| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00794| echo " skip packages/gen/env/src/generated/web/index.ts (unchanged)" -00795| fi -00796| else -00797| mkdir -p "$(dirname "$_dst")" -00798| cat "$_src" > "$_dst" -00799| -00800| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00801| echo " write packages/gen/env/src/generated/web/index.ts" -00802| fi -00803| -00804| # packages/gen/env/src/index.ts (text) -00805| _src=/nix/store/096pv4vxh8qz2ykq89wwi05pc5gbrsd6-index.ts -00806| _dst=packages/gen/env/src/index.ts -00807| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00808| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00809| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00810| echo " skip packages/gen/env/src/index.ts (unchanged)" -00811| fi -00812| else -00813| mkdir -p "$(dirname "$_dst")" -00814| cat "$_src" > "$_dst" -00815| -00816| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00817| echo " write packages/gen/env/src/index.ts" -00818| fi -00819| -00820| # packages/gen/env/tsconfig.json (text) -00821| _src=/nix/store/xlw50m2x5wwfx35vw7nlifmn20f5m74p-tsconfig.json -00822| _dst=packages/gen/env/tsconfig.json -00823| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00824| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00825| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00826| echo " skip packages/gen/env/tsconfig.json (unchanged)" -00827| fi -00828| else -00829| mkdir -p "$(dirname "$_dst")" -00830| cat "$_src" > "$_dst" -00831| -00832| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00833| echo " write packages/gen/env/tsconfig.json" -00834| fi -00835| -00836| # packages/infra/README.md (text) -00837| _src=/nix/store/yrg9k2k0sq84ypsz53h9b6s08naixpjn-README.md -00838| _dst=packages/infra/README.md -00839| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00840| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00841| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00842| echo " skip packages/infra/README.md (unchanged)" -00843| fi -00844| else -00845| mkdir -p "$(dirname "$_dst")" -00846| cat "$_src" > "$_dst" -00847| chmod 0644 "$_dst" -00848| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00849| echo " write packages/infra/README.md" -00850| fi -00851| -00852| # packages/infra/alchemy.run.ts (text) -00853| _src=/nix/store/19a18m8xl2z8h3ljwrjs0pwnnnilf9lx-alchemy.run.ts -00854| _dst=packages/infra/alchemy.run.ts -00855| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00856| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00857| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00858| echo " skip packages/infra/alchemy.run.ts (unchanged)" -00859| fi -00860| else -00861| mkdir -p "$(dirname "$_dst")" -00862| cat "$_src" > "$_dst" -00863| chmod 0644 "$_dst" -00864| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00865| echo " write packages/infra/alchemy.run.ts" -00866| fi -00867| -00868| # packages/infra/modules/aws-secrets/index.ts (text) -00869| _src=/nix/store/5z0y9wgh6ygksx7dz88zj1d1x33i80v2-index.ts -00870| _dst=packages/infra/modules/aws-secrets/index.ts -00871| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00872| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00873| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00874| echo " skip packages/infra/modules/aws-secrets/index.ts (unchanged)" -00875| fi -00876| else -00877| mkdir -p "$(dirname "$_dst")" -00878| cat "$_src" > "$_dst" -00879| chmod 0644 "$_dst" -00880| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00881| echo " write packages/infra/modules/aws-secrets/index.ts" -00882| fi -00883| -00884| # packages/infra/modules/aws-secrets/policies.ts (text) -00885| _src=/nix/store/wpa910hzzsfvhcjvc62kwkr642zhzb75-policies.ts -00886| _dst=packages/infra/modules/aws-secrets/policies.ts -00887| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00888| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00889| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00890| echo " skip packages/infra/modules/aws-secrets/policies.ts (unchanged)" -00891| fi -00892| else -00893| mkdir -p "$(dirname "$_dst")" -00894| cat "$_src" > "$_dst" -00895| chmod 0644 "$_dst" -00896| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00897| echo " write packages/infra/modules/aws-secrets/policies.ts" -00898| fi -00899| -00900| # packages/infra/modules/database.ts (text) -00901| _src=/nix/store/5mv1q14p46aycghhakihkfi9jgn23680-database.ts -00902| _dst=packages/infra/modules/database.ts -00903| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00904| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00905| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00906| echo " skip packages/infra/modules/database.ts (unchanged)" -00907| fi -00908| else -00909| mkdir -p "$(dirname "$_dst")" -00910| cat "$_src" > "$_dst" -00911| chmod 0644 "$_dst" -00912| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00913| echo " write packages/infra/modules/database.ts" -00914| fi -00915| -00916| # packages/infra/modules/deployment.ts (text) -00917| _src=/nix/store/r6r2jlqfbvsl8xfbsaliryq3l99z17a0-deployment.ts -00918| _dst=packages/infra/modules/deployment.ts -00919| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00920| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00921| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00922| echo " skip packages/infra/modules/deployment.ts (unchanged)" -00923| fi -00924| else -00925| mkdir -p "$(dirname "$_dst")" -00926| cat "$_src" > "$_dst" -00927| chmod 0644 "$_dst" -00928| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00929| echo " write packages/infra/modules/deployment.ts" -00930| fi -00931| -00932| # packages/infra/package.json (json) -00933| _src=/nix/store/bwm12v071f1wxvsjyxx24dw3d5galwz3-package.json -00934| _dst=packages/infra/package.json -00935| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00936| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00937| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00938| echo " skip packages/infra/package.json (unchanged)" -00939| fi -00940| else -00941| mkdir -p "$(dirname "$_dst")" -00942| cat "$_src" > "$_dst" -00943| chmod 0644 "$_dst" -00944| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00945| echo " write packages/infra/package.json" -00946| fi -00947| -00948| # packages/infra/src/index.ts (text) -00949| _src=/nix/store/c15vywm3vy5g7p6zwh8pks79wycxn8x1-index.ts -00950| _dst=packages/infra/src/index.ts -00951| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00952| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00953| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00954| echo " skip packages/infra/src/index.ts (unchanged)" -00955| fi -00956| else -00957| mkdir -p "$(dirname "$_dst")" -00958| cat "$_src" > "$_dst" -00959| chmod 0644 "$_dst" -00960| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00961| echo " write packages/infra/src/index.ts" -00962| fi -00963| -00964| # packages/infra/src/resources/iam-role.ts (text) -00965| _src=/nix/store/wa89f8n7jgksqwi9dm8x6hxg15x4hbxh-iam-role.ts -00966| _dst=packages/infra/src/resources/iam-role.ts -00967| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00968| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00969| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00970| echo " skip packages/infra/src/resources/iam-role.ts (unchanged)" -00971| fi -00972| else -00973| mkdir -p "$(dirname "$_dst")" -00974| cat "$_src" > "$_dst" -00975| chmod 0644 "$_dst" -00976| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00977| echo " write packages/infra/src/resources/iam-role.ts" -00978| fi -00979| -00980| # packages/infra/src/resources/kms-alias.ts (text) -00981| _src=/nix/store/5p9p1cmgkk1srzpyajcrbpm0pq4x063x-kms-alias.ts -00982| _dst=packages/infra/src/resources/kms-alias.ts -00983| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -00984| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -00985| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -00986| echo " skip packages/infra/src/resources/kms-alias.ts (unchanged)" -00987| fi -00988| else -00989| mkdir -p "$(dirname "$_dst")" -00990| cat "$_src" > "$_dst" -00991| chmod 0644 "$_dst" -00992| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -00993| echo " write packages/infra/src/resources/kms-alias.ts" -00994| fi -00995| -00996| # packages/infra/src/resources/kms-key.ts (text) -00997| _src=/nix/store/vl8ya63nzf1zvv7zajs59maj4l3ggz8q-kms-key.ts -00998| _dst=packages/infra/src/resources/kms-key.ts -00999| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -01000| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) -01001| if [[ "${STACKPANEL_DEBUG:-}" == "1" ]] || [[ "$VERBOSE" == "1" ]]; then -01002| echo " skip packages/infra/src/resources/kms-key.ts (unchanged)" -01003| fi -01004| else -01005| mkdir -p "$(dirname "$_dst")" -01006| cat "$_src" > "$_dst" -01007| chmod 0644 "$_dst" -01008| WRITTEN_COUNT=$((WRITTEN_COUNT + 1)) -01009| echo " write packages/infra/src/resources/kms-key.ts" -01010| fi -01011| -01012| # packages/infra/src/types.ts (text) -01013| _src=/nix/store/k9fzvkiq52h33rqp4qm3m0mmn57cvljm-types.ts -01014| _dst=packages/infra/src/types.ts -01015| if [[ "$FORCE" == "0" ]] && [[ -f "$_dst" ]] && [[ "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_dst" | cut -d' ' -f1)" == "$(/nix/store/sqv8nbx301w2xg2iqzim7hraid6ki1la-coreutils-9.8/bin/sha256sum "$_src" | cut -d' ' -f1)" ]]; then -01016| UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1)) - -(Output truncated at 51200 bytes. Use 'offset' parameter to read beyond line 1016) - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 68.1s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Reproduce hanging nix develop invocation" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 60000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 125.7s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c bash -lc 'echo OK'", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 120000, - "description": "Test non-interactive nix develop execution" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 120000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 65.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Retest quick non-interactive develop command" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. - - - -bash tool terminated command after exceeding timeout 60000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.7s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps aux | rg \"nix( |-)\"", - "description": "Inspect running nix processes" -} -``` -**Output:** -``` -cm 1879 32.3 1.1 1892714544 2203424 ?? R 4Feb26 3787:08.73 /Applications/Spotify.app/Contents/Frameworks/Spotify Helper (Renderer).app/Contents/MacOS/Spotify Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Caches/com.spotify.client --log-severity=disable --user-agent-product=Chrome/143.0.7499.147 Spotify/1.2.82.428 --autoplay-policy=no-user-gesture-required --lang=en --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=7 --time-ticks-at-unix-epoch=-1770199086552014 --launch-time-ticks=63893764 --shared-files --metrics-shmem-handle=1752395122,r,18075293782321839323,16565303333635656333,2097152 --field-trial-handle=1718379636,r,12727821680991632083,2473769911402266290,262144 --disable-features=BackForwardCache,FullscreenBubbleShowOrigin,GatherProcessRequirementMetrics,GlicActorUi,LensOverlay,MacAppCodeSignClone,PartitionAllocDanglingPtr,PartitionAllocUnretainedDanglingPtr,StorageNotificationService --variations-seed-version --trace-process-track-uuid=3190708992871164437 --seatbelt-client=156 -cm 82121 4.1 0.2 1891142576 498224 ?? R 5:26AM 40:49.46 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=12135 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2073854062053 --shared-files --metrics-shmem-handle=1752395122,r,17709506286022536681,3949385927244310922,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190720357314709109 --seatbelt-client=479 -cm 40672 3.1 0.4 1890660528 805232 ?? S Thu02PM 267:35.23 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=149 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=1932012527390 --shared-files --metrics-shmem-handle=1752395122,r,15765483710706824124,16002080598677288752,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190709125931106995 --seatbelt-client=482 -cm 61192 1.9 0.3 1892373360 579536 ?? R Thu12PM 145:07.09 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=52 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=1927449510017 --shared-files --metrics-shmem-handle=1752395122,r,12768281527441084377,2585529616629979506,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190709035038047642 --seatbelt-client=570 -cm 56675 1.9 0.3 1892565824 697792 ?? S Thu11PM 43:40.33 /Applications/Cursor.app/Contents/Frameworks/Cursor Helper (Renderer).app/Contents/MacOS/Cursor Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Cursor --standard-schemes=vscode-webview,vscode-file --enable-sandbox --secure-schemes=vscode-webview,vscode-file,sentry-ipc --bypasscsp-schemes=sentry-ipc --cors-schemes=vscode-webview,vscode-file,sentry-ipc --fetch-schemes=vscode-webview,vscode-file,sentry-ipc --service-worker-schemes=vscode-webview --code-cache-schemes=vscode-webview,vscode-file --app-path=/Applications/Cursor.app/Contents/Resources/app --enable-sandbox --enable-blink-features=HighlightAPI --force-gpu-mem-available-mb=1024 --disable-blink-features=FontMatchingCTMigration,StandardizedBrowserZoom, --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=5 --time-ticks-at-unix-epoch=-1770211314554835 --launch-time-ticks=1965180301532 --shared-files --field-trial-handle=1718379636,r,821801881454846134,1381156323050333243,262144 --enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,PdfUseShowSaveFilePicker,ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma --disable-features=CalculateNativeWinOcclusion,LocalNetworkAccessChecks,MacWebContentsOcclusion,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TimeoutHangingVideoCaptureStarts,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190708990997080739 --vscode-window-config=vscode:7bb1ea4a-3a6f-45fd-9bb4-3eaaab1b60fb --seatbelt-client=64 -cm 5730 0.4 0.2 1890874128 476992 ?? S 5:42AM 0:49.54 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=12226 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2074805677428 --shared-files --metrics-shmem-handle=1752395122,r,6055735794293847437,9864598546796104124,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190720442585517368 --seatbelt-client=412 -cm 89293 0.3 0.5 1892753360 1011760 ?? R Fri09AM 46:03.10 /Applications/Cursor.app/Contents/Frameworks/Cursor Helper (Renderer).app/Contents/MacOS/Cursor Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Cursor --standard-schemes=vscode-webview,vscode-file --enable-sandbox --secure-schemes=vscode-webview,vscode-file,sentry-ipc --bypasscsp-schemes=sentry-ipc --cors-schemes=vscode-webview,vscode-file,sentry-ipc --fetch-schemes=vscode-webview,vscode-file,sentry-ipc --service-worker-schemes=vscode-webview --code-cache-schemes=vscode-webview,vscode-file --app-path=/Applications/Cursor.app/Contents/Resources/app --enable-sandbox --enable-blink-features=HighlightAPI --force-gpu-mem-available-mb=1024 --disable-blink-features=FontMatchingCTMigration,StandardizedBrowserZoom, --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=27 --time-ticks-at-unix-epoch=-1770211314554835 --launch-time-ticks=2001889058696 --shared-files --field-trial-handle=1718379636,r,821801881454846134,1381156323050333243,262144 --enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,PdfUseShowSaveFilePicker,ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma --disable-features=CalculateNativeWinOcclusion,LocalNetworkAccessChecks,MacWebContentsOcclusion,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TimeoutHangingVideoCaptureStarts,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190709011612001417 --vscode-window-config=vscode:8846bc4e-02ac-4d58-8a97-5856ac698630 --seatbelt-client=162 -cm 49508 0.3 0.2 1890850896 448016 ?? S 9:27PM 0:05.33 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=16476 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2130272407002 --shared-files --metrics-shmem-handle=1752395122,r,14884369062768137033,17099298946744255642,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190724425013375618 --seatbelt-client=408 -cm 3142 0.1 0.3 1890853136 641296 ?? S Fri05AM 7:25.93 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1634 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=1986687140792 --shared-files --metrics-shmem-handle=1752395122,r,10092697895117854445,120582960677777772,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190710517438252760 --seatbelt-client=204 -cm 61151 0.1 0.1 1890637600 230240 ?? S Thu12PM 21:24.21 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=24 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=1927446695400 --shared-files --metrics-shmem-handle=1752395122,r,5329380571875094890,7772071185423490799,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190709008800875870 --seatbelt-client=548 -cm 51888 0.1 0.2 1890854688 477584 ?? S 9:29PM 0:05.42 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=39 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2130370220682 --shared-files --metrics-shmem-handle=1752395122,r,5916929602840116513,1792270658326080582,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190709022856503605 --seatbelt-client=470 -cm 30641 0.1 0.1 1892464656 158816 ?? S 7:45AM 1:39.98 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --extension-process --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=12672 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2082227149336 --shared-files --metrics-shmem-handle=1752395122,r,7220323268356968865,13724241527034123714,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190720860506182022 --seatbelt-client=397 -cm 30690 0.0 0.2 1890855040 493968 ?? S 8:14PM 0:08.24 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=16117 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2125874363767 --shared-files --metrics-shmem-handle=1752395122,r,5118448565227957357,17479617471176007340,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190724088615351827 --seatbelt-client=233 -cm 47641 0.0 0.1 1890662048 119088 ?? S 7:35PM 0:00.65 /Applications/1Password.app/Contents/Frameworks/1Password Helper (Renderer).app/Contents/MacOS/1Password Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/1Password --standard-schemes=resource,file-icon --enable-sandbox --bypasscsp-schemes=resource,file-icon --fetch-schemes=resource,file-icon --app-path=/Applications/1Password.app/Contents/Resources/app.asar --enable-sandbox --disable-blink-features=Auxclick --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=35 --time-ticks-at-unix-epoch=-1770211314645659 --launch-time-ticks=2123525506641 --shared-files --field-trial-handle=1718379636,r,13975914789046476565,2955752183344283696,262144 --enable-features=PdfUseShowSaveFilePicker,ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma --disable-features=LocalNetworkAccessChecks,MacWebContentsOcclusion,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TimeoutHangingVideoCaptureStarts,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190709019108336209 --seatbelt-client=17 -cm 25858 0.0 0.1 1892250288 165904 ?? S 7:28PM 0:11.16 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --extension-process --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2123131540230 --shared-files --metrics-shmem-handle=1752395122,r,10553073201464051367,7673053428883009174,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190708998493415531 --seatbelt-client=612 -cm 86267 0.0 0.2 1891038256 365264 ?? S 7:19PM 0:10.87 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=15870 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2122612203539 --shared-files --metrics-shmem-handle=1752395122,r,11109452145938223482,2862267384128081671,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190723857166015124 --seatbelt-client=514 -cm 70151 0.0 0.1 1892248976 162672 ?? S 7:13PM 0:15.20 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --extension-process --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1219 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2122238002685 --shared-files --metrics-shmem-handle=1752395122,r,4260162289140347127,1189276236456750648,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190710128565885425 --seatbelt-client=434 -cm 61590 0.0 0.1 1892432144 131648 ?? S 6:46PM 0:02.23 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --extension-process --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=15831 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2121898269092 --shared-files --metrics-shmem-handle=1752395122,r,13207449033862383205,11926576526052716047,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190723820621383013 --seatbelt-client=178 -cm 90248 0.0 0.1 1890609088 184112 ?? S 3:46PM 0:01.28 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1103 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2111090903328 --shared-files --metrics-shmem-handle=1752395122,r,614972508607143889,13647606844834214248,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190710019869030941 --seatbelt-client=496 -cm 90231 0.0 0.2 1890629632 302864 ?? S 3:46PM 7:02.58 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1102 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2111089946352 --shared-files --metrics-shmem-handle=1752395122,r,14315016306713090806,4528352458819450075,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190710018931989092 --seatbelt-client=496 -cm 80532 0.0 0.1 1890645440 223328 ?? S 3:40PM 0:01.90 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1085 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2110695011065 --shared-files --metrics-shmem-handle=1752395122,r,15697231376318228970,10173139644838729971,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190710003002277659 --seatbelt-client=150 -cm 58495 0.0 0.2 1890859856 388592 ?? S 3:25PM 0:16.31 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1051 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2109807747324 --shared-files --metrics-shmem-handle=1752395122,r,5971938317863129816,2741675552049810311,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190709971142854793 --seatbelt-client=651 -cm 56055 0.0 0.1 1890510528 165952 ?? S 3:23PM 0:00.74 /Applications/Dia.app/Contents/Frameworks/ArcCore.framework/Helpers/Browser Helper (Renderer).app/Contents/MacOS/Browser Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Dia/User Data --bcny-app-id=1 --enable-blink-features=MediaSessionEnterPictureInPictureOnGMeetOnly --enable-blink-features=TopBandColorSampling --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=1048 --time-ticks-at-unix-epoch=-1770211314528384 --launch-time-ticks=2109708876584 --shared-files --metrics-shmem-handle=1752395122,r,12849590721316341891,9859456646889618443,2097152 --field-trial-handle=1718379636,r,15378615743981171622,11036397517288871603,262144 --variations-seed-version --trace-process-track-uuid=3190709968331729246 --seatbelt-client=629 -cm 9994 0.0 0.1 1890832096 119824 ?? S 2:09PM 0:00.78 /Applications/Cursor.app/Contents/Frameworks/Cursor Helper (Renderer).app/Contents/MacOS/Cursor Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Cursor --standard-schemes=vscode-webview,vscode-file --enable-sandbox --secure-schemes=vscode-webview,vscode-file,sentry-ipc --bypasscsp-schemes=sentry-ipc --cors-schemes=vscode-webview,vscode-file,sentry-ipc --fetch-schemes=vscode-webview,vscode-file,sentry-ipc --service-worker-schemes=vscode-webview --code-cache-schemes=vscode-webview,vscode-file --app-path=/Applications/Cursor.app/Contents/Resources/app --enable-sandbox --enable-blink-features --disable-blink-features --force-gpu-mem-available-mb=1024 --disable-blink-features=FontMatchingCTMigration,StandardizedBrowserZoom, --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=83 --time-ticks-at-unix-epoch=-1770211314554835 --launch-time-ticks=2105255192062 --shared-files --field-trial-handle=1718379636,r,821801881454846134,1381156323050333243,262144 --enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,PdfUseShowSaveFilePicker,ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma --disable-features=CalculateNativeWinOcclusion,LocalNetworkAccessChecks,MacWebContentsOcclusion,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TimeoutHangingVideoCaptureStarts,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190709064086344961 --seatbelt-client=235 -cm 9621 0.0 0.0 1890611712 71856 ?? S 2:09PM 0:00.15 /Applications/Cursor.app/Contents/Frameworks/Cursor Helper (Renderer).app/Contents/MacOS/Cursor Helper (Renderer) --type=renderer --user-data-dir=/Users/cm/Library/Application Support/Cursor --standard-schemes=vscode-webview,vscode-file --enable-sandbox --secure-schemes=vscode-webview,vscode-file,sentry-ipc --bypasscsp-schemes=sentry-ipc --cors-schemes=vscode-webview,vscode-file,sentry-ipc --fetch-schemes=vscode-webview,vscode-file,sentry-ipc --service-worker-schemes=vscode-webview --code-cache-schemes=vscode-webview,vscode-file --app-path=/Applications/Cursor.app/Contents/Resources/app --enable-sandbox --enable-blink-features --disable-blink-features --force-gpu-mem-available-mb=1024 --disable-blink-features=FontMatchingCTMigration,StandardizedBrowserZoom, --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=76 --time-ticks-at-unix-epoch=-1770211314554835 --launch-time-ticks=2105245246816 --shared-files --field-trial-handle=1718379636,r,821801881454846134,1381156323050333243,262144 --enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,PdfUseShowSaveFilePicker,ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma --disable-features=CalculateNativeWinOcclusion,LocalNetworkAccessChecks,MacWebContentsOcclusion,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TimeoutHangingVideoCaptureStarts,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190709057527052018 --seatbelt-client=196 -cm 99002 0.0 0.0 435306400 9536 s064 S 2:07PM 0:01.73 bash --rcfile /tmp/nix-shell.qZDJVz -cm 59070 0.0 0.0 435301936 8816 s023 S+ 1:50PM 0:01.74 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O -cm 28199 0.0 0.2 1890852688 402848 ?? S 1:36PM 0:14.76 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=179 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2103292748373 --shared-files --metrics-shmem-handle=1752395122,r,4286272944177015539,13061816219432580972,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190709154042362465 --seatbelt-client=510 -cm 25804 0.0 0.1 1890830800 268672 ?? S 1:35PM 0:08.99 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14393 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2103199714891 --shared-files --metrics-shmem-handle=1752395122,r,3780853788117916526,8294123537721033029,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722473155204151 --seatbelt-client=171 -cm 25212 0.0 0.1 1890849072 287696 ?? S 1:34PM 0:07.12 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14391 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2103175280338 --shared-files --metrics-shmem-handle=1752395122,r,6005843363002711026,9835085928727484366,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722471281120453 --seatbelt-client=429 -cm 15822 0.0 0.2 1890839232 303216 ?? S 1:28PM 0:17.57 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14364 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2102793321502 --shared-files --metrics-shmem-handle=1752395122,r,13028497512916363731,13306905535991242292,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722445980990530 --seatbelt-client=511 -cm 90568 0.0 0.1 1890830672 238944 ?? S 1:12PM 0:05.88 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14260 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101816666498 --shared-files --metrics-shmem-handle=1752395122,r,7471742655265588790,15822132395704460822,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722348528638234 --seatbelt-client=261 -cm 90523 0.0 0.1 1890799168 274944 ?? S 1:12PM 0:06.68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14259 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101814640348 --shared-files --metrics-shmem-handle=1752395122,r,11394321922097131908,2140009244379054666,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722347591596385 --seatbelt-client=261 -cm 90460 0.0 0.2 1890839920 331472 ?? S 1:12PM 0:08.33 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14258 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101812600398 --shared-files --metrics-shmem-handle=1752395122,r,7049586399441138667,1265346096226965817,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722346654554536 --seatbelt-client=337 -cm 90358 0.0 0.2 1890839120 328960 ?? S 1:12PM 0:08.25 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14257 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101807375033 --shared-files --metrics-shmem-handle=1752395122,r,9722223590574894487,8486271041025842802,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722345717512687 --seatbelt-client=337 -cm 90260 0.0 0.2 1891030256 331680 ?? S 1:11PM 0:08.10 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14256 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101803734645 --shared-files --metrics-shmem-handle=1752395122,r,4889808995760383009,15956537908421313906,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722344780470838 --seatbelt-client=337 -cm 90217 0.0 0.2 1890840608 334624 ?? S 1:11PM 0:08.43 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14255 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101801634346 --shared-files --metrics-shmem-handle=1752395122,r,6631366657891871310,9390051221796308899,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722343843428989 --seatbelt-client=337 -cm 89980 0.0 0.2 1890839616 316608 ?? S 1:11PM 0:11.59 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14253 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101796690667 --shared-files --metrics-shmem-handle=1752395122,r,9543530895886968124,2718200641305989195,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722341969345291 --seatbelt-client=456 -cm 89727 0.0 0.2 1891025232 304064 ?? S 1:11PM 0:11.21 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14252 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101786740155 --shared-files --metrics-shmem-handle=1752395122,r,3242577752739665558,2902107156518921949,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722341032303442 --seatbelt-client=456 -cm 89482 0.0 0.2 1891036208 401072 ?? S 1:11PM 0:13.16 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14251 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101774667328 --shared-files --metrics-shmem-handle=1752395122,r,3253905989919417679,3938216856963800540,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722340095261593 --seatbelt-client=454 -cm 73473 0.0 0.2 1890847904 410992 ?? S 1:00PM 0:19.59 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14189 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2101133102417 --shared-files --metrics-shmem-handle=1752395122,r,9945870908694196731,7841009420349623255,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722281998666955 --seatbelt-client=476 -cm 59370 0.0 0.1 1891023312 189744 ?? S 12:51PM 0:04.32 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=14142 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2100600435571 --shared-files --metrics-shmem-handle=1752395122,r,16486840859621263112,14059444230334476142,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722237957700052 --seatbelt-client=491 -cm 57826 0.0 0.2 1891040912 470352 ?? S 12:20PM 0:52.68 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13976 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2098730759754 --shared-files --metrics-shmem-handle=1752395122,r,11025467588867423122,14433324792941386526,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722082408753118 --seatbelt-client=414 -root 52956 0.0 0.0 416404992 5600 ?? Ss 12:19PM 0:00.21 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 52955 0.0 0.0 416404992 5952 ?? Ss 12:19PM 0:00.13 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -cm 37722 0.0 0.2 1891038304 494096 ?? S 12:13PM 0:36.46 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13930 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2098281756863 --shared-files --metrics-shmem-handle=1752395122,r,734669570093349685,2975160268625714249,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190722039304828064 --seatbelt-client=414 -cm 19755 0.0 0.2 1897699728 421008 ?? S 12:07PM 0:56.28 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13875 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2097916933846 --shared-files --metrics-shmem-handle=1752395122,r,5301552394290123901,16425089366743802498,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721987767526369 --seatbelt-client=434 -root 80500 0.0 0.0 416404960 5552 ?? Ss 11:57AM 0:00.14 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 80499 0.0 0.0 416404960 6240 ?? Ss 11:57AM 0:00.23 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -cm 22953 0.0 0.0 435310368 8320 s023 S 11:44AM 0:01.65 bash --rcfile /tmp/nix-shell.RkSZvV -cm 89187 0.0 0.2 1892566960 462704 ?? S 11:21AM 0:35.53 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13634 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2095167445394 --shared-files --metrics-shmem-handle=1752395122,r,14385005636680248389,13601870375419926393,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721761940440760 --seatbelt-client=84 -cm 60778 0.0 0.1 1890955248 288256 ?? S 8:56AM 0:15.28 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13029 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2086477181665 --shared-files --metrics-shmem-handle=1752395122,r,8933584527402526060,1258933517181403392,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721195030122115 --seatbelt-client=455 -cm 60765 0.0 0.1 1890828320 250336 ?? S 8:56AM 0:12.34 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13028 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2086476280942 --shared-files --metrics-shmem-handle=1752395122,r,5340773591771501926,14840638318806840966,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721194093080266 --seatbelt-client=455 -cm 54604 0.0 0.2 1890839968 385040 ?? S 8:53AM 6:43.52 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=13007 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2086286301073 --shared-files --metrics-shmem-handle=1752395122,r,12360772142190442103,11054767784994036192,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721174415201437 --seatbelt-client=309 -root 51834 0.0 0.0 418219712 18144 ?? Ss 8:52AM 0:04.26 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 51833 0.0 0.0 418219712 19072 ?? Ss 8:52AM 0:04.17 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 51832 0.0 0.0 418219712 19104 ?? Ss 8:52AM 0:04.10 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 51831 0.0 0.0 418219712 19648 ?? Ss 8:52AM 0:04.20 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 51823 0.0 0.0 418219456 8896 ?? Ss 8:52AM 0:00.14 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -root 51821 0.0 0.0 418219456 9184 ?? Ss 8:52AM 0:00.22 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -cm 41502 0.0 0.4 1892619856 884192 ?? S 8:46AM 6:28.26 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=12961 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2085895070242 --shared-files --metrics-shmem-handle=1752395122,r,17874610411112786155,15905617351758893265,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190721131311276383 --seatbelt-client=456 -cm 33088 0.0 0.1 1890843456 275888 ?? S 7:46AM 0:14.88 /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/145.0.7632.117/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer) --type=renderer --metrics-client-id=a7a30fe8-bd52-4c6d-8c26-c8bf171ba0b5 --lang=en-US --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=12696 --time-ticks-at-unix-epoch=-1770211314516819 --launch-time-ticks=2082298140610 --shared-files --metrics-shmem-handle=1752395122,r,13830121283307077749,7567713538804634704,2097152 --field-trial-handle=1718379636,r,9888638325425578765,14862384303711314753,262144 --variations-seed-version=20260226-170056.924000-production --trace-process-track-uuid=3190720882995186398 --seatbelt-client=470 - -...164816 bytes truncated... - -The tool call succeeded but the output was truncated. Full output saved to: /Users/cm/.local/share/opencode/tool-output/tool_ca7efb208001LPx5djJueThZWm -Use the Task tool to have explore agent process this file with Grep and Read (with offset/limit). Do NOT read the full file yourself - delegate to save context. -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 9.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -axo pid,ppid,stat,etime,command | rg \"nix-daemon|nix develop|/tmp/nix-shell|stack hook|write-files|step-cert-setup|nix-shell\"", - "description": "Inspect nix and shellhook related processes" -} -``` -**Output:** -``` -51821 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -51823 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -51831 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -51832 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -51833 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -51834 85724 Ss 12:55:12 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -52955 85724 Ss 09:28:09 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -52956 85724 Ss 09:28:09 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -80499 85724 Ss 09:50:19 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -80500 85724 Ss 09:50:19 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -85586 85724 Ss 07:40 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -85629 85724 Ss 07:37 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -85724 91592 S 09-06:29:13 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -87957 85724 Ss 01-15:54:17 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -87958 85724 Ss 01-15:54:17 /nix/var/nix/profiles/default/bin/nix-daemon --option json-log-path /var/run/determinate-nixd-logger.socket -90006 85629 Ss 04:56 nix-daemon __build-remote 6 -97997 32823 Ss 00:00 /bin/zsh -c ps -axo pid,ppid,stat,etime,command | rg "nix-daemon|nix develop|/tmp/nix-shell|stack hook|write-files|step-cert-setup|nix-shell" -97999 97997 U 00:00 rg nix-daemon|nix develop|/tmp/nix-shell|stack hook|write-files|step-cert-setup|nix-shell -12984 72659 S 02-21:34:50 bash --rcfile /tmp/nix-shell.ifHEBl -85568 5179 S+ 07:40 nix develop --verbose --debug --log-format bar-with-logs -L -22953 23851 S 10:03:16 bash --rcfile /tmp/nix-shell.RkSZvV -59070 22953 S+ 07:57:11 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O -75186 52085 S+ 01:05:30 bash --rcfile /tmp/nix-shell.5v7q3p -45950 11608 S 01-01:48:58 bash --rcfile /tmp/nix-shell.n5blbc -99002 90569 S 07:39:50 bash --rcfile /tmp/nix-shell.qZDJVz - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.6s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -axo pid,ppid,stat,etime,command | rg \"^ *85568 |^ *[0-9]+ +85568 |^ *[0-9]+ +59070 |^ *[0-9]+ +22953 \"", - "description": "Inspect hanging nix develop process tree" -} -``` -**Output:** -``` -85568 5179 S+ 07:48 nix develop --verbose --debug --log-format bar-with-logs -L -59070 22953 S+ 07:57:19 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 9.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix show-config | rg \"^builders =|^max-jobs =|^builders-use-substitutes =|^fallback =\"", - "description": "Check nix builder configuration values" -} -``` -**Output:** -``` -warning: 'show-config' is a deprecated alias for 'config show' -builders = ssh://root@100.102.113.26 x86_64-linux /etc/nix/builder_ed25519 16 1 big-parallel,benchmark,kvm,nixos-test -builders-use-substitutes = true -fallback = false -max-jobs = 24 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.9s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ssh -o ConnectTimeout=5 -i /etc/nix/builder_ed25519 root@100.102.113.26 'echo ok'", - "description": "Test remote nix builder SSH connectivity" -} -``` -**Output:** -``` -ok - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.1s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix store ping --store \"ssh://root@100.102.113.26\"", - "description": "Ping remote Nix store over SSH" -} -``` -**Output:** -``` -warning: 'ping' is a deprecated alias for 'info' -Store URL: ssh://root@100.102.113.26 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 35.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix build --impure .#devShells.aarch64-darwin.default -L", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 30000, - "description": "Check if devshell build itself stalls" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 30000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 96.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure --option builders '' --option fallback true -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 90000, - "description": "Test develop without remote builder configuration" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 90000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix eval --raw --impure --expr '\"ok\"'", - "description": "Test basic nix daemon responsiveness" -} -``` -**Output:** -``` -ok -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 28.4s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "pgrep -af \"nix develop|bash --rcfile /tmp/nix-shell|nix-daemon __build-remote\"", - "description": "List hanging nix develop and nix-shell processes" -} -``` -**Output:** -``` -12984 -22953 -45950 -59070 -75186 -85568 -90006 -99002 - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 12984,22953,45950,59070,75186,85568,90006,99002 -o pid,ppid,stat,etime,command", - "description": "Show details for lingering nix-related process IDs" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND -90006 85629 Ss 08:25 nix-daemon __build-remote 6 -12984 72659 S 02-21:38:19 bash --rcfile /tmp/nix-shell.ifHEBl -85568 5179 S+ 11:09 nix develop --verbose --debug --log-format bar-with-logs -L -22953 23851 S 10:06:45 bash --rcfile /tmp/nix-shell.RkSZvV -59070 22953 S+ 08:00:40 bash --rcfile /tmp/nix-shell.dci1FV/nix-shell.YCMR9O -75186 52085 S+ 01:08:59 bash --rcfile /tmp/nix-shell.5v7q3p -45950 11608 S 01-01:52:27 bash --rcfile /tmp/nix-shell.n5blbc -99002 90569 S 07:43:19 bash --rcfile /tmp/nix-shell.qZDJVz - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.0s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "kill 85568 90006", - "description": "Terminate stuck nix develop and remote build helper" -} -``` -**Output:** -``` -zsh:kill:1: kill 90006 failed: operation not permitted - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "ps -p 85568,90006 -o pid,ppid,stat,etime,command", - "description": "Check whether targeted stuck processes remain" -} -``` -**Output:** -``` - PID PPID STAT ELAPSED COMMAND - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 64.5s) - -_Thinking:_ - - - -``` -Tool: bash - -**Input:** -```json -{ - "command": "nix develop --impure -c true", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Retest develop after killing stuck process" -} -``` -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -building '/nix/store/p67312d0vwhswgdzf38d8ffa12ky2pyf-stack-0.1.0.drv'... -building '/nix/store/26h81d1lqmd7w1qznqjmmd1n0ql7nzrh-check-files-drift.drv'... -building '/nix/store/qrd6rbkpvy6dpf4hzz9pwpwayzqs4hkr-write-files.drv'... -building '/nix/store/mq0vqf71y6cngd22sdqaqqrfz0prdv2k-check-files.drv'... -building '/nix/store/1spcb854akafmycs84cqyvnjcix3a0vl-write-files.drv'... -building '/nix/store/0hzvw2kw65bjj3mvc6xm22jagpj3l6kb-stack-scripts.drv'... -error: interrupted by the user - - - -bash tool terminated command after exceeding timeout 60000 ms - -``` -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.9s) - -_Thinking:_ - - - -Yep — I found the likely cause. - -`nix develop` wasn’t truly deadlocked in eval; it was blocked behind an already-running stuck `nix develop`/remote build worker. - -I found these long-lived processes on your machine: - -- `nix develop --verbose --debug --log-format bar-with-logs -L` (running ~11 min) -- `nix-daemon __build-remote` worker tied to it -- several old `bash --rcfile /tmp/nix-shell...` sessions - -After terminating the stuck `nix develop` process, a fresh `nix develop --impure -c true` immediately started progressing again (building derivations), instead of sitting at daemon op logs. - -**What to do now** -1. In your terminal, kill stale `nix develop` jobs: - - `pkill -f "nix develop --verbose --debug --log-format bar-with-logs -L"` - - or manually: `ps -axo pid,etime,command | rg "nix develop|nix-shell"` then `kill ` -2. If it still stalls, restart Determinate Nix daemon: - - `sudo launchctl kickstart -k system/systems.determinate.nix-daemon` -3. Retry: - - `nix develop --impure` - -If you want, I can also give you a small `bin/nix-unstick` script for this repo to automate those checks/cleanup. - ---- - -## User - -how do i setup machines in the studio UI? lets also make sure the docs site is updated and has clear, detailed docs for this deploy feature - ---- - -## Assistant (Build · claude-opus-4-6 · 174.7s) - -_Thinking:_ - -The user is asking about two things: -1. How to set up machines in the studio UI (likely referring to Fly.io machines or some deployment feature) -2. Making sure the docs site has clear, detailed documentation for the deploy feature - -Let me first understand what "machines" means in this context - it's likely related to the Fly.io deployment module that was throwing "Unsupported host fly" earlier. Let me explore the codebase to understand the deployment/machines setup in the studio UI and the docs. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore deployment/machines UI and docs", - "prompt": "Search the codebase thoroughly for:\n\n1. How \"machines\" or deployment targets are configured in the studio UI (apps/web). Look for components related to deployment, machines, infra setup, infrastructure-step, etc.\n2. The deployment module in nix/stack/infra/modules/deployment/ - understand what hosts/frameworks are supported and how apps get registered\n3. The current docs content for deployment/infra in apps/docs/content/docs/ - find all relevant deployment and infra docs\n4. How the infra panel works in the studio UI (apps/web/src/components/studio/panels/infra-panel.tsx, setup steps, etc.)\n5. The .stack/config.nix to understand how deployment is currently configured\n\nReturn:\n- File paths and key line numbers for all deployment-related UI components\n- File paths for all deployment docs\n- The current deployment config from .stack/config.nix (the deployment/infra section)\n- What hosts/frameworks the deployment module supports\n- How the studio UI handles infrastructure/deployment setup", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have a thorough understanding of the entire deployment/infra system. Here is my complete report. - ---- - -## Comprehensive Deployment & Infrastructure Search Results - -### 1. Deployment-Related UI Components (apps/web) - -#### Route Files -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/deploy.tsx` | L1-11 | Route at `/studio/deploy`, renders `DeployPanel` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/routes/studio/infra.tsx` | L1-11 | Route at `/studio/infra`, renders `InfraPanel` | - -#### Deploy Panel (Colmena-based NixOS Deployment) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx` | L1-547 | **Primary deploy panel**. Colmena-centric deployment management. Shows machine inventory cards, app-to-machine mapping, and deploy actions (eval/build/apply). Tabs: Machines, App Targets, Actions, Settings. | -| ^^ | L48-65 | `MachineInfo` type: id, name, host, ssh, tags, roles, provider, arch, publicIp, privateIp, targetEnv, labels | -| ^^ | L67-74 | `AppDeployMapping` type: enable, targets, resolvedTargets, role, nixosModules, system | -| ^^ | L89-125 | `useColmenaData()` hook: reads `nixConfig.serializable.colmena` for machine inventory and iterates `apps` for deploy-enabled entries | -| ^^ | L131-201 | `MachineCard` component: renders individual machine with SSH info, architecture, provider, tags, roles | -| ^^ | L250-546 | `DeployPanel` main component: status overview (machines/reachable/unreachable/deploy-enabled apps), tabbed machine list, app target rows, Colmena action buttons (eval/build/apply), settings display | - -#### Infra Panel (SST/AWS Infrastructure) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra-panel.tsx` | L1-808 | **Infrastructure panel**. AWS infrastructure provisioning with SST. Tabs: Status, Deploy, Outputs, Resources, Configure | -| ^^ | L50-248 | `InfraPanel` main component: status cards (Enabled/Deployed/KMS), tabbed layout | -| ^^ | L257-363 | `StatusTabContent`: shows SST project name, region, OIDC provider, IAM role, KMS key, config path | -| ^^ | L366-451 | `DeployTabContent`: stage selector (dev/staging/production), CommandRunner for `sst deploy` and `sst remove` | -| ^^ | L453-496 | `OutputsTabContent`: displays deployed stack outputs (ARNs, etc.) | -| ^^ | L498-540 | `ResourcesTabContent`: lists deployed AWS resources by type/URN | -| ^^ | L542-807 | `ConfigureTabContent`: full SST config form -- enable toggle, project name, region, account ID, config path, KMS settings, OIDC provider (GitHub Actions/Fly.io/Roles Anywhere), IAM role name | - -#### Infra Shared Components -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/index.ts` | L1-9 | Barrel export for infra module | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/types.ts` | L1-54 | Type definitions: `SSTData`, `SSTStatus`, `SSTResource` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/constants.ts` | L1-73 | Constants: `OIDC_PROVIDERS`, `AWS_REGIONS`, `DEFAULT_SST_DATA`, `mergeWithDefaults()` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/components.tsx` | L1-114 | Shared UI: `StatusCard`, `ResourceRow`, `OutputRow` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` | L1-327 | `useSSTConfig()` hook: manages SST form state, reads `useNixData("sst")`, calls agent for status/outputs/resources, handles deploy/remove via `agentClient.deploySSTInfra()` | - -#### Setup Wizard Infrastructure Step -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/setup/steps/infrastructure-step.tsx` | L1-340 | **Setup wizard step** for AWS infrastructure. Guided config for IAM Role, KMS Key, SST config. Toggle enable, AWS region/account, OIDC provider (GitHub Actions specific fields), KMS key alias. Links to infra panel for deployment. | - -#### Per-App Deployment Tab -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/app-expanded-content.tsx` | L52 | Nav item `{ id: "deployment", label: "Deployment", icon: Rocket }` | -| ^^ | L85-86 | Props: `deploymentPanels?: AppModulePanel[]` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps-panel-alt.tsx` | L88-101 | Filters module panels: deployment panels are `module === "deployment-fly"` or `module === "deployment-cloudflare"`, routed to app Deployment tab | - -#### Sidebar Navigation -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-sidebar.tsx` | L91-92 | Panel IDs include `"infra"` and `"deploy"` | -| ^^ | L122 | Main nav: `{ id: "deploy", label: "Deploy", icon: Rocket }` | -| ^^ | L129 | Tools nav: `{ id: "infra", label: "Infrastructure", icon: Cloud }` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/dashboard-header.tsx` | L44 | Label mapping: `infra: "Infrastructure"` | -| `/Users/cm/git/darkmatter/stack/apps/web/src/components/landing/infrastructure-section.tsx` | -- | Landing page infrastructure section | - ---- - -### 2. Deployment Module in Nix (nix/stack/) - -#### Top-Level Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/default.nix` | L1-39 | **Aggregator** -- imports `./fly` and `./cloudflare` | - -#### Infra Modules Deployment (Alchemy-Based) -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix` | L1-157 | **Infra deployment module** -- reads `framework` x `deployment.host` from each app and creates Alchemy resources | - -**Supported Framework x Host Matrix** (from lines 12-19): - -| Framework | Host | Alchemy Resource | -|---|---|---| -| `tanstack-start` | `cloudflare` | `TanStackStart` | -| `nextjs` | `cloudflare` | `Nextjs` | -| `vite` | `cloudflare` | `cloudflare.Vite` | -| `hono` | `cloudflare` | `cloudflare.Worker` | -| `astro` | `cloudflare` | `Astro` | -| `remix` | `cloudflare` | `Remix` (Worker-based) | -| `nuxt` | `cloudflare` | (listed in frameworkNames, line 51) | -| `*` | `fly` | Fly container (separate module) | -| `*` | `vercel` | (planned, checked on line 80) | -| `*` | `aws` | (planned, checked on line 81) | - -**How apps get registered** (lines 64-69): `deployableApps` = apps where `deployment.enable == true` AND a framework is detected AND `deployment.host != null`. The module auto-enables `stack.infra.enable` and registers itself as `stack.infra.modules.deployment` with pure-data inputs per app (framework, host, path, bindings, secrets + framework-specific options). - -#### Fly.io Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix` | L1-603 | **Full Fly.io deployment module**. Container-based deployment using nix2container. Generates fly.toml, deploy scripts, wrapped `fly-` CLI commands, turbo tasks, health checks | -| ^^ | L59-135 | Per-app options added via `deploymentAppModule`: fly.appName, fly.region, fly.memory, fly.cpus, fly.cpuKind (shared/performance), fly.autoStop (off/stop/suspend), fly.autoStart, fly.minMachines, fly.forceHttps, fly.env; container.type (bun/node/go/static/custom), container.port, container.entrypoint | -| ^^ | L380-415 | **Global options**: `stack.deployment.defaultHost` (enum: cloudflare/fly/vercel/aws), `stack.deployment.fly.organization`, `stack.deployment.fly.defaultRegion` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/meta.nix` | L1-64 | Metadata: id=`deployment-fly`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/schema.nix` | -- | SpField schema definitions for Fly options | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/container.nix` | -- | Container configuration | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/reference.nix` | -- | Reference docs | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix` | -- | UI panel definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/lib/fly-oidc.nix` | -- | Fly.io OIDC integration | - -#### Cloudflare Deployment Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/module.nix` | L1-110 | **Cloudflare Workers per-app options**. Adds `deployment.cloudflare.*` to each app via appModules: workerName, route, compatibility (node/browser), kvNamespaces, d1Databases, r2Buckets. Global options: accountId, compatibilityDate, defaultRoute | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/meta.nix` | L1-43 | Metadata: id=`deployment-cloudflare`, category=`deployment` | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/schema.nix` | -- | SpField schema definitions | -| `/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix` | -- | UI panel definitions | - -#### Machine Inventory Module -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` | L1-307 | **Machine inventory** for Colmena deployments. Sources: `static` or `aws-ec2`. Machine type: id, name, host, ssh config, tags, roles, provider, arch, publicIp, privateIp, labels, nixosProfile, nixosModules, targetEnv, env, metadata. AWS EC2 discovery via filters, tag-based role/tag/env resolution, host preference order | - -#### Infrastructure Module System -| File | Key Lines | Purpose | -|---|---|---| -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` | L1-38 | **Aggregator** -- imports options.nix, codegen.nix, and all 11 infra sub-modules | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix` | L1-335 | **Core options**: enable, framework (alchemy only), output-dir, key-format, storage-backend (chamber/sops/ssm/none), modules registry (infraModuleType), package config, outputs stub | -| `/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix` | L1-968 | **Code generation engine**: generates `src/index.ts` (Infra class), `src/types.ts`, custom alchemy resources (KMS, IAM, EC2, ALB, etc.), `alchemy.run.ts` orchestrator, `package.json`, `tsconfig.json`, `infra-inputs.json`. Registers scripts: `infra:deploy`, `infra:destroy`, `infra:dev`, `infra:pull-outputs`. Registers as module `infra` in category `infrastructure` | - -All 11 infra sub-modules: -- `aws-secrets` -- KMS + IAM roles -- `deployment` -- App hosting via Alchemy -- `database` -- Neon Postgres / devenv / Docker -- `cache` -- Upstash Redis / devenv / Docker -- `machines` -- Machine inventory (static/AWS EC2) -- `aws-network` -- VPC, subnets -- `aws-security-groups` -- Security groups -- `aws-key-pairs` -- SSH key pairs -- `aws-iam` -- IAM roles/policies -- `aws-ec2` -- EC2 instances -- `aws-ec2-app` -- Per-app EC2 resources (ALB, target groups, etc.) - ---- - -### 3. Deployment Documentation - -#### Public Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx` | Deployment overview | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx` | Fly.io deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx` | Cloudflare deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx` | Container deployment guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx` | SST infrastructure guide | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json` | Section metadata | - -#### Internal/Reference Docs -| File | Topic | -|---|---| -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment.mdx` | Deployment module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra.mdx` | Infra module system reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/options.mdx` | Infra options reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/codegen.mdx` | Infra codegen reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/templates.mdx` | Infra templates reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/deployment/module.mdx` | Deployment infra module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-secrets/module.mdx` | AWS secrets module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/database/module.mdx` | Database module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/cache/module.mdx` | Cache module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/machines/module.mdx` | Machines module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/infra/modules/aws-network/module.mdx` | AWS network module | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/module.mdx` | Fly module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/container.mdx` | Fly container reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/reference.mdx` | Fly reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/schema.mdx` | Fly schema reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/fly/lib/fly-oidc.mdx` | Fly OIDC reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/module.mdx` | Cloudflare module reference | -| `/Users/cm/git/darkmatter/stack/apps/docs/content/docs/internal/deployment/cloudflare/schema.mdx` | Cloudflare schema reference | - ---- - -### 4. Current Deployment Config from .stack/config.nix - -From `/Users/cm/git/darkmatter/stack/.stack/config.nix`: - -**Deployment section** (lines 218-223): -```nix -deployment = { - fly = { - organization = "darkmatter"; - }; -}; -``` - -**Per-app deployment** -- `web` app (lines 76-96): -```nix -web = { - deployment = { - bindings = [ "DATABASE_URL" "CORS_ORIGIN" "BETTER_AUTH_SECRET" "BETTER_AUTH_URL" "POLAR_ACCESS_TOKEN" "POLAR_SUCCESS_URL" ]; - enable = true; - host = "fly"; - secrets = [ "DATABASE_URL" "BETTER_AUTH_SECRET" "POLAR_ACCESS_TOKEN" ]; - fly = { - appName = "stack-web"; - region = "iad"; - }; - }; - framework = { tanstack-start = { enable = true; }; }; - container = { enable = true; type = "bun"; }; -}; -``` - -**Infra section** (lines 447-475): -```nix -infra = { - enable = true; - storage-backend = { - type = "sops"; - sops.group = "dev"; - }; - database = { - enable = true; - provider = "neon"; - neon = { region = "aws-us-east-1"; api-key-ssm-path = "/common/neon-api-key"; }; - }; - machines = { - enable = true; - source = "aws-ec2"; - aws = { - region = "us-west-2"; - filters = [{ name = "instance-state-name"; values = [ "running" ]; }]; - }; - machines = {}; - }; -}; -``` - -**SST section** (lines 480-509): -```nix -sst = { - account-id = "950224716579"; - config-path = "packages/infra/sst.config.ts"; - enable = true; - iam = { role-name = "stack-secrets-role"; }; - kms = { alias = "stack-secrets"; deletion-window-days = 30; enable = true; }; - oidc = { - flyio = { app-name = "*"; org-id = ""; }; - github-actions = { branch = "*"; org = "darkmatter"; repo = "stack"; }; - provider = "github-actions"; - roles-anywhere = { trust-anchor-arn = ""; }; - }; - project-name = "stack"; - region = "us-west-2"; -}; -``` - ---- - -### 5. How the Studio UI Handles Infrastructure/Deployment Setup - -The studio UI has **two separate panels** for deployment, plus a setup wizard step: - -**A. Deploy Panel (`/studio/deploy`)** -- Colmena-based NixOS deployment to machines: -- Reads machine inventory from `nixConfig.colmena.machinesComputed` and app deploy configs from `nixConfig.apps.*.deploy` -- Displays a **machine inventory grid** (MachineCard components showing hostname, SSH, arch, provider, tags, roles) -- Shows **app-to-machine mapping** (which apps deploy to which machines, resolved by targets/roles) -- Provides **Colmena actions**: eval, build, apply buttons -- Displays **settings** for Colmena config (machine source, hive config path, etc.) -- Data flow: Agent evaluates Nix config -> serializes colmena data -> SSE events -> React queries `useNixConfig()` - -**B. Infra Panel (`/studio/infra`)** -- SST/AWS infrastructure provisioning: -- Uses `useSSTConfig()` hook that reads from `useNixData("sst")` for persistent config and calls agent REST endpoints (`getSSTStatus`, `getSSTOutputs`, `getSSTResources`, `deploySSTInfra`, `removeSSTInfra`) -- **Status tab**: shows project overview, IAM role, KMS key, config path status -- **Deploy tab**: stage selector (dev/staging/production) + CommandRunner that executes `sst deploy --stage ` or `sst remove --stage ` via the agent -- **Outputs tab**: displays stack outputs (ARNs, URLs) with copy-to-clipboard -- **Resources tab**: lists deployed AWS resources by type/URN -- **Configure tab**: full SST config editor (enable, project name, region, account ID, KMS settings, OIDC provider, IAM role). Links to the Setup wizard for initial configuration. - -**C. Setup Wizard Infrastructure Step** (`infrastructure-step.tsx`): -- Part of the Project Setup wizard at `/studio/setup` -- Guided step for initial AWS infrastructure config -- Shows a table of what gets deployed (IAM Role, KMS Key, SST Config) -- Form: enable toggle, AWS region/account, OIDC provider (GitHub Actions with org/repo), KMS key toggle + alias -- Saves to `.stack/data/sst.nix` via the agent - -**D. Per-App Deployment Tab** (within the Apps panel): -- Each expanded app has a "Deployment" tab (nav item at line 52 of app-expanded-content.tsx) -- Deployment panels are filtered by module: `deployment-fly` and `deployment-cloudflare` panels are separated from generic module panels (apps-panel-alt.tsx lines 88-101) -- These panels render the per-app deployment config form via `AppConfigFormRenderer` using the schema defined in the Nix module's `ui.nix` - - -session_id: ses_35680e048ffea12FLMy7pJZVXe - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and infra panel UI code to understand machine setup flow", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update/create comprehensive deployment docs covering: machines setup, deploy panel, infra panel, studio UI workflows", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Verify docs build without errors", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.6s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers & Deployment -00003| description: Build containers and deploy your applications to production -00004| icon: cloud -00005| --- -00006| -00007| import { Cards, Card } from "fumadocs-ui/components/card"; -00008| import { Callout } from "fumadocs-ui/components/callout"; -00009| -00010| Stack supports building containers and deploying to multiple platforms—all configured from the same Nix source of truth as your dev environment. Container definitions, deployment targets, and infrastructure provisioning are declared alongside your services and apps, so everything stays in sync. -00011| -00012| ## The Deployment Story -00013| -00014| Stack's approach to deployment follows the same philosophy as everything else: **declare it once, generate the artifacts your tools expect.** -00015| -00016| - Need a `Dockerfile`? Stack generates one optimized for your stack. -00017| - Need a Cloudflare Workers config? Declared in Nix, generated as standard config files. -00018| - Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up. -00019| -00020| The generated files are standard—`Dockerfile`, `fly.toml`, `sst.config.ts`—committed to Git and usable without Nix. Your CI pipeline reads them as normal config. If you eject from Stack, the files stay. -00021| -00022| ## What's Available -00023| -00024| -00025| -00026| Build OCI-compatible container images using Nix or Dockerfile generation -00027| -00028| -00029| Deploy to Cloudflare Workers and Pages -00030| -00031| -00032| Deploy to Fly.io with generated fly.toml and Dockerfile -00033| -00034| -00035| AWS infrastructure provisioning with KMS, IAM, and OIDC authentication -00036| -00037| -00038| -00039| ## Quick Example -00040| -00041| A typical deployment setup might combine containers with a deployment target: -00042| -00043| ```nix -00044| { -00045| # Define your apps -00046| stack.apps.api = { -00047| port = 1; -00048| root = "./apps/api"; -00049| build = "bun run build"; -00050| }; -00051| -00052| # Build a container for the API -00053| stack.containers.api = { -00054| enable = true; -00055| app = "api"; -00056| registry = "ghcr.io/my-org/myapp-api"; -00057| }; -00058| -00059| # Deploy to Fly.io -00060| stack.deployment.fly = { -00061| enable = true; -00062| apps.api = { -00063| region = "iad"; -00064| vm-size = "shared-cpu-1x"; -00065| }; -00066| }; -00067| } -00068| ``` -00069| -00070| From this, Stack generates a `Dockerfile` for the API, a `fly.toml` with the correct configuration, and optionally CI steps that build and deploy on merge to `main`. -00071| -00072| ## How Deployment Connects to CI -00073| -00074| Deployment configuration feeds into [CI generation](/docs/apps-ci/ci-generation). When you define a deployment target and enable the CI extension, the generated GitHub Actions workflow includes the appropriate deploy steps: -00075| -00076| ``` -00077| stack.apps.api.container -00078| → Generates container image -00079| → CI workflow gets docker build + push steps -00080| -00081| stack.apps.api.deployment.host = "fly" -00082| → Generates fly.toml -00083| → CI workflow gets flyctl deploy step -00084| -00085| stack.apps.api.deployment.host = "cloudflare" -00086| → Generates wrangler config -00087| → CI workflow gets cloudflare deploy step -00088| ``` -00089| -00090| Each deployment platform adds its own steps to the CI pipeline. Multiple platforms can coexist—deploy your frontend to Cloudflare and your API to Fly.io from the same workflow. -00091| -00092| -00093| All deployment configuration generates standard files that work without Nix. Your CI runners don't need Nix installed—they use Docker, flyctl, wrangler, or sst directly. -00094| -00095| -00096| ## Container Strategies -00097| -00098| Stack supports two approaches to building containers: -00099| -00100| | Strategy | How It Works | Best For | -00101| |----------|-------------|----------| -00102| | **Dockerfile generation** | Generates a standard `Dockerfile` optimized for your stack | Most projects, standard CI/CD pipelines | -00103| | **Nix-based containers** | Uses `nix2container` to build minimal OCI images directly from Nix | Minimal image size, reproducible builds | -00104| -00105| The Dockerfile approach is more portable—any Docker-compatible CI can build it. The Nix approach produces smaller, more reproducible images but requires Nix at build time. See [Containers](./containers) for details on both strategies. -00106| -00107| ## Platform-Specific Notes -00108| -00109| ### Cloudflare -00110| -00111| Cloudflare Workers and Pages deployments are lightweight—no containers involved. Stack generates the `wrangler.toml` configuration and can produce the correct build output format for your framework (static export for Pages, Worker bundles for Workers). -00112| -00113| ### Fly.io -00114| -00115| Fly.io deployments use containers. Stack generates both the `Dockerfile` and `fly.toml`, with the correct ports, health checks, and environment variables wired up from your Stack config. -00116| -00117| ### SST (AWS) -00118| -00119| SST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. -00120| -00121| ## Reference -00122| -00123| - [Options Reference → CI](/docs/reference/ci) for CI generation options -00124| - [Apps & CI](/docs/apps-ci) for how app definitions drive deployment -00125| - [Core Concepts → Building Source](/docs/concepts/building-source) for why deployment config is generated -00126| - [Core Concepts → File Generation](/docs/concepts/file-generation) for how generated files work - -(End of file - total 126 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/fly.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Fly.io -00003| description: Deploy applications to Fly.io with generated configuration and OIDC authentication -00004| icon: plane -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can generate Fly.io deployment configuration from your project definition—`fly.toml` files, Dockerfiles, and OIDC-based authentication for passwordless deploys from CI. Instead of hand-maintaining Fly config that drifts from your actual app setup, the deployment extension derives everything from your Stack config. -00010| -00011| ## Enable Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Fly extension" - just configure your apps with `deployment.host = "fly"`. -00014| -00015| Stack will automatically add the `flyctl` CLI to your devshell when you have apps configured for Fly.io deployment. -00016| -00017| ## Defining Fly Apps -00018| -00019| Each app that should be deployed to Fly needs deployment configuration: -00020| -00021| ```nix -00022| stack.apps = { -00023| api = { -00024| port = 1; -00025| root = "./apps/api"; -00026| build = "bun run build"; -00027| -00028| deployment = { -00029| enable = true; -00030| host = "fly"; -00031| -00032| fly = { -00033| appName = "myapp-api"; -00034| region = "iad"; -00035| memory = "512mb"; -00036| cpus = 1; -00037| forceHttps = true; -00038| }; -00039| }; -00040| }; -00041| -00042| web = { -00043| port = 0; -00044| root = "./apps/web"; -00045| build = "bun run build"; -00046| -00047| deployment = { -00048| enable = true; -00049| host = "fly"; -00050| -00051| fly = { -00052| appName = "myapp-web"; -00053| region = "iad"; -00054| }; -00055| }; -00056| }; -00057| }; -00058| ``` -00059| -00060| Stack generates deployment configuration for each app, wiring up the correct ports, health checks, and resource settings. -00061| -00062| ## Generated Files -00063| -00064| When the Fly extension is enabled, Stack generates: -00065| -00066| | File | Description | -00067| |------|-------------| -00068| | `apps//fly.toml` | Fly.io app configuration | -00069| | `apps//Dockerfile` | Container image (if not already defined) | -00070| -00071| These are standard Fly.io files—`flyctl` reads them directly. Like all Stack-generated files, they're committed to Git so that deploys work without Nix. -00072| -00073| ```nix -00074| # The extension generates fly.toml from your config -00075| stack.files.entries."apps/api/fly.toml" = { -00076| # ... generated automatically from deployment.fly settings -00077| }; -00078| ``` -00079| -00080| -00081| You can inspect the generated `fly.toml` at any time. It's a standard file that `flyctl` understands—no proprietary format or wrapper. -00082| -00083| -00084| ## Container Builds -00085| -00086| Fly.io deploys container images. Configure containers alongside your app deployment: -00087| -00088| ```nix -00089| stack.apps.api = { -00090| port = 1; -00091| root = "./apps/api"; -00092| build = "bun run build"; -00093| -00094| deployment = { -00095| enable = true; -00096| host = "fly"; -00097| -00098| fly = { -00099| appName = "myapp-api"; -00100| region = "iad"; -00101| }; -00102| }; -00103| -00104| container = { -00105| enable = true; -00106| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00107| }; -00108| }; -00109| ``` -00110| -00111| Or define containers separately using the [containers module](/docs/deployment/containers) with matching names. -00112| -00113| ## OIDC Authentication -00114| -00115| For CI/CD deployments, configure OIDC authentication at the global deployment level: -00116| -00117| ```nix -00118| stack.deployment = { -00119| fly = { -00120| organization = "my-fly-org"; -00121| }; -00122| }; -00123| ``` -00124| -00125| When configured, the [CI generation](/docs/apps-ci/ci-generation) can produce GitHub Actions steps that authenticate with Fly using GitHub's OIDC token. This means: -00126| -00127| - No `FLY_API_TOKEN` secret to manage -00128| - Tokens are short-lived and scoped to each CI run -00129| - Authentication is tied to your GitHub org/repo identity -00130| - No token rotation or expiration to worry about -00131| -00132| ```yaml -00133| # Generated CI step (simplified) -00134| - name: Deploy to Fly -00135| uses: superfly/flyctl-actions/setup-flyctl@master -00136| - run: flyctl deploy --remote-only -00137| env: -00138| FLY_API_TOKEN: ${{ steps.fly-oidc.outputs.token }} -00139| ``` -00140| -00141| -00142| OIDC authentication requires your Fly.io organization to have OIDC configured with your GitHub repository. See the [Fly.io OIDC docs](https://fly.io/docs/reference/openid-connect/) for setup instructions. -00143| -00144| -00145| ## Multi-Region Deployment -00146| -00147| Deploy to multiple Fly.io regions for lower latency: -00148| -00149| ```nix -00150| stack.apps.api.deployment.fly = { -00151| appName = "myapp-api"; -00152| region = "iad"; -00153| -00154| # Note: Multi-region configuration is managed through Fly.io directly -00155| # Set your primary region here and use flyctl to scale to additional regions -00156| }; -00157| ``` -00158| -00159| After deploying to your primary region, use `flyctl scale` to add machines in additional regions. -00160| -00161| ## Environment and Secrets -00162| -00163| Configure environment variables and secrets for your Fly.io deployments: -00164| -00165| ```nix -00166| stack.apps.api.deployment.fly = { -00167| appName = "myapp-api"; -00168| -00169| # These get set as Fly secrets -00170| secrets = [ -00171| "DATABASE_URL" -00172| "API_KEY" -00173| "STRIPE_SECRET_KEY" -00174| ]; -00175| -00176| # Static environment variables (non-secret) -00177| env = { -00178| NODE_ENV = "production"; -00179| LOG_LEVEL = "info"; -00180| }; -00181| }; -00182| ``` -00183| -00184| Environment variables are included in deployment configuration. Secrets are managed through Stack's [secrets system](/docs/secrets/variables) and synced to Fly.io. -00185| -00186| ## Deployment Scripts -00187| -00188| The Fly extension registers scripts for common deployment tasks: -00189| -00190| ```bash -00191| # Deploy a specific app -00192| fly:deploy api -00193| -00194| # Deploy all Fly-configured apps -00195| fly:deploy-all -00196| -00197| # Check deployment status -00198| fly:status -00199| -00200| # Open the Fly dashboard -00201| fly:dashboard -00202| ``` -00203| -00204| These are standard Stack [scripts](/docs/dev-environment/packages-scripts) that wrap `flyctl` with the correct app names and configuration. -00205| -00206| ## Databases on Fly -00207| -00208| For managed databases, use Fly's CLI to create Postgres instances, then reference them in your app: -00209| -00210| ```bash -00211| # Create a Postgres cluster -00212| flyctl postgres create --name myapp-db --region iad -00213| -00214| # Attach it to your app -00215| flyctl postgres attach myapp-db --app myapp-api -00216| ``` -00217| -00218| Then reference the database in your deployment config: -00219| -00220| ```nix -00221| stack.apps.api.deployment.fly = { -00222| appName = "myapp-api"; -00223| -00224| secrets = [ -00225| "DATABASE_URL" # Automatically set by Fly when attached -00226| ]; -00227| }; -00228| ``` -00229| -00230| -00231| For local development, use Stack's built-in [PostgreSQL service](/docs/services/postgres) instead of Fly Postgres. -00232| -00233| -00234| ## Combining with Other Deployment Targets -00235| -00236| You can deploy different apps in your monorepo to different platforms: -00237| -00238| ```nix -00239| stack.apps = { -00240| web = { -00241| port = 0; -00242| # Deploy the frontend to Cloudflare -00243| deployment = { -00244| enable = true; -00245| host = "cloudflare"; -00246| }; -00247| }; -00248| -00249| api = { -00250| port = 1; -00251| # Deploy the API to Fly.io -00252| deployment = { -00253| enable = true; -00254| host = "fly"; -00255| fly.appName = "myapp-api"; -00256| }; -00257| }; -00258| }; -00259| ``` -00260| -00261| Each deployment target generates its own configuration independently. The [CI generation](/docs/apps-ci/ci-generation) produces workflow steps for all enabled deployment targets. -00262| -00263| ## CLI Commands -00264| -00265| When you have apps configured for Fly.io deployment, `flyctl` is automatically added to your devshell: -00266| -00267| ```bash -00268| # Direct flyctl access -00269| flyctl status --app myapp-api -00270| flyctl logs --app myapp-api -00271| flyctl ssh console --app myapp-api -00272| -00273| # Build containers for deployment -00274| nix build .#packages.x86_64-linux.container-api -00275| ``` -00276| -00277| ## Troubleshooting -00278| -00279| ### "App not found" errors -00280| -00281| Make sure the Fly app exists. On first deployment, you may need to create it: -00282| -00283| ```bash -00284| flyctl apps create myapp-api --org my-org -00285| ``` -00286| -00287| Or let `flyctl deploy` create it automatically on the first run. -00288| -00289| ### Build failures in CI -00290| -00291| Check that the generated Dockerfile is correct: -00292| -00293| ```bash -00294| cat apps/api/Dockerfile -00295| ``` -00296| -00297| If the auto-generated Dockerfile doesn't work for your app, you can provide a custom one and point the extension at it. -00298| -00299| ### OIDC token errors -00300| -00301| Verify that your Fly.io organization has OIDC configured for your GitHub repository. The OIDC subject must match what Fly expects—typically `repo:/:ref:refs/heads/main` or similar. -00302| -00303| ```bash -00304| # Test OIDC locally (requires flyctl >= 0.1.130) -00305| flyctl auth token --oidc -00306| ``` -00307| -00308| ### Secrets not available in the deployed app -00309| -00310| Fly secrets are set separately from `fly.toml` environment variables. Make sure secrets are actually set: -00311| -00312| ```bash -00313| flyctl secrets list --app myapp-api -00314| ``` -00315| -00316| If a secret is missing, set it manually or re-run the deployment script that syncs Stack variables to Fly secrets. -00317| -00318| ## Reference -00319| -00320| - [Options Reference → Deployment](/docs/reference) for Fly deployment options -00321| - [Containers](/docs/deployment/containers) for container image configuration -00322| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00323| - [Fly.io documentation](https://fly.io/docs/) for platform-specific details -00324| - [Fly.io OIDC](https://fly.io/docs/reference/openid-connect/) for passwordless CI authentication - -(End of file - total 324 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/cloudflare.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Cloudflare -00003| description: Deploy applications to Cloudflare Workers and Pages -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack includes a Cloudflare deployment module that generates the configuration needed to deploy your applications to [Cloudflare Workers](https://workers.cloudflare.com/) and [Cloudflare Pages](https://pages.cloudflare.com/). Like everything in Stack, the deployment config is derived from your project's source of truth—no hand-maintained `wrangler.toml` files that drift out of sync. -00010| -00011| ## Enable Cloudflare Deployment -00012| -00013| Deployment configuration is per-app. You don't need a global "Cloudflare extension" - just configure your apps with `deployment.host = "cloudflare"`. -00014| -00015| Stack will automatically add the `wrangler` CLI to your devshell when you have apps configured for Cloudflare deployment. -00016| -00017| ## Configuration -00018| -00019| ### Global Settings -00020| -00021| Set your Cloudflare account ID at the global deployment level: -00022| -00023| ```nix -00024| stack.deployment = { -00025| cloudflare = { -00026| accountId = "your-cloudflare-account-id"; -00027| compatibilityDate = "2024-01-01"; -00028| }; -00029| }; -00030| ``` -00031| -00032| ### Per-App Deployment -00033| -00034| Configure each app with Cloudflare-specific deployment settings: -00035| -00036| ```nix -00037| stack.apps.web = { -00038| port = 0; -00039| framework = "tanstack-start"; -00040| root = "./apps/web"; -00041| build = "bun run build"; -00042| -00043| deployment = { -00044| enable = true; -00045| host = "cloudflare"; -00046| -00047| cloudflare = { -00048| workerName = "myapp-web"; -00049| type = "CLOUDFLARE_WORKER_TYPE_VITE"; -00050| route = "myapp.com/*"; -00051| bindings = { -00052| DATABASE_URL = "..."; -00053| }; -00054| }; -00055| }; -00056| }; -00057| ``` -00058| -00059| ### Environment Variables -00060| -00061| Pass environment variables to your Workers at deploy time: -00062| -00063| ```nix -00064| stack.apps.api = { -00065| port = 1; -00066| root = "./apps/api"; -00067| -00068| deployment = { -00069| enable = true; -00070| host = "cloudflare"; -00071| -00072| cloudflare = { -00073| workerName = "myapp-api"; -00074| bindings = { -00075| APP_ENV = "production"; -00076| API_VERSION = "v1"; -00077| }; -00078| }; -00079| }; -00080| }; -00081| ``` -00082| -00083| For sensitive values, use Cloudflare's secret bindings instead of plain vars. You can set these through the Wrangler CLI: -00084| -00085| ```bash -00086| wrangler secret put API_KEY --name myapp-api -00087| ``` -00088| -00089| -00090| Never put sensitive values in `vars`—they're visible in your generated config and committed to Git. Use Wrangler secrets or Stack's [secrets system](/docs/secrets) for anything confidential. -00091| -00092| -00093| ## Generated Files -00094| -00095| The Cloudflare module generates standard Wrangler configuration files: -00096| -00097| ``` -00098| wrangler.toml # or per-app: apps/web/wrangler.toml -00099| ``` -00100| -00101| These are normal Wrangler config files that the `wrangler` CLI reads directly. If you eject from Stack, the files stay and work as-is. -00102| -00103| Like all Stack-generated files, they're committed to Git so that team members and CI can deploy without Nix. -00104| -00105| ## Scripts -00106| -00107| When Cloudflare deployment is enabled, the following scripts are registered: -00108| -00109| | Script | Description | -00110| |--------|-------------| -00111| | `cloudflare:deploy` | Deploy all configured Workers | -00112| | `cloudflare:deploy:` | Deploy a specific Worker | -00113| | `cloudflare:dev` | Start Wrangler dev mode | -00114| | `cloudflare:tail` | Tail Worker logs in real time | -00115| -00116| ```bash -00117| # Deploy everything -00118| cloudflare:deploy -00119| -00120| # Deploy just the API -00121| cloudflare:deploy:api -00122| -00123| # Start local dev with Wrangler -00124| cloudflare:dev -00125| -00126| # Tail production logs -00127| cloudflare:tail -00128| ``` -00129| -00130| ## CI Integration -00131| -00132| When you have apps configured for Cloudflare deployment and [CI generation](/docs/apps-ci/ci-generation) is enabled, the generated GitHub Actions workflow includes Cloudflare deploy steps: -00133| -00134| ```nix -00135| { -00136| stack.deployment = { -00137| cloudflare = { -00138| accountId = "your-account-id"; -00139| }; -00140| }; -00141| -00142| stack.apps.api = { -00143| port = 1; -00144| deployment = { -00145| enable = true; -00146| host = "cloudflare"; -00147| cloudflare.workerName = "myapp-api"; -00148| }; -00149| }; -00150| -00151| stack.ci = { -00152| enable = true; -00153| secrets = { -00154| "CLOUDFLARE_API_TOKEN" = "CF_API_TOKEN"; # GitHub secret name -00155| }; -00156| }; -00157| } -00158| ``` -00159| -00160| The generated workflow uses `wrangler deploy` with proper authentication via the `CLOUDFLARE_API_TOKEN` GitHub Actions secret. -00161| -00162| -00163| You'll need to create a Cloudflare API token with the appropriate permissions and add it as a GitHub Actions secret. See the [Cloudflare docs](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) for token creation. -00164| -00165| -00166| ## Workers with Bindings -00167| -00168| Cloudflare Workers support various bindings—KV namespaces, R2 buckets, D1 databases, Durable Objects, and more. Configure them through the module: -00169| -00170| ```nix -00171| stack.apps.api = { -00172| port = 1; -00173| -00174| deployment = { -00175| enable = true; -00176| host = "cloudflare"; -00177| -00178| cloudflare = { -00179| workerName = "myapp-api"; -00180| kvNamespaces = [ "CACHE" ]; -00181| r2Buckets = [ "UPLOADS" ]; -00182| d1Databases = [ "DB" ]; -00183| bindings = { -00184| CACHE = "abc123"; -00185| DB = "def456"; -00186| }; -00187| }; -00188| }; -00189| }; -00190| ``` -00191| -00192| These bindings are written into the generated `wrangler.toml` and available as typed bindings in your Worker code. -00193| -00194| ## Pages Deployment -00195| -00196| For static sites or frameworks with static export (like Next.js static export or Astro), use Cloudflare Pages: -00197| -00198| ```nix -00199| stack.apps.docs = { -00200| port = 2; -00201| -00202| deployment = { -00203| enable = true; -00204| host = "cloudflare"; -00205| -00206| cloudflare = { -00207| workerName = "myapp-docs"; -00208| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00209| }; -00210| }; -00211| }; -00212| ``` -00213| -00214| Pages deployments use `wrangler pages deploy` under the hood. -00215| -00216| ## Local Development with Wrangler -00217| -00218| The Cloudflare module adds `wrangler` to your devshell, so you can use Wrangler's local development features alongside Stack's dev environment: -00219| -00220| ```bash -00221| # Start Wrangler dev server (uses miniflare locally) -00222| wrangler dev --config apps/api/wrangler.toml -00223| -00224| # Or use the registered script -00225| cloudflare:dev -00226| ``` -00227| -00228| Wrangler's dev mode emulates the Workers runtime locally, including KV, R2, D1, and other bindings. This complements Stack's [process-compose](/docs/services/process-compose) setup—you can run Wrangler alongside your other local services. -00229| -00230| ## Combining with Other Deployment Targets -00231| -00232| Not every app in your project needs to deploy to Cloudflare. You can mix deployment targets: -00233| -00234| ```nix -00235| { -00236| stack.apps = { -00237| web = { -00238| port = 0; -00239| framework = "nextjs"; -00240| deployment = { -00241| enable = true; -00242| host = "fly"; -00243| fly.appName = "myapp-web"; -00244| }; -00245| }; -00246| -00247| api = { -00248| port = 1; -00249| framework = "hono"; -00250| deployment = { -00251| enable = true; -00252| host = "cloudflare"; -00253| cloudflare.workerName = "myapp-api"; -00254| }; -00255| }; -00256| -00257| docs = { -00258| port = 2; -00259| framework = "nextjs"; -00260| deployment = { -00261| enable = true; -00262| host = "cloudflare"; -00263| cloudflare = { -00264| workerName = "myapp-docs"; -00265| type = "CLOUDFLARE_WORKER_TYPE_PAGES"; -00266| }; -00267| }; -00268| }; -00269| }; -00270| } -00271| ``` -00272| -00273| Each deployment target generates its own config files and scripts independently. -00274| -00275| ## Troubleshooting -00276| -00277| ### "Authentication error" during deploy -00278| -00279| Make sure your Cloudflare API token is set. For local deploys: -00280| -00281| ```bash -00282| export CLOUDFLARE_API_TOKEN="your-token" -00283| cloudflare:deploy -00284| ``` -00285| -00286| Or log in interactively: -00287| -00288| ```bash -00289| wrangler login -00290| ``` -00291| -00292| ### "Worker not found" errors -00293| -00294| Verify the Worker name matches what's in your Cloudflare dashboard. Workers are created on first deploy if they don't exist, but the account ID must be correct. -00295| -00296| ### Generated `wrangler.toml` not updating -00297| -00298| If your Stack config changed but the generated file hasn't, trigger a rebuild: -00299| -00300| ```bash -00301| # The agent watches for changes, but you can force a rebuild -00302| stack status -00303| ``` -00304| -00305| Or check the file status in [Studio](/docs/studio) to see if it's marked as stale. -00306| -00307| ## Reference -00308| -00309| - [Options Reference → CI](/docs/reference/ci) for CI configuration that includes Cloudflare deploys -00310| - [Apps & CI → CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00311| - [Fly.io](./fly) for an alternative deployment target -00312| - [SST](./sst) for AWS infrastructure provisioning -00313| - [Cloudflare Workers docs](https://developers.cloudflare.com/workers/) for Workers-specific configuration - -(End of file - total 313 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/sst.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: SST -00003| description: AWS infrastructure provisioning with SST and OIDC-based authentication -00004| icon: cloud -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| [SST](https://sst.dev/) is an infrastructure-as-code framework for deploying to AWS. Stack's SST extension generates configuration, manages IAM roles with OIDC authentication, and provisions KMS keys for secrets encryption—all from your Nix config. -00010| -00011| ## Enable SST -00012| -00013| ```nix -00014| stack.sst = { -00015| enable = true; -00016| project-name = "myapp"; -00017| region = "us-west-2"; -00018| }; -00019| ``` -00020| -00021| When enabled, the SST extension: -00022| -00023| - Adds the SST CLI to your `PATH` -00024| - Generates an `sst.config.ts` from your Nix configuration -00025| - Registers `sst:deploy`, `sst:dev`, and other scripts in your devshell -00026| - Optionally provisions KMS keys and IAM roles for secure deployments -00027| -00028| ## Configuration -00029| -00030| ### Basic Setup -00031| -00032| ```nix -00033| stack.sst = { -00034| enable = true; -00035| project-name = "myapp"; -00036| region = "us-west-2"; -00037| }; -00038| ``` -00039| -00040| This generates a minimal `sst.config.ts` that you can extend with your own infrastructure definitions. -00041| -00042| ### KMS for Secrets Encryption -00043| -00044| SST can provision a KMS key for encrypting secrets at rest: -00045| -00046| ```nix -00047| stack.sst = { -00048| enable = true; -00049| project-name = "myapp"; -00050| region = "us-west-2"; -00051| kms.enable = true; -00052| }; -00053| ``` -00054| -00055| The KMS key integrates with Stack's [secrets system](/docs/secrets)—secrets encrypted with this key can be decrypted in your deployed application using the IAM role's KMS permissions. -00056| -00057| ### OIDC Authentication -00058| -00059| Instead of long-lived AWS credentials, the SST extension supports OIDC-based authentication. This gives you short-lived, automatically rotated credentials for CI/CD and local development. -00060| -00061| #### GitHub Actions OIDC -00062| -00063| ```nix -00064| stack.sst = { -00065| enable = true; -00066| project-name = "myapp"; -00067| region = "us-west-2"; -00068| -00069| oidc = { -00070| provider = "github-actions"; -00071| github-actions = { -00072| org = "my-org"; -00073| repo = "my-repo"; -00074| }; -00075| }; -00076| }; -00077| ``` -00078| -00079| This provisions: -00080| -00081| - An IAM OIDC identity provider for GitHub Actions -00082| - An IAM role that GitHub Actions can assume via OIDC -00083| - Trust policies scoped to your specific org and repo -00084| -00085| Your [CI workflows](/docs/apps-ci/ci-generation) automatically get the correct role ARN and OIDC configuration—no manual secret management. -00086| -00087| #### Fly.io OIDC -00088| -00089| ```nix -00090| stack.sst = { -00091| enable = true; -00092| project-name = "myapp"; -00093| region = "us-west-2"; -00094| -00095| oidc = { -00096| provider = "fly"; -00097| fly = { -00098| org = "my-fly-org"; -00099| }; -00100| }; -00101| }; -00102| ``` -00103| -00104| #### AWS Roles Anywhere -00105| -00106| For environments that don't support OIDC natively, use [AWS Roles Anywhere](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html) with mTLS certificates from [Step CA](/docs/networking/certificates): -00107| -00108| ```nix -00109| stack.sst = { -00110| enable = true; -00111| project-name = "myapp"; -00112| region = "us-west-2"; -00113| -00114| oidc = { -00115| provider = "roles-anywhere"; -00116| }; -00117| }; -00118| -00119| stack.aws.roles-anywhere = { -00120| enable = true; -00121| region = "us-west-2"; -00122| account-id = "123456789012"; -00123| role-name = "dev-role"; -00124| trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:trust-anchor/abc123"; -00125| profile-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:profile/def456"; -00126| }; -00127| ``` -00128| -00129| This provisions certificate-based AWS authentication through your team's [Step CA](/docs/networking/certificates) instance, giving every developer short-lived AWS credentials without any shared secrets. -00130| -00131| -00132| Roles Anywhere is the most secure option for local development—each developer gets their own short-lived certificate, and all access is logged in CloudTrail. It requires a [Step CA](/docs/networking/certificates) instance but provides the strongest security guarantees. -00133| -00134| -00135| ## Generated Files -00136| -00137| The SST extension generates: -00138| -00139| | File | Purpose | -00140| |------|---------| -00141| | `sst.config.ts` | SST project configuration with app name, region, and settings | -00142| -00143| Like all Stack-generated files, `sst.config.ts` is committed to Git and works without Nix. If you eject from Stack, you have a standard SST project. -00144| -00145| -00146| Don't edit `sst.config.ts` by hand—your changes will be overwritten on the next build. Add custom infrastructure in separate files that SST imports, or configure everything through the Nix options. -00147| -00148| -00149| ## Scripts -00150| -00151| When SST is enabled, the following scripts are available in your devshell: -00152| -00153| | Script | Description | -00154| |--------|-------------| -00155| | `sst:dev` | Start SST in development mode with live Lambda reloading | -00156| | `sst:deploy` | Deploy infrastructure to AWS | -00157| | `sst:remove` | Remove all deployed resources | -00158| -00159| These scripts handle credential injection automatically—whether you're using OIDC, Roles Anywhere, or environment-based credentials. -00160| -00161| ## Using with CI -00162| -00163| The SST extension integrates with [CI generation](/docs/apps-ci/ci-generation). When both are enabled, the generated GitHub Actions workflow includes: -00164| -00165| - AWS OIDC credential setup (if GitHub Actions OIDC is configured) -00166| - SST deploy steps with the correct region and project name -00167| - Environment variable injection for secrets -00168| -00169| ```nix -00170| { -00171| stack.sst = { -00172| enable = true; -00173| project-name = "myapp"; -00174| region = "us-west-2"; -00175| oidc.provider = "github-actions"; -00176| oidc.github-actions = { -00177| org = "my-org"; -00178| repo = "my-repo"; -00179| }; -00180| }; -00181| -00182| stack.ci.enable = true; -00183| } -00184| ``` -00185| -00186| This generates a CI pipeline that authenticates to AWS via OIDC and deploys your SST infrastructure—no long-lived AWS credentials stored in GitHub Secrets. -00187| -00188| ## Full Example -00189| -00190| A complete SST setup with KMS, OIDC, and secrets: -00191| -00192| ```nix -00193| { -00194| # SST infrastructure -00195| stack.sst = { -00196| enable = true; -00197| project-name = "myapp"; -00198| region = "us-west-2"; -00199| -00200| # KMS key for secrets encryption -00201| kms.enable = true; -00202| -00203| # GitHub Actions OIDC for CI deployments -00204| oidc = { -00205| provider = "github-actions"; -00206| github-actions = { -00207| org = "my-org"; -00208| repo = "my-repo"; -00209| }; -00210| }; -00211| }; -00212| -00213| # Local AWS access via Roles Anywhere -00214| stack.aws.roles-anywhere = { -00215| enable = true; -00216| region = "us-west-2"; -00217| account-id = "123456789012"; -00218| role-name = "dev-role"; -00219| trust-anchor-arn = "arn:aws:rolesanywhere:..."; -00220| profile-arn = "arn:aws:rolesanywhere:..."; -00221| }; -00222| -00223| # Secrets encrypted with KMS -00224| stack.variables."/prod/api-key" = { -00225| key = "API_KEY"; -00226| type = "SECRET"; -00227| master-keys = [ "prod" ]; -00228| }; -00229| -00230| # CI pipeline with SST deploy -00231| stack.ci.enable = true; -00232| } -00233| ``` -00234| -00235| ## Troubleshooting -00236| -00237| ### "Unable to assume role" in CI -00238| -00239| Verify your OIDC configuration matches your GitHub repository: -00240| -00241| ```nix -00242| oidc.github-actions = { -00243| org = "my-org"; # Must match your GitHub org exactly -00244| repo = "my-repo"; # Must match your GitHub repo name exactly -00245| }; -00246| ``` -00247| -00248| Also confirm the IAM OIDC provider and role have been provisioned. The SST extension creates these resources on first deploy—you may need to run `sst deploy` locally with admin credentials once to bootstrap. -00249| -00250| ### "KMS key not found" -00251| -00252| If KMS is enabled but not yet provisioned, run the initial deployment: -00253| -00254| ```bash -00255| sst:deploy -00256| ``` -00257| -00258| The KMS key is created as part of the SST infrastructure stack. After the first deploy, it's available for encrypting secrets. -00259| -00260| ### SST dev mode not connecting -00261| -00262| Ensure your AWS credentials are working: -00263| -00264| ```bash -00265| aws sts get-caller-identity -00266| ``` -00267| -00268| If using Roles Anywhere, verify your [Step CA](/docs/networking/certificates) certificate is valid and the trust anchor is configured correctly. -00269| -00270| ## Reference -00271| -00272| - [Options Reference → AWS](/docs/reference/aws) for AWS Roles Anywhere configuration -00273| - [Secrets](/docs/secrets) for encrypted secrets management -00274| - [CI Generation](/docs/apps-ci/ci-generation) for automated deployment pipelines -00275| - [Certificates](/docs/networking/certificates) for Step CA and Roles Anywhere -00276| - [SST documentation](https://sst.dev/docs) for SST-specific configuration - -(End of file - total 276 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/containers.mdx" -} -``` -**Output:** -``` - -00001| --- -00002| title: Containers -00003| description: Build OCI containers from your Nix configuration -00004| icon: box -00005| --- -00006| -00007| import { Callout } from "fumadocs-ui/components/callout"; -00008| -00009| Stack can build OCI-compatible container images directly from your Nix configuration—no Dockerfile required. Images are built with [nix2container](https://github.com/nlewo/nix2container), producing minimal, reproducible containers with only the dependencies your application actually needs. -00010| -00011| ## Why Nix-Built Containers? -00012| -00013| Traditional Dockerfiles are imperative scripts that install packages, copy files, and run commands. The result depends on the base image version, network availability during build, and the order of operations. Two builds of the same Dockerfile can produce different images. -00014| -00015| Nix-built containers are **declarative and reproducible**. You describe what goes in the image, and Nix computes the exact result. The same input always produces the same image, byte-for-byte. -00016| -00017| Additional benefits: -00018| -00019| - **Minimal images** — Only your application and its runtime dependencies. No package manager, no shell, no leftover build tools. -00020| - **Layer caching** — Nix automatically splits your image into layers based on the dependency graph. Dependencies that don't change produce identical layers, giving you efficient caching without manual multi-stage builds. -00021| - **No base image** — Images are built from scratch. No inherited vulnerabilities from a base image you don't control. -00022| - **Shared with your devshell** — The same Nix packages used in your dev environment end up in your container. No "works locally but not in Docker" surprises. -00023| -00024| ## Enable Containers -00025| -00026| Container definitions live under `stack.containers`: -00027| -00028| ```nix -00029| stack.containers.api = { -00030| enable = true; -00031| name = "myapp-api"; -00032| tag = "latest"; -00033| -00034| # What goes in the image -00035| config = { -00036| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00037| env = { -00038| NODE_ENV = "production"; -00039| }; -00040| exposedPorts = { -00041| "4201/tcp" = {}; -00042| }; -00043| }; -00044| -00045| # Files to include -00046| copyToRoot = [ -00047| ./apps/api/dist -00048| ./apps/api/package.json -00049| ]; -00050| }; -00051| ``` -00052| -00053| ## Building Images -00054| -00055| Build a container image from the command line: -00056| -00057| ```bash -00058| # Build a specific container -00059| nix build --impure .#packages.x86_64-linux.container-api -00060| -00061| # Load it into Docker -00062| docker load < result -00063| -00064| # Or build and load in one step -00065| nix build --impure .#packages.x86_64-linux.container-api && docker load < result -00066| ``` -00067| -00068| The output is a standard OCI image that works with Docker, Podman, containerd, or any OCI-compatible runtime. -00069| -00070| ## Per-App Containers -00071| -00072| When you define [apps](/docs/apps-ci/defining-apps), you can attach container definitions directly: -00073| -00074| ```nix -00075| stack.apps.api = { -00076| port = 1; -00077| root = "./apps/api"; -00078| build = "bun run build"; -00079| }; -00080| -00081| stack.containers.api = { -00082| enable = true; -00083| name = "ghcr.io/my-org/myapp-api"; -00084| -00085| config = { -00086| entrypoint = [ "${pkgs.bun}/bin/bun" "run" "dist/index.js" ]; -00087| env = { -00088| NODE_ENV = "production"; -00089| PORT = "4201"; -00090| }; -00091| exposedPorts = { -00092| "4201/tcp" = {}; -00093| }; -00094| }; -00095| }; -00096| ``` -00097| -00098| ## Multi-Stage Patterns -00099| -00100| Nix replaces the need for multi-stage Docker builds. Instead of copying between build and runtime stages, you define separate derivations: -00101| -00102| ```nix -00103| { pkgs, ... }: -00104| let -00105| # Build stage — produces the compiled output -00106| apiBundle = pkgs.stdenv.mkDerivation { -00107| name = "api-bundle"; -00108| src = ./apps/api; -00109| buildInputs = [ pkgs.bun ]; -00110| buildPhase = '' -00111| bun install --frozen-lockfile -00112| bun run build -00113| ''; -00114| installPhase = '' -00115| mkdir -p $out -00116| cp -r dist $out/ -00117| cp package.json $out/ -00118| ''; -00119| }; -00120| in -00121| { -00122| stack.containers.api = { -00123| enable = true; -00124| name = "ghcr.io/my-org/myapp-api"; -00125| -00126| # Only the built output and runtime go in the image -00127| # Build tools (bun, node headers, etc.) are NOT included -00128| copyToRoot = [ apiBundle ]; -00129| -00130| config = { -00131| entrypoint = [ "${pkgs.nodejs}/bin/node" "dist/index.js" ]; -00132| workingDir = "/"; -00133| env.NODE_ENV = "production"; -00134| }; -00135| }; -00136| } -00137| ``` -00138| -00139| The resulting image contains only Node.js and your compiled code—no build tools, no source files, no `node_modules` with devDependencies. -00140| -00141| ## Image Layers -00142| -00143| nix2container automatically creates efficient image layers based on the Nix store dependency graph. Each Nix store path becomes a potential layer boundary, so: -00144| -00145| - **Runtime dependencies** (Node.js, system libraries) rarely change → cached layers -00146| - **Application code** changes frequently → small top layer -00147| - **No manual layer optimization** — Nix computes the optimal layer split -00148| -00149| This gives you Docker-like layer caching without writing `COPY package.json` tricks or managing multi-stage builds. -00150| -00151| ## Registry Push -00152| -00153| Push built images to a container registry: -00154| -00155| ```bash -00156| # Build the container -00157| nix build --impure .#packages.x86_64-linux.container-api -00158| docker load < result -00159| -00160| # Push to registry -00161| docker tag myapp-api:latest ghcr.io/my-org/myapp-api:latest -00162| docker push ghcr.io/my-org/myapp-api:latest -00163| -00164| # Or use skopeo for direct copy -00165| nix run nixpkgs#skopeo -- copy \ -00166| docker-archive:./result \ -00167| docker://ghcr.io/my-org/myapp-api:latest -00168| ``` -00169| -00170| For CI, the [CI generation](/docs/apps-ci/ci-generation) system can produce Docker build and push steps in your GitHub Actions workflow automatically. -00171| -00172| ## Configuration Reference -00173| -00174| Each container supports these options: -00175| -00176| | Option | Type | Description | -00177| |--------|------|-------------| -00178| | `enable` | `bool` | Whether to build this container | -00179| | `name` | `string` | Image name (including registry prefix) | -00180| | `tag` | `string` | Image tag (default: `"latest"`) | -00181| | `config.entrypoint` | `list` | Container entrypoint command | -00182| | `config.cmd` | `list` | Default command arguments | -00183| | `config.env` | `attrset` | Environment variables baked into the image | -00184| | `config.exposedPorts` | `attrset` | Ports to expose | -00185| | `config.workingDir` | `string` | Working directory inside the container | -00186| | `config.user` | `string` | User to run as | -00187| | `copyToRoot` | `list` | Files and derivations to include in the image | -00188| | `layers` | `list` | Explicit layer definitions (advanced) | -00189| | `maxLayers` | `int` | Maximum number of layers (default: 127) | -00190| -00191| ## Comparing to Dockerfiles -00192| -00193| If you're already using Dockerfiles, you can continue to do so. Stack's container system is an alternative, not a replacement. Here's when each makes sense: -00194| -00195| | Use Nix containers when... | Use Dockerfiles when... | -00196| |---------------------------|------------------------| -00197| | You want reproducible builds | You need specific base images (e.g., CUDA) | -00198| | You want minimal image size | Your team is more comfortable with Docker | -00199| | You're already using Nix for your devshell | You need Docker-specific features (BuildKit secrets, etc.) | -00200| | You want shared dependencies with your dev environment | You're deploying to a platform that expects Dockerfiles | -00201| -00202| -00203| Stack can also generate Dockerfiles through the [Docker module](/docs/deployment/index), giving you a middle ground: Nix-generated Dockerfiles that are standard files on disk. -00204| -00205| -00206| ## Troubleshooting -00207| -00208| ### Image is larger than expected -00209| -00210| Check what's being pulled in as dependencies: -00211| -00212| ```bash -00213| nix path-info -rsSh .#packages.x86_64-linux.container-api -00214| ``` -00215| -00216| This shows every store path in the image closure and its size. Look for unexpected dependencies—a common issue is accidentally including build-time tools in the runtime closure. -00217| -00218| ### "No such file or directory" at runtime -00219| -00220| The container's filesystem only contains what you explicitly include via `copyToRoot` and what Nix determines as runtime dependencies. If a file is missing, add it to `copyToRoot` or ensure the derivation that produces it is in the dependency graph. -00221| -00222| ### Can't run shell commands in the container -00223| -00224| By default, Nix-built containers don't include a shell (`bash`, `sh`). If you need one for debugging: -00225| -00226| ```nix -00227| copyToRoot = [ -00228| apiBundle -00229| pkgs.bashInteractive # Adds bash for debugging -00230| pkgs.coreutils # Adds ls, cat, etc. -00231| ]; -00232| ``` -00233| -00234| Remove these before deploying to production to keep the image minimal. -00235| -00236| ## Reference -00237| -00238| - [Deployment Overview](/docs/deployment) for other deployment options -00239| - [Apps → Defining Apps](/docs/apps-ci/defining-apps) for the app definitions containers can reference -00240| - [CI Generation](/docs/apps-ci/ci-generation) for automated container builds in CI -00241| - [nix2container](https://github.com/nlewo/nix2container) for the underlying container builder - -(End of file - total 241 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json" -} -``` -**Output:** -``` - -00001| { -00002| "title": "Containers & Deployment", -00003| "description": "Build containers and deploy to production", -00004| "icon": "cloud-upload", -00005| "pages": ["index", "containers", "cloudflare", "fly", "sst"] -00006| } -00007| - -(End of file - total 7 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "in_progress", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 5.7s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix" -} -``` -**Output:** -``` - -00001| # ============================================================================== -00002| # infra/modules/machines/module.nix -00003| # -00004| # Machine inventory infra module. -00005| # -00006| # Accepts a machine inventory definition in Nix and emits a JSON string -00007| # output that can be stored via the infra output backend. Colmena consumes -00008| # this inventory from stack.infra.outputs.machines. -00009| # ============================================================================== -00010| { -00011| lib, -00012| config, -00013| ... -00014| }: -00015| let -00016| cfg = config.stack.infra.machines; -00017| -00018| sshConfigType = lib.types.submodule { -00019| options = { -00020| user = lib.mkOption { -00021| type = lib.types.str; -00022| default = "root"; -00023| description = "SSH user for connecting to the machine."; -00024| }; -00025| -00026| port = lib.mkOption { -00027| type = lib.types.int; -00028| default = 22; -00029| description = "SSH port for connecting to the machine."; -00030| }; -00031| -00032| keyPath = lib.mkOption { -00033| type = lib.types.nullOr lib.types.str; -00034| default = null; -00035| description = "Path to the SSH private key for this machine."; -00036| }; -00037| }; -00038| }; -00039| -00040| awsFilterType = lib.types.submodule { -00041| options = { -00042| name = lib.mkOption { -00043| type = lib.types.str; -00044| description = "EC2 filter name (e.g., instance-state-name, tag:Name)."; -00045| }; -00046| -00047| values = lib.mkOption { -00048| type = lib.types.listOf lib.types.str; -00049| default = [ ]; -00050| description = "Values for the EC2 filter."; -00051| }; -00052| }; -00053| }; -00054| -00055| machineType = lib.types.submodule { -00056| options = { -00057| id = lib.mkOption { -00058| type = lib.types.nullOr lib.types.str; -00059| default = null; -00060| description = "Optional machine identifier (defaults to the attrset key)."; -00061| }; -00062| -00063| name = lib.mkOption { -00064| type = lib.types.nullOr lib.types.str; -00065| default = null; -00066| description = "Human-friendly machine name."; -00067| }; -00068| -00069| host = lib.mkOption { -00070| type = lib.types.nullOr lib.types.str; -00071| default = null; -00072| description = "SSH host or hostname for the machine."; -00073| }; -00074| -00075| ssh = lib.mkOption { -00076| type = sshConfigType; -00077| default = { }; -00078| description = "SSH connection settings for the machine."; -00079| }; -00080| -00081| tags = lib.mkOption { -00082| type = lib.types.listOf lib.types.str; -00083| default = [ ]; -00084| description = "Tags used for grouping and target selection."; -00085| }; -00086| -00087| roles = lib.mkOption { -00088| type = lib.types.listOf lib.types.str; -00089| default = [ ]; -00090| description = "Roles associated with this machine."; -00091| }; -00092| -00093| provider = lib.mkOption { -00094| type = lib.types.nullOr lib.types.str; -00095| default = null; -00096| description = "Infrastructure provider name (aws, gcp, hetzner, etc.)."; -00097| }; -00098| -00099| arch = lib.mkOption { -00100| type = lib.types.nullOr lib.types.str; -00101| default = null; -00102| description = "Target system architecture (e.g., x86_64-linux)."; -00103| }; -00104| -00105| publicIp = lib.mkOption { -00106| type = lib.types.nullOr lib.types.str; -00107| default = null; -00108| description = "Public IPv4/IPv6 address for the machine."; -00109| }; -00110| -00111| privateIp = lib.mkOption { -00112| type = lib.types.nullOr lib.types.str; -00113| default = null; -00114| description = "Private IPv4/IPv6 address for the machine."; -00115| }; -00116| -00117| labels = lib.mkOption { -00118| type = lib.types.attrsOf lib.types.str; -00119| default = { }; -00120| description = "Arbitrary labels attached to the machine."; -00121| }; -00122| -00123| nixosProfile = lib.mkOption { -00124| type = lib.types.nullOr lib.types.str; -00125| default = null; -00126| description = "NixOS profile name to deploy on this machine."; -00127| }; -00128| -00129| nixosModules = lib.mkOption { -00130| type = lib.types.listOf lib.types.str; -00131| default = [ ]; -00132| description = "Extra NixOS modules to include for this machine."; -00133| }; -00134| -00135| targetEnv = lib.mkOption { -00136| type = lib.types.nullOr lib.types.str; -00137| default = null; -00138| description = "Deployment environment label for this machine."; -00139| }; -00140| -00141| env = lib.mkOption { -00142| type = lib.types.attrsOf lib.types.str; -00143| default = { }; -00144| description = "Environment variables applied to this machine."; -00145| }; -00146| -00147| metadata = lib.mkOption { -00148| type = lib.types.attrsOf lib.types.anything; -00149| default = { }; -00150| description = "Extra metadata for downstream tooling."; -00151| }; -00152| }; -00153| }; -00154| in -00155| { -00156| options.stack.infra.machines = { -00157| enable = lib.mkOption { -00158| type = lib.types.bool; -00159| default = false; -00160| description = "Enable machine inventory provisioning via infra."; -00161| }; -00162| -00163| source = lib.mkOption { -00164| type = lib.types.enum [ -00165| "static" -00166| "aws-ec2" -00167| ]; -00168| default = "static"; -00169| description = "Machine inventory source (static or AWS EC2)."; -00170| }; -00171| -00172| aws = { -00173| region = lib.mkOption { -00174| type = lib.types.nullOr lib.types.str; -00175| default = config.stack.aws.roles-anywhere.region or null; -00176| description = "AWS region for EC2 inventory (falls back to AWS env defaults)."; -00177| }; -00178| -00179| instance-ids = lib.mkOption { -00180| type = lib.types.listOf lib.types.str; -00181| default = [ ]; -00182| description = "Explicit EC2 instance IDs to include in inventory."; -00183| }; -00184| -00185| filters = lib.mkOption { -00186| type = lib.types.listOf awsFilterType; -00187| default = [ -00188| { -00189| name = "instance-state-name"; -00190| values = [ "running" ]; -00191| } -00192| ]; -00193| description = "EC2 filters for inventory discovery."; -00194| }; -00195| -00196| name-tag-keys = lib.mkOption { -00197| type = lib.types.listOf lib.types.str; -00198| default = [ "Name" ]; -00199| description = "Tag keys used to derive machine names."; -00200| }; -00201| -00202| role-tag-keys = lib.mkOption { -00203| type = lib.types.listOf lib.types.str; -00204| default = [ -00205| "stack:role" -00206| "role" -00207| ]; -00208| description = "Tag keys used to derive machine roles."; -00209| }; -00210| -00211| tag-keys = lib.mkOption { -00212| type = lib.types.listOf lib.types.str; -00213| default = [ -00214| "stack:tag" -00215| "tag" -00216| ]; -00217| description = "Tag keys used to derive machine tags."; -00218| }; -00219| -00220| env-tag-keys = lib.mkOption { -00221| type = lib.types.listOf lib.types.str; -00222| default = [ -00223| "stack:env" -00224| "env" -00225| "stage" -00226| ]; -00227| description = "Tag keys used to derive machine target environments."; -00228| }; -00229| -00230| host-preference = lib.mkOption { -00231| type = lib.types.listOf (lib.types.enum [ -00232| "publicDns" -00233| "publicIp" -00234| "privateIp" -00235| ]); -00236| default = [ -00237| "publicDns" -00238| "publicIp" -00239| "privateIp" -00240| ]; -00241| description = "Preferred host fields for connecting to EC2 machines."; -00242| }; -00243| -00244| ssh = lib.mkOption { -00245| type = sshConfigType; -00246| default = { }; -00247| description = "Default SSH settings for EC2 machines."; -00248| }; -00249| }; -00250| -00251| machines = lib.mkOption { -00252| type = lib.types.attrsOf machineType; -00253| default = { }; -00254| description = "Machine inventory definitions to emit via infra outputs."; -00255| }; -00256| -00257| sync-outputs = lib.mkOption { -00258| type = lib.types.listOf lib.types.str; -00259| default = [ "machines" ]; -00260| description = "Which outputs to sync to the storage backend."; -00261| }; -00262| }; -00263| -00264| config = lib.mkIf cfg.enable { -00265| stack.infra.enable = lib.mkDefault true; -00266| -00267| stack.infra.modules.machines = { -00268| name = "Machine Inventory"; -00269| description = "Machine inventory provider for Colmena deployments"; -00270| path = ./index.ts; -00271| inputs = { -00272| source = cfg.source; -00273| machines = cfg.machines; -00274| aws = { -00275| region = cfg.aws.region; -00276| instanceIds = cfg.aws.instance-ids; -00277| filters = cfg.aws.filters; -00278| nameTagKeys = cfg.aws.name-tag-keys; -00279| roleTagKeys = cfg.aws.role-tag-keys; -00280| tagKeys = cfg.aws.tag-keys; -00281| envTagKeys = cfg.aws.env-tag-keys; -00282| hostPreference = cfg.aws.host-preference; -00283| ssh = { -00284| user = cfg.aws.ssh.user; -00285| port = cfg.aws.ssh.port; -00286| keyPath = cfg.aws.ssh.keyPath; -00287| }; -00288| }; -00289| }; -00290| dependencies = lib.optionalAttrs (cfg.source == "aws-ec2") { -00291| "@aws-sdk/client-ec2" = "catalog:"; -00292| }; -00293| outputs = -00294| let -00295| mkOutput = key: desc: { -00296| description = desc; -00297| sensitive = false; -00298| sync = builtins.elem key cfg.sync-outputs; -00299| }; -00300| in -00301| { -00302| machines = mkOutput "machines" "Machine inventory (JSON)"; -00303| }; -00304| }; -00305| }; -00306| } -00307| - -(End of file - total 307 lines) - -``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Data comes from the agent via colmena-machines.json -00008| * and colmena-app-deploy.json state files. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, -00017| CardDescription, -00018| CardHeader, -00019| CardTitle, -00020| } from "@ui/card"; -00021| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs"; -00022| import { -00023| Activity, -00024| AlertCircle, -00025| CheckCircle, -00026| CloudOff, -00027| Cpu, -00028| HardDrive, -00029| Loader2, -00030| Network, -00031| Play, -00032| RefreshCw, -00033| Rocket, -00034| Server, -00035| Settings, -00036| Shield, -00037| XCircle, -00038| } from "lucide-react"; -00039| import { useAgentContext } from "@/lib/agent-provider"; -00040| import { useNixConfig } from "@/lib/use-agent"; -00041| import { PanelHeader } from "../shared/panel-header"; -00042| import { cn } from "@/lib/utils"; -00043| -00044| // ============================================================================= -00045| // Types -00046| // ============================================================================= -00047| -00048| interface MachineInfo { -00049| id: string; -00050| name: string; -00051| host: string | null; -00052| ssh: { -00053| user: string; -00054| port: number; -00055| keyPath: string | null; -00056| }; -00057| tags: string[]; -00058| roles: string[]; -00059| provider: string | null; -00060| arch: string | null; -00061| publicIp: string | null; -00062| privateIp: string | null; -00063| targetEnv: string | null; -00064| labels: Record; -00065| } -00066| -00067| interface AppDeployMapping { -00068| enable: boolean; -00069| targets: string[]; -00070| resolvedTargets: string[]; -00071| role: string | null; -00072| nixosModules: string[]; -00073| system: string | null; -00074| } -00075| -00076| interface ColmenaConfig { -00077| enable: boolean; -00078| machineSource: string; -00079| generateHive: boolean; -00080| config: string; -00081| machineCount: number; -00082| machineIds: string[]; -00083| } -00084| -00085| // ============================================================================= -00086| // Hooks -00087| // ============================================================================= -00088| -00089| function useColmenaData() { -00090| const { data: nixConfig, isLoading, refetch } = useNixConfig(); -00091| -00092| const result = useMemo(() => { -00093| const cfg = nixConfig as Record | null | undefined; -00094| if (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null }; -00095| -00096| const serializable = cfg.serializable as Record | undefined; -00097| const colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null; -00098| -00099| // Try to get machines from colmena serialized data or panels -00100| const colmenaData = cfg.colmena as Record | undefined; -00101| const machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record; -00102| -00103| // Apps with deploy config -00104| const rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>; -00105| const appDeploy: Record = {}; -00106| -00107| for (const [appName, appCfg] of Object.entries(rawApps)) { -00108| const deploy = appCfg.deploy as Record | undefined; -00109| if (deploy?.enable) { -00110| appDeploy[appName] = { -00111| enable: true, -00112| targets: (deploy.targets as string[]) ?? [], -00113| resolvedTargets: (deploy.resolvedTargets as string[]) ?? [], -00114| role: (deploy.role as string | null) ?? null, -00115| nixosModules: (deploy.nixosModules as string[]) ?? [], -00116| system: (deploy.system as string | null) ?? null, -00117| }; -00118| } -00119| } -00120| -00121| return { machines: machinesComputed, appDeploy, colmenaConfig }; -00122| }, [nixConfig]); -00123| -00124| return { ...result, isLoading, refetch }; -00125| } -00126| -00127| // ============================================================================= -00128| // Sub-components -00129| // ============================================================================= -00130| -00131| function MachineCard({ machine }: { machine: MachineInfo }) { -00132| const isReachable = machine.host !== null; -00133| -00134| return ( -00135| -00139| -00140|
-00141|
-00142| -00143| {machine.name} -00144|
-00145|
-00146| {isReachable ? ( -00147| -00148| -00149| {machine.host} -00150| -00151| ) : ( -00152| -00153| -00154| no host -00155| -00156| )} -00157|
-00158|
-00159| -00160|
-00161|
-00162| -00163| {machine.ssh.user}@{machine.ssh.port} -00164|
-00165| {machine.arch && ( -00166|
-00167| -00168| {machine.arch} -00169|
-00170| )} -00171| {machine.provider && ( -00172|
-00173| -00174| {machine.provider} -00175|
-00176| )} -00177| {machine.targetEnv && ( -00178|
-00179| -00180| {machine.targetEnv} -00181|
-00182| )} -00183|
-00184| -00185| {(machine.tags.length > 0 || machine.roles.length > 0) && ( -00186|
-00187| {machine.roles.map((role) => ( -00188| -00189| {role} -00190| -00191| ))} -00192| {machine.tags.map((tag) => ( -00193| -00194| {tag} -00195| -00196| ))} -00197|
-00198| )} -00199|
-00200|
-00201| ); -00202| } -00203| -00204| function AppTargetRow({ -00205| appName, -00206| deploy, -00207| machines, -00208| }: { -00209| appName: string; -00210| deploy: AppDeployMapping; -00211| machines: Record; -00212| }) { -00213| const resolvedNames = deploy.resolvedTargets.map( -00214| (id) => machines[id]?.name ?? id, -00215| ); -00216| -00217| return ( -00218|
-00219|
-00220|
-00221| -00222|
-00223|
-00224|

{appName}

-00225|

-00226| {deploy.targets.length > 0 -00227| ? `Targets: ${deploy.targets.join(", ")}` -00228| : "No targets defined"} -00229|

-00230|
-00231|
-00232|
-00233| {deploy.role && ( -00234| -00235| {deploy.role} -00236| -00237| )} -00238| -00239| {resolvedNames.length} machine{resolvedNames.length !== 1 ? "s" : ""} -00240| -00241|
-00242|
-00243| ); -00244| } -00245| -00246| // ============================================================================= -00247| // Main Component -00248| // ============================================================================= -00249| -00250| export function DeployPanel() { -00251| const { isConnected } = useAgentContext(); -00252| const { machines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData(); -00253| const [isRefreshing, setIsRefreshing] = useState(false); -00254| -00255| const machineList = Object.values(machines); -00256| const machineCount = machineList.length; -00257| const appDeployEntries = Object.entries(appDeploy); -00258| const healthyCount = machineList.filter((m) => m.host !== null).length; -00259| const unhealthyCount = machineCount - healthyCount; -00260| -00261| const handleRefresh = async () => { -00262| setIsRefreshing(true); -00263| try { -00264| await refetch(); -00265| } finally { -00266| setIsRefreshing(false); -00267| } -00268| }; -00269| -00270| if (!isConnected) { -00271| return ( -00272|
-00273| -00277| -00278| -00279| -00280|
-00281|

Agent Not Connected

-00282|

-00283| Connect to the stack agent to manage deployments. -00284|

-00285|
-00286|
-00287|
-00288|
-00289| ); -00290| } -00291| -00292| if (isLoading) { -00293| return ( -00294|
-00295| -00296|
-00297| ); -00298| } -00299| -00300| return ( -00301|
-00302| -00312| -00315| {isRefreshing ? "Refreshing..." : "Refresh"} -00316| -00317| } -00318| /> -00319| -00320| {/* Status overview */} -00321|
-00322| -00323| -00324|
-00325| -00326|
-00327|
-00328|

Machines

-00329|

{machineCount}

-00330|
-00331|
-00332|
-00333| -00334| -00335|
0 ? "bg-green-500/10" : "bg-secondary", -00338| )}> -00339| 0 ? "text-green-500" : "text-muted-foreground", -00342| )} /> -00343|
-00344|
-00345|

Reachable

-00346|

{healthyCount}

-00347|
-00348|
-00349|
-00350| -00351| -00352|
0 ? "bg-amber-500/10" : "bg-secondary", -00355| )}> -00356| 0 ? "text-amber-500" : "text-muted-foreground", -00359| )} /> -00360|
-00361|
-00362|

Unreachable

-00363|

{unhealthyCount}

-00364|
-00365|
-00366|
-00367| -00368| -00369|
0 ? "bg-blue-500/10" : "bg-secondary", -00372| )}> -00373| 0 ? "text-blue-500" : "text-muted-foreground", -00376| )} /> -00377|
-00378|
-00379|

Deploy-enabled apps

-00380|

{appDeployEntries.length}

-00381|
-00382|
-00383|
-00384|
-00385| -00386| -00387| -00388| -00389| Machines -00390| {machineCount > 0 && ( -00391| -00392| {machineCount} -00393| -00394| )} -00395| -00396| -00397| App Targets -00398| {appDeployEntries.length > 0 && ( -00399| -00400| {appDeployEntries.length} -00401| -00402| )} -00403| -00404| Actions -00405| Settings -00406| -00407| -00408| {/* Machines Tab */} -00409| -00410| {machineCount === 0 ? ( -00411| -00412| -00413| -00414|
-00415|

No Machines

-00416|

-00417| Machine inventory is empty. Run infra:deploy and infra:pull-outputs to populate it, then reload the shell. -00418|

-00419|
-00420|
-00421|
-00422| ) : ( -00423|
-00424| {machineList.map((machine) => ( -00425| -00426| ))} -00427|
-00428| )} -00429|
-00430| -00431| {/* App Targets Tab */} -00432| -00433| {appDeployEntries.length === 0 ? ( -00434| -00435| -00436| -00437|
-00438|

No App Targets

-00439|

-00440| No apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config. -00441|

-00442|
-00443|
-00444|
-00445| ) : ( -00446|
-00447| {appDeployEntries.map(([appName, deploy]) => ( -00448| -00454| ))} -00455|
-00456| )} -00457|
-00458| -00459| {/* Actions Tab */} -00460| -00461| -00462| -00463| Colmena Actions -00464| -00465| Run Colmena commands against your fleet -00466| -00467| -00468| -00469| {machineCount === 0 ? ( -00470|
-00471|
-00472| -00473|

-00474| No machines in inventory. Provision infrastructure first. -00475|

-00476|
-00477|
-00478| ) : ( -00479| <> -00480|
-00481| -00485| -00489| -00493|
-00494|

-00495| Actions run the generated wrapper scripts with your configured defaults. -00496| Use the CLI for advanced options: colmena-apply --on tag:prod -00497|

-00498| -00499| )} -00500|
-00501|
-00502|
-00503| -00504| {/* Settings Tab */} -00505| -00506| -00507| -00508| Colmena Configuration -00509| -00510| Current Colmena module settings -00511| -00512| -00513| -00514|
-00515|
-00516|

Machine Source

-00517|

-00518| {colmenaConfig?.machineSource ?? "infra"} -00519|

-00520|
-00521|
-00522|

Hive Config

-00523|

-00524| {colmenaConfig?.config ?? ".stack/state/colmena/hive.nix"} -00525|

-00526|
-00527|
-00528|

Generate Hive

-00529|

-00530| {colmenaConfig?.generateHive ? "Yes" : "No"} -00531|

-00532|
-00533|
-00534|

Machine Count

-00535|

-00536| {colmenaConfig?.machineCount ?? machineCount} -00537|

-00538|
-00539|
-00540|
-00541|
-00542|
-00543|
-00544|
-00545| ); -00546| } -00547| - -(End of file - total 547 lines) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -Now I have a thorough understanding of the system. Let me create the docs. - -**How machines work in Studio UI**: The Deploy panel (`/studio/deploy`) reads machine inventory from `nixConfig.serializable.colmena` and `nixConfig.colmena.machinesComputed`. When the Machines tab is empty, it shows guidance to run `infra:deploy` and `infra:pull-outputs`. The machines come from either static Nix definitions or AWS EC2 discovery via the machines infra module. - -Let me create the new docs and update the index. - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "content": "---\ntitle: Machines\ndescription: Define and discover your deployment target machines for NixOS deployments\nicon: server\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel's machine inventory system lets you define the servers your applications deploy to. Machines can be declared statically in Nix or discovered dynamically from AWS EC2. The inventory feeds into Colmena for NixOS deployments and is visible in the Studio UI's Deploy panel.\n\n## How It Works\n\nMachine inventory flows through three layers:\n\n1. **Nix config** -- You declare machines (static) or EC2 discovery rules in `.stack/config.nix`\n2. **Infra module** -- The machines infra module resolves the inventory at deploy time (for EC2, it queries the AWS API)\n3. **Studio UI** -- The Deploy panel reads the resolved inventory and displays machine cards, app-to-machine mappings, and Colmena actions\n\n## Quick Start\n\n\n\n\n### Enable the machines module\n\nAdd to your `.stack/config.nix`:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\"; # or \"aws-ec2\"\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"10.0.1.10\";\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n ssh.user = \"deploy\";\n };\n };\n};\n```\n\n\n\n\n### Deploy infrastructure\n\nRun `infra:deploy` to provision and register the machine inventory:\n\n```bash\ninfra:deploy\n```\n\n\n\n\n### Pull outputs and reload\n\nPull the resolved inventory into your local state and reload the devshell:\n\n```bash\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\n\n\n\n### View in Studio\n\nOpen the Studio UI and navigate to **Deploy**. Your machines appear in the Machines tab with their status, roles, and connection info.\n\n\n\n\n## Static Machines\n\nFor fixed infrastructure (bare metal, pre-provisioned VMs, Hetzner, etc.), declare machines directly:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"static\";\n\n machines = {\n web-1 = {\n name = \"Web Server 1\";\n host = \"web1.example.com\";\n roles = [ \"web\" \"app\" ];\n tags = [ \"production\" \"us-east\" ];\n provider = \"hetzner\";\n arch = \"x86_64-linux\";\n publicIp = \"203.0.113.10\";\n privateIp = \"10.0.1.10\";\n targetEnv = \"production\";\n\n ssh = {\n user = \"deploy\";\n port = 22;\n keyPath = \"~/.ssh/deploy_ed25519\";\n };\n\n labels = {\n team = \"platform\";\n cost-center = \"infra\";\n };\n };\n\n db-1 = {\n name = \"Database Primary\";\n host = \"10.0.2.10\";\n roles = [ \"database\" ];\n tags = [ \"production\" ];\n arch = \"x86_64-linux\";\n targetEnv = \"production\";\n ssh.user = \"root\";\n };\n };\n};\n```\n\n### Machine Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | `string` | Human-friendly display name |\n| `host` | `string` | SSH hostname or IP address |\n| `ssh.user` | `string` | SSH user (default: `root`) |\n| `ssh.port` | `int` | SSH port (default: `22`) |\n| `ssh.keyPath` | `string?` | Path to SSH private key |\n| `roles` | `[string]` | Roles for app-to-machine targeting |\n| `tags` | `[string]` | Tags for grouping and filtering |\n| `provider` | `string?` | Infrastructure provider (aws, hetzner, etc.) |\n| `arch` | `string?` | System architecture (e.g., `x86_64-linux`) |\n| `publicIp` | `string?` | Public IP address |\n| `privateIp` | `string?` | Private IP address |\n| `targetEnv` | `string?` | Environment label (production, staging, etc.) |\n| `labels` | `{string: string}` | Arbitrary key-value metadata |\n| `nixosProfile` | `string?` | NixOS profile to deploy |\n| `nixosModules` | `[string]` | Extra NixOS modules for this machine |\n| `env` | `{string: string}` | Environment variables for this machine |\n| `metadata` | `{string: any}` | Extra metadata for downstream tooling |\n\n## AWS EC2 Discovery\n\nFor dynamic infrastructure, Stack can discover machines from AWS EC2 at deploy time:\n\n```nix\nstackpanel.infra.machines = {\n enable = true;\n source = \"aws-ec2\";\n\n aws = {\n region = \"us-west-2\";\n\n # EC2 filters (same syntax as AWS CLI --filters)\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n };\n};\n```\n\nWhen `infra:deploy` runs, the machines module queries the EC2 API using the specified filters and builds an inventory from the results.\n\n### EC2 Tag Mapping\n\nEC2 instances are mapped to machine properties using configurable tag keys:\n\n| Machine Property | Default Tag Keys | Example Tag Value |\n|-----------------|-----------------|-------------------|\n| `name` | `Name` | `web-server-1` |\n| `roles` | `stack:role`, `role` | `web,app` |\n| `tags` | `stack:tag`, `tag` | `production,us-west` |\n| `targetEnv` | `stack:env`, `env`, `stage` | `production` |\n\nCustomize the tag keys used for mapping:\n\n```nix\nstackpanel.infra.machines.aws = {\n region = \"us-west-2\";\n\n # Which EC2 tags map to machine roles\n role-tag-keys = [ \"stack:role\" \"Role\" ];\n\n # Which EC2 tags map to machine tags\n tag-keys = [ \"stack:tag\" \"Environment\" ];\n\n # Which EC2 tags map to target environment\n env-tag-keys = [ \"stack:env\" \"Stage\" ];\n\n # Which EC2 tags provide the machine name\n name-tag-keys = [ \"Name\" ];\n\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n ];\n};\n```\n\n### Host Resolution\n\nWhen discovering EC2 instances, Stack picks the SSH host using a preference order:\n\n```nix\nstackpanel.infra.machines.aws = {\n # Try public DNS first, then public IP, then private IP\n host-preference = [ \"publicDns\" \"publicIp\" \"privateIp\" ];\n};\n```\n\nFor VPN or private-network setups where you connect to private IPs:\n\n```nix\nstackpanel.infra.machines.aws = {\n host-preference = [ \"privateIp\" ];\n};\n```\n\n### Default SSH Settings for EC2\n\nApply SSH defaults to all discovered EC2 machines:\n\n```nix\nstackpanel.infra.machines.aws = {\n ssh = {\n user = \"ec2-user\";\n port = 22;\n keyPath = \"~/.ssh/aws_deploy\";\n };\n};\n```\n\n### Filtering by Instance IDs\n\nTarget specific instances instead of using filters:\n\n```nix\nstackpanel.infra.machines.aws = {\n instance-ids = [\n \"i-0abc123def456789a\"\n \"i-0def456789abc1230\"\n ];\n};\n```\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:\n\n- Machine name and hostname\n- SSH connection info (user, port)\n- Architecture and provider\n- Roles (colored badges) and tags\n- Reachability status (green check or amber warning)\n\nWhen the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab\n\nShows which apps are configured for deployment and their target machines. Each row displays:\n\n- App name\n- Target machines (resolved from roles or explicit targets)\n- Role badge (if targeting by role)\n- Machine count\n\n### Actions Tab\n\nProvides buttons for Colmena deployment actions:\n\n- **colmena eval** -- Evaluate the NixOS configuration\n- **colmena build** -- Build the NixOS closures\n- **colmena apply** -- Deploy to target machines\n\n\nThe Actions tab is disabled when no machines are in the inventory. Provision infrastructure first with `infra:deploy`.\n\n\n### Settings Tab\n\nDisplays the current Colmena configuration: machine source, hive config path, whether hive generation is enabled, and total machine count.\n\n## Mapping Apps to Machines\n\nAfter machines are in your inventory, configure apps to deploy to them:\n\n```nix\nstackpanel.apps.api = {\n port = 1;\n root = \"./apps/api\";\n\n # Deploy this app to machines with the \"web\" role\n deploy = {\n enable = true;\n role = \"web\";\n };\n};\n\nstackpanel.apps.worker = {\n port = 2;\n root = \"./apps/worker\";\n\n # Deploy to specific machines by name\n deploy = {\n enable = true;\n targets = [ \"worker-1\" \"worker-2\" ];\n };\n};\n```\n\n## End-to-End Example\n\nHere is a complete setup with AWS EC2 discovery, app targeting, and infra provisioning:\n\n```nix\n{\n # Infrastructure provisioning\n stack.infra = {\n enable = true;\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n\n # AWS secrets (IAM role, KMS key)\n aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n };\n\n # Machine discovery\n machines = {\n enable = true;\n source = \"aws-ec2\";\n aws = {\n region = \"us-west-2\";\n filters = [\n { name = \"instance-state-name\"; values = [ \"running\" ]; }\n { name = \"tag:Project\"; values = [ \"myapp\" ]; }\n ];\n ssh.user = \"deploy\";\n };\n };\n };\n\n # Apps with deployment targets\n stack.apps = {\n web = {\n port = 0;\n root = \"./apps/web\";\n deploy = {\n enable = true;\n role = \"web\";\n };\n };\n\n api = {\n port = 1;\n root = \"./apps/api\";\n deploy = {\n enable = true;\n role = \"api\";\n };\n };\n };\n}\n```\n\nThen deploy:\n\n```bash\n# Provision infrastructure and discover machines\ninfra:deploy\n\n# Pull machine inventory into local state\ninfra:pull-outputs\n\n# Reload shell to pick up new inventory\nexit && nix develop --impure\n\n# View machines in Studio\n# Navigate to /studio/deploy\n\n# Deploy with Colmena\ncolmena apply\n```\n\n## Troubleshooting\n\n### \"No Machines\" in the Deploy panel\n\nThe machine inventory is populated by `infra:deploy`. If you see no machines:\n\n1. Run `infra:deploy` to execute the machines infra module\n2. Run `infra:pull-outputs` to sync the inventory to local state\n3. Reload your devshell (`exit && nix develop --impure`)\n4. Refresh the Deploy panel in Studio\n\n### EC2 discovery returns no instances\n\nCheck your filters and region. Test directly with the AWS CLI:\n\n```bash\naws ec2 describe-instances \\\n --region us-west-2 \\\n --filters \"Name=instance-state-name,Values=running\" \"Name=tag:Project,Values=myapp\" \\\n --query 'Reservations[].Instances[].{Id:InstanceId,Name:Tags[?Key==`Name`].Value|[0]}'\n```\n\n### SSH connection failures\n\nVerify SSH connectivity to the resolved host:\n\n```bash\nssh -o ConnectTimeout=5 deploy@ 'echo ok'\n```\n\nCommon issues:\n- Security group doesn't allow SSH from your IP\n- SSH key path is incorrect\n- Using public DNS but instance only has a private IP (adjust `host-preference`)\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Infrastructure](/docs/deployment/infrastructure) for the infra module system\n- [Fly.io](/docs/deployment/fly) for container-based deployment\n- [Cloudflare](/docs/deployment/cloudflare) for edge deployment\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 63.1s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/infrastructure.mdx", - "content": "---\ntitle: Infrastructure\ndescription: Provision cloud infrastructure with Alchemy and the infra module system\nicon: layers\n---\n\nimport { Callout } from \"fumadocs-ui/components/callout\";\nimport { Steps, Step } from \"fumadocs-ui/components/steps\";\n\nStackpanel includes a modular infrastructure provisioning system built on [Alchemy](https://alchemy.run). It provisions AWS resources (IAM roles, KMS keys, OIDC providers), databases (Neon), caches (Upstash), deployment targets (Cloudflare Workers), and machine inventories -- all declared in Nix and deployed with a single command.\n\n## How It Works\n\nThe infra system has three parts:\n\n1. **Nix modules** declare what infrastructure you need (`.stack/config.nix`)\n2. **Code generation** produces TypeScript modules in `packages/infra/` with typed inputs\n3. **Alchemy runtime** provisions the actual cloud resources when you run `infra:deploy`\n\nInfrastructure outputs (ARNs, URLs, keys) are synced back to your storage backend (SOPS, SSM, or Chamber) so other parts of your stack can reference them.\n\n## Quick Start\n\n\n\n\n### Enable infrastructure\n\nAdd to `.stack/config.nix`:\n\n```nix\nstackpanel.infra = {\n enable = true;\n\n # Where to store provisioned outputs\n storage-backend = {\n type = \"sops\";\n sops.group = \"dev\";\n };\n};\n```\n\n\n\n\n### Enable modules\n\nTurn on the infrastructure modules you need:\n\n```nix\n# AWS IAM + KMS for secrets\nstackpanel.infra.aws.secrets = {\n enable = true;\n oidc.provider = \"github-actions\";\n oidc.github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n };\n};\n\n# Database (Neon Postgres)\nstackpanel.infra.database = {\n enable = true;\n provider = \"neon\";\n neon.region = \"aws-us-east-1\";\n};\n```\n\n\n\n\n### Reload and deploy\n\n```bash\n# Reload to pick up config changes\nexit && nix develop --impure\n\n# Deploy all infrastructure\ninfra:deploy\n```\n\n\n\n\n## Available Modules\n\n| Module | What it provisions | Key options |\n|--------|-------------------|-------------|\n| **aws-secrets** | IAM role, OIDC provider, KMS key + alias | `oidc.provider`, `iam.role-name`, `kms.alias` |\n| **deployment** | Cloudflare Workers/Pages per app | Per-app `deployment.host = \"cloudflare\"` |\n| **database** | Neon Postgres or devenv/Docker Postgres | `provider` (neon/devenv/docker) |\n| **cache** | Upstash Redis or devenv/Docker Redis | `provider` (upstash/devenv/docker) |\n| **machines** | Machine inventory (static or AWS EC2) | `source` (static/aws-ec2) |\n\n## Deploying Infrastructure\n\n### Basic Deploy\n\n```bash\n# Deploy all modules\ninfra:deploy\n```\n\nThis runs `alchemy deploy` in the generated `packages/infra/` directory. Alchemy evaluates each module, creates or updates resources, and writes state to its `.alchemy/` directory.\n\n### Adopting Existing Resources\n\nIf your AWS resources already exist (e.g., you created an IAM role manually), use the `--adopt` flag to bring them under Alchemy management without recreating them:\n\n```bash\ninfra:deploy --adopt\n```\n\nThis tells Alchemy to adopt existing resources instead of failing with \"already exists\" errors. The infra modules' custom resources (IAM roles, KMS keys) are built to handle adoption gracefully -- they detect existing resources and update them in place.\n\n\nThe `--adopt` flag is an Alchemy CLI option, not a per-resource setting. However, the Stack infra modules are written to be adopt-safe by default -- IAM roles use get-or-create logic, and KMS keys look up existing aliases before creating new keys.\n\n\n### Stage Selection\n\nDeploy to a specific stage (environment):\n\n```bash\ninfra:deploy --stage production\ninfra:deploy --stage staging\n```\n\nWithout `--stage`, Alchemy defaults to your username as the stage name. This lets each developer have isolated infrastructure for testing.\n\n### Destroying Infrastructure\n\n```bash\n# Remove all provisioned resources\ninfra:destroy\n\n# Remove a specific stage\ninfra:destroy --stage staging\n```\n\n\n`infra:destroy` removes cloud resources permanently. Adopted resources (IAM roles, KMS keys) are not deleted -- they're only unlinked from Alchemy state.\n\n\n## Storage Backends\n\nInfrastructure outputs (ARNs, URLs, connection strings) need to be stored somewhere accessible to your apps and CI. Stack supports three backends:\n\n### SOPS (Recommended for teams)\n\nOutputs are written to a SOPS-encrypted YAML file in your repo:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"sops\";\n sops.group = \"dev\"; # writes to .stack/secrets/vars/dev.sops.yaml\n};\n```\n\nAfter `infra:deploy`, outputs like `aws-secrets-roleArn` appear in the SOPS file. They're decrypted at runtime using your team's AGE keys.\n\n### AWS SSM Parameter Store\n\nOutputs are written to SSM parameters:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"ssm\";\n ssm.prefix = \"/myapp/infra\";\n};\n```\n\n### Chamber\n\nOutputs are written via Chamber:\n\n```nix\nstackpanel.infra.storage-backend = {\n type = \"chamber\";\n chamber.service = \"myapp\";\n};\n```\n\n### None\n\nSkip output sync entirely (outputs are only in Alchemy state):\n\n```nix\nstackpanel.infra.storage-backend.type = \"none\";\n```\n\n## Studio UI: Infrastructure Panel\n\nThe Studio UI's **Infrastructure** panel (`/studio/infra`) provides a visual interface for managing your provisioned infrastructure. It has five tabs:\n\n### Status Tab\n\nShows an overview of your infrastructure configuration:\n- SST project name and region\n- OIDC provider type\n- IAM role name and ARN\n- KMS key status\n- Config file path\n\n### Deploy Tab\n\nDeploy or destroy infrastructure from the UI:\n- Stage selector (dev, staging, production)\n- Deploy button (runs `sst deploy --stage `)\n- Remove button (runs `sst remove --stage `)\n- Live command output\n\n### Outputs Tab\n\nDisplays all provisioned infrastructure outputs:\n- ARNs, URLs, and connection strings\n- Copy-to-clipboard for each value\n- Output source module\n\n### Resources Tab\n\nLists all deployed AWS resources by type and URN.\n\n### Configure Tab\n\nFull configuration form for SST/infrastructure settings:\n- Enable toggle\n- Project name, region, account ID\n- KMS key settings\n- OIDC provider selection (GitHub Actions, Fly.io, Roles Anywhere)\n- IAM role name\n\n## AWS Secrets Module\n\nThe `aws-secrets` module is the most commonly used infra module. It provisions:\n\n- **IAM Role** -- For CI/CD and local AWS access\n- **OIDC Provider** -- For passwordless authentication from GitHub Actions or Fly.io\n- **KMS Key + Alias** -- For encrypting secrets at rest\n- **KMS Access Policy** -- Grants the IAM role encrypt/decrypt permissions\n\n```nix\nstackpanel.infra.aws.secrets = {\n enable = true;\n region = \"us-west-2\";\n\n iam = {\n role-name = \"myapp-secrets-role\";\n additional-policies = [\n \"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess\"\n ];\n };\n\n kms = {\n alias = \"myapp-secrets\";\n deletion-window-days = 30;\n };\n\n oidc = {\n provider = \"github-actions\";\n github-actions = {\n org = \"my-org\";\n repo = \"my-repo\";\n branch = \"*\";\n };\n };\n};\n```\n\n### OIDC Providers\n\n| Provider | Use case | Authentication flow |\n|----------|----------|-------------------|\n| `github-actions` | CI/CD from GitHub | GitHub OIDC token -> STS AssumeRoleWithWebIdentity |\n| `flyio` | Fly.io Machines | Fly OIDC token -> STS AssumeRoleWithWebIdentity |\n| `roles-anywhere` | Local dev with Step CA | mTLS certificate -> IAM Roles Anywhere |\n\n## Deployment Module\n\nThe deployment infra module provisions hosting resources for your apps. Currently supports Cloudflare Workers:\n\n```nix\nstackpanel.apps.web = {\n port = 0;\n framework.tanstack-start.enable = true;\n\n deployment = {\n enable = true;\n host = \"cloudflare\";\n bindings = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n secrets = [ \"DATABASE_URL\" \"BETTER_AUTH_SECRET\" ];\n };\n};\n```\n\nSupported framework/host combinations:\n\n| Framework | Cloudflare | Fly.io |\n|-----------|-----------|--------|\n| `tanstack-start` | TanStackStart Worker | Container |\n| `nextjs` | Nextjs Worker | Container |\n| `vite` | Vite Worker | Container |\n| `hono` | Worker (Node compat) | Container |\n| `astro` | Astro Worker | Container |\n| `remix` | Remix Worker | Container |\n| `nuxt` | Nuxt Worker | Container |\n\n## Generated Files\n\nThe infra system generates a complete TypeScript package at `packages/infra/`:\n\n```\npackages/infra/\n alchemy.run.ts # Orchestrator (imports all modules, syncs outputs)\n package.json # Dependencies from all active modules\n tsconfig.json # TypeScript config\n src/\n index.ts # Infra class (input resolution, output sync)\n types.ts # Per-module input TypeScript interfaces\n resources/\n iam-role.ts # Custom adopt-safe IAM Role resource\n kms-key.ts # Custom adopt-safe KMS Key resource\n kms-alias.ts # Custom KMS Alias resource\n modules/\n aws-secrets/\n index.ts # AWS secrets provisioning\n policies.ts # IAM policy builders\n deployment.ts # App deployment to Cloudflare\n database.ts # Database provisioning\n```\n\nThese files are regenerated on each devshell entry. The `alchemy.run.ts` orchestrator imports all active modules and runs `Infra.syncAll()` to write outputs to your storage backend.\n\n## Pulling Outputs\n\nAfter deploying, pull outputs from your storage backend into the local Nix state:\n\n```bash\ninfra:pull-outputs\n```\n\nThis reads outputs from Chamber/SSM and writes them to `.stack/data/infra-outputs.nix`, making them available as `config.stack.infra.outputs.*` in your Nix config.\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `infra:deploy` | Deploy all infrastructure modules |\n| `infra:deploy --adopt` | Deploy, adopting existing resources |\n| `infra:deploy --stage ` | Deploy to a specific stage |\n| `infra:destroy` | Remove all provisioned resources |\n| `infra:dev` | Start infrastructure dev mode |\n| `infra:pull-outputs` | Pull outputs from storage backend |\n\nAll commands accept additional Alchemy CLI options. Run `infra:deploy --help` for the full list.\n\n## Troubleshooting\n\n### \"EntityAlreadyExists\" on first deploy\n\nYour AWS resources already exist from a previous provisioning tool. Use `--adopt`:\n\n```bash\ninfra:deploy --adopt\n```\n\n### \"Resource already exists\" Alchemy error\n\nThis is different from the AWS error above. It means the Alchemy resource type is registered twice. This can happen if you have conflicting Alchemy versions. Check `packages/infra/package.json` and ensure a single `alchemy` version.\n\n### Outputs not showing up after deploy\n\n1. Check that `storage-backend` is configured (not `\"none\"`)\n2. Run `infra:pull-outputs` to sync outputs to local state\n3. Reload the devshell to pick up new outputs\n\n### \"storage-bakend\" typo error\n\nIf you see `The option 'stack.infra.storage-bakend' does not exist`, check your config for the typo. The correct option is `storage-backend` (with a `c`).\n\n## Reference\n\n- [Deployment Overview](/docs/deployment) for all deployment options\n- [Machines](/docs/deployment/machines) for machine inventory\n- [SST](/docs/deployment/sst) for the older SST-based infrastructure\n- [Secrets](/docs/secrets) for encrypted secrets management\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.8s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "in_progress", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.1s) - -Now update the deployment index and meta.json: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "\n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n", - "newString": "\n \n Provision cloud resources with the Alchemy-based infra module system\n \n \n Define and discover deployment target machines (static or AWS EC2)\n \n \n Deploy to Cloudflare Workers and Pages\n \n \n Deploy to Fly.io with generated fly.toml and Dockerfile\n \n \n Build OCI-compatible container images using Nix or Dockerfile generation\n \n \n AWS infrastructure provisioning with KMS, IAM, and OIDC authentication\n \n" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "- Need AWS infrastructure? The SST extension provisions it with IAM, KMS, and OIDC already wired up.", - "newString": "- Need AWS infrastructure? The [infra module system](./infrastructure) provisions it with IAM, KMS, and OIDC already wired up.\n- Need to manage deployment targets? The [machines module](./machines) discovers EC2 instances or accepts static definitions." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/index.mdx", - "oldString": "### SST (AWS)\n\nSST is the most comprehensive—it provisions cloud infrastructure (KMS keys, IAM roles, S3 buckets) alongside your deployment. The SST extension handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials.", - "newString": "### Infrastructure (Alchemy)\n\nThe [infra module system](./infrastructure) is the most comprehensive approach -- it provisions cloud infrastructure (KMS keys, IAM roles, databases, caches) alongside your deployment using Alchemy. It handles OIDC authentication for CI, so your GitHub Actions workflow can deploy to AWS without long-lived credentials. Use `infra:deploy --adopt` to bring existing resources under management.\n\n### Machines\n\nThe [machines module](./machines) maintains an inventory of your deployment targets. Machines can be declared statically or discovered from AWS EC2 at deploy time. The inventory feeds into the Studio UI's Deploy panel and Colmena for NixOS deployments.\n\n### SST (Legacy)\n\n[SST](./sst) is an alternative infrastructure approach using the SST framework directly. For new projects, the [infra module system](./infrastructure) is recommended as it provides more flexibility and integrates with the broader Stack infra pipeline." -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.6s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/meta.json", - "oldString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"containers\", \"cloudflare\", \"fly\", \"sst\"]\n}", - "newString": "{\n \"title\": \"Containers & Deployment\",\n \"description\": \"Build containers and deploy to production\",\n \"icon\": \"cloud-upload\",\n \"pages\": [\"index\", \"infrastructure\", \"machines\", \"cloudflare\", \"fly\", \"containers\", \"sst\"]\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.2s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Read current deployment docs to understand what's already documented", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read the deploy panel and machines infra module for machine setup flow", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Read the machines infra module to understand configuration", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Update deployment index to include machines and infrastructure docs", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Create machines setup doc covering: Nix config, Studio UI, AWS EC2 discovery", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Create infrastructure provisioning doc covering: infra modules, alchemy, adopt, deploy", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Update meta.json to include new pages", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.6s) - -Here's a summary of what was done: - -## How to Set Up Machines in Studio UI - -The **Deploy** panel in Studio (`/studio/deploy`) shows your machine inventory. To populate it: - -1. Enable the machines module in `.stack/config.nix` with either `source = "static"` (declare machines in Nix) or `source = "aws-ec2"` (discover from EC2 at deploy time) -2. Run `infra:deploy` to provision/discover machines -3. Run `infra:pull-outputs` to sync inventory to local state -4. Reload devshell and open Studio -- machines appear in the Deploy panel's Machines tab - -The Deploy panel has four tabs: **Machines** (card grid with host/SSH/roles/tags), **App Targets** (which apps deploy where), **Actions** (Colmena eval/build/apply buttons), and **Settings** (Colmena config). - -## Docs Created/Updated - -**New pages:** -- `apps/docs/content/docs/deployment/machines.mdx` -- Comprehensive machine inventory docs covering static machines, AWS EC2 discovery (tag mapping, host resolution, SSH defaults), Studio UI Deploy panel walkthrough, app-to-machine mapping, and end-to-end example -- `apps/docs/content/docs/deployment/infrastructure.mdx` -- Infrastructure provisioning docs covering the Alchemy-based infra module system, available modules (aws-secrets, deployment, database, cache, machines), `--adopt` for existing resources, stage selection, storage backends (SOPS/SSM/Chamber), generated files, Studio UI Infrastructure panel, and CLI reference - -**Updated pages:** -- `apps/docs/content/docs/deployment/index.mdx` -- Added Infrastructure and Machines cards at the top, updated platform-specific notes to reference new docs and mark SST as legacy -- `apps/docs/content/docs/deployment/meta.json` -- Added `infrastructure` and `machines` to the page order - ---- - -## User - -the studio UI should allow me to add/configure machines fully - ---- - -## Assistant (Build · claude-opus-4-6 · 168.7s) - -_Thinking:_ - -The user wants the Studio UI to have full machine configuration capabilities - not just viewing machines, but adding, editing, and configuring them directly from the UI. Let me explore what's currently there and what needs to be built. - -Let me look at: -1. The current deploy panel to understand the read-only view -2. How other panels handle config editing (e.g., the infra panel's Configure tab, the apps panel's config forms) -3. How data gets written back to Nix config via the agent -4. The agent API endpoints for writing nix data - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore config editing patterns in Studio", - "prompt": "I need to understand how the Studio UI writes configuration back to Nix. Search thoroughly for:\n\n1. How `useNixData` and `useNixDataMutation` work - find the hooks in apps/web/src/lib/use-agent.ts or similar\n2. How the infra panel's Configure tab saves changes (apps/web/src/components/studio/panels/infra/use-sst-config.ts)\n3. How other panels handle form state + saving to Nix (look for patterns using agent client methods like `updateNixData`, `setNixData`, `writeNixData`, or similar)\n4. The agent HTTP client methods for writing data back - search in apps/web/src/lib/agent-http-client.ts or packages/agent-client\n5. Find examples of dialogs/forms that add new items to Nix config (e.g., add-variable-dialog, add-user dialogs, etc.)\n\nReturn:\n- The exact hook signatures for reading/writing nix data\n- The agent client method signatures for writing data\n- 2-3 concrete examples of how existing panels save config changes (file paths, line numbers, patterns)\n- How the data flows: UI form -> hook -> agent client -> Go agent -> nix file write", - "subagent_type": "explore" -} -``` -**Output:** -``` -Now I have a complete picture. Here is the comprehensive analysis: - ---- - -## Complete Data Flow: Studio UI -> Nix File Writes - -### 1. Hook Signatures for Reading/Writing Nix Data - -There are **two generations** of hooks and **three write mechanisms**: - -#### A. Legacy Generic Hook: `useNixData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1153-1200 - -```typescript -function useNixData( - entity: string, - options: { initialData?: T; autoRefetch?: boolean } = {}, -): { - // ... all useQuery return fields - data: T | null; - mutate: (data: T) => Promise; // writes the entire entity - refetch: () => Promise<...>; -} -``` - -- **Read:** `GET /api/nix/data?entity=` -- transforms response keys from kebab-case to snake_case via `kebabToSnake()` -- **Write (mutate):** `POST /api/nix/data` with `{ entity, data: snakeToKebab(data) }` -- transforms keys from snake_case to kebab-case - -#### B. Legacy Map Hook: `useNixMapData` (HTTP REST) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1210-1267 - -```typescript -function useNixMapData( - entity: string, - options: { initialData?: Record; autoRefetch?: boolean } = {}, -): { - data: Record; - set: (key: string, value: V) => Promise; // key-level upsert - update: (key: string, updates: Partial) => Promise; // merge update - remove: (key: string) => Promise; // key-level delete - refetch: () => Promise<...>; -} -``` - -- Uses `MapEntityClient` internally (in `agent.ts`, lines 112-163) -- **set** -> `POST /api/nix/data { entity, key, data }` -- **remove** -> `POST /api/nix/data { entity, key, delete: true }` - -#### C. Modern Typed Hooks: Connect-RPC (e.g., `useApps`, `useSetApps`) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 336-540 - -```typescript -function useApps(): UseQueryResult>; -function useSetApps(): UseMutationResult; // calls client.setApps(apps) -function useVariables(): UseQueryResult>; -function useSetVariables(): UseMutationResult; // calls client.setVariables(variables) -function useUsers(): UseQueryResult<...>; -function useSetUsers(): UseMutationResult; // calls client.setUsers(users) -function useConfig(): UseQueryResult<...>; -function useSetConfig(): UseMutationResult; // calls client.setConfig(config) -function useSecrets(): UseQueryResult<...>; -function useSetSecrets(): UseMutationResult; // calls client.setSecrets(secrets) -function useAws(): UseQueryResult<...>; -function useSetAws(): UseMutationResult; // calls client.setAws(aws) -``` - -These use `createClient(AgentService, transport)` to call the proto-defined RPCs. - -#### D. Patch Hook: `usePatchNixData` (Connect-RPC) -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts`, lines 1718-1750 - -```typescript -function usePatchNixData(): UseMutationResult; - -interface PatchNixDataParams { - entity: string; // e.g., "apps", "config" - key: string; // e.g., "web" (for map entities), "" for non-map - path: string; // dot-separated camelCase path, e.g., "go.mainPackage" - value: string; // JSON-encoded value, e.g., '"./cmd/api"' - valueType: string; // "string" | "bool" | "number" | "list" | "object" | "null" -} -``` - -This is the surgical editor -- changes a single field without replacing the whole entity. - ---- - -### 2. Agent HTTP Client Methods for Writing Data - -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent.ts` - -#### Generic REST methods (lines 607-675): -```typescript -class AgentHttpClient { - async get(path: string): Promise; - async post(path: string, body: unknown): Promise; - async delete(path: string): Promise; -} -``` - -#### Nix entity clients (lines 514-557): -```typescript -nix = { - apps: new MapEntityClient(this, "apps"), - services: new MapEntityClient(this, "services"), - users: new MapEntityClient(this, "users"), - variables: new MapEntityClient(this, "variables"), - tasks: new MapEntityClient(this, "tasks"), - generatedFiles: new MapEntityClient(this, "generated-files"), - entity: (name: string) => new MapEntityClient(this, name), - config: async (options) => { /* GET /api/nix/config */ }, - refreshConfig: async () => { /* POST /api/nix/config */ }, -}; -``` - -#### MapEntityClient (lines 112-163): -```typescript -class MapEntityClient { - async all(): Promise>; // GET /api/nix/data?entity=... - async get(key: string): Promise; // reads all, extracts key - async set(key: string, value: V): Promise; // POST /api/nix/data {entity, key, data} - async update(key: string, updates: Partial): Promise; // read-merge-set - async remove(key: string): Promise; // POST /api/nix/data {entity, key, delete:true} -} -``` - ---- - -### 3. Concrete Examples of How Panels Save Config Changes - -#### Example 1: SST Infra Panel -- Configuration Tab -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/infra/use-sst-config.ts` - -**Pattern:** `useNixData` (whole-entity replacement) - -1. **Hook setup** (line 78-82): `const { data: sstData, mutate: setSstData } = useNixData("sst", { initialData: DEFAULT_SST_DATA });` -2. **Local form state** (lines 85-97): `useState` for each field (`formData`, `hasChanges`, etc.) -3. **Sync effect** (lines 100-154): When `sstData` changes, merge with inherited defaults and populate form state -4. **Field updaters** (lines 157-192): `updateField()`, `updateNestedField()`, `updateOidcProviderField()` set local state and mark `hasChanges = true` -5. **Save** (lines 195-208): `handleSave()` calls `await setSstData(formData)` -- this triggers: - - `useNixData.mutation.mutationFn` -> `client.post("/api/nix/data", { entity: "sst", data: snakeToKebab(formData) })` - - Go agent `handleNixDataWrite` -> `s.store.WriteEntity("sst", data)` -> serializes to Nix, writes `.stack/config.nix` - -#### Example 2: Configuration Panel -- AWS/Step CA/Theme/IDE -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/configuration/use-configuration.ts` - -**Pattern:** Multiple independent `useNixData` hooks with per-section save callbacks - -1. **Hook setup** (lines 116-140): Multiple calls: - ```typescript - const { data: stepCaData, mutate: setStepCa } = useNixData("step-ca"); - const { data: awsData, mutate: setAws } = useNixData("aws"); - const { data: themeData, mutate: setTheme } = useNixData("theme"); - const { data: ideData, mutate: setIde } = useNixData("ide"); - ``` -2. **Per-section save** (lines 459-475 for Step CA): - ```typescript - const saveStepCa = useCallback(async () => { - await setStepCa({ - ...(stepCaData ?? {}), - enable: stepEnabled, - "ca-url": optionalValue(stepCaUrl), - "ca-fingerprint": optionalValue(stepCaFingerprint), - // ... other fields - }); - }, [...deps]); - ``` - Each `setXxx()` call replaces the entire entity in its data file. - -#### Example 3: Add Variable Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/variables/add-variable-dialog.tsx` - -**Pattern:** Direct `MapEntityClient` for key-level writes (not whole entity) - -1. **Get client** (line 64): `const agentClient = useAgentClient();` -2. **On submit** (lines 214-238): - ```typescript - const variablesClient = client.nix.mapEntity("variables"); - const existing = await variablesClient.get(fullId); - if (existing) { toast.error("already exists"); return; } - const newVariable: Variable = { id: fullId, value: trimmedValue }; - await variablesClient.set(fullId, newVariable); - ``` -3. For **secrets** (lines 204-221): Also writes to SOPS via `client.writeGroupSecret({...})` plus creates a variable stub entry. -4. The `variablesClient.set(key, value)` call -> `POST /api/nix/data { entity: "variables", key: fullId, data: snakeToKebabValues({...}) }` -> Go agent `handleKeyLevelWrite` -> `s.store.SetKey("variables", key, value)` -- reads existing map, inserts key, serializes entire map back to Nix. - -#### Example 4: Add App Dialog -- Key-Level Map Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx` - -Same pattern as variables (lines 76-100): -```typescript -const appsClient = client.nix.mapEntity("apps"); -const existingApp = await appsClient.get(values.id); -if (existingApp) { toast.error("already exists"); return; } -const newApp: AppEntity = { id: values.id, name: ..., path: ..., ... }; -await appsClient.set(values.id, newApp); -``` - -#### Example 5: Packages Panel -- Whole-Entity Array Write -**File:** `/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/packages/use-packages.ts` - -```typescript -const { data: userPackages, mutate: setUserPackages } = useNixData("packages", { initialData: [] }); - -const handleAddPackage = async (pkg) => { - const newPackages = [...currentPackages, attrPath]; - await setUserPackages(newPackages); // replaces entire packages list -}; -``` - ---- - -### 4. Complete Data Flow Diagram - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ UI Form (React Component) │ -│ ├── useState for local form state │ -│ ├── onChange handlers update local state │ -│ └── onSave calls hook mutate/set method │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Hook Layer (use-agent.ts) │ -│ │ -│ Path A: useNixData(entity).mutate(data) │ -│ ├── snakeToKebab(data) transforms keys to Nix convention │ -│ └── client.post("/api/nix/data", { entity, data }) │ -│ │ -│ Path B: MapEntityClient.set(key, value) │ -│ ├── snakeToKebabValues({[key]: value}) transforms inner keys │ -│ └── client.post("/api/nix/data", { entity, key, data }) │ -│ │ -│ Path C: usePatchNixData().mutate({entity,key,path,value,valueType}) │ -│ └── Connect-RPC: client.patchNixData({...}) │ -│ │ -│ Path D: useSetApps/useSetVariables etc. │ -│ └── Connect-RPC: client.setApps(proto_message) │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ HTTP or Connect-RPC to localhost:9876 - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Go Agent (apps/stack-go/internal/agent/server/) │ -│ │ -│ Path A/B: handleNixData (nix_data.go:42) │ -│ ├── POST: handleNixDataWrite (line 191) │ -│ │ ├── With key: handleKeyLevelWrite -> store.SetKey(entity,key,val)│ -│ │ │ Reads existing map, inserts/updates key, writes back │ -│ │ └── Without key: store.WriteEntity(entity, data) │ -│ │ Replaces entire entity │ -│ └── GET: handleNixDataRead -> store.ReadEntity / evaluateConfig │ -│ │ -│ Path C: PatchNixData (connect_patch.go:25) │ -│ ├── Builds full path: entity + "." + key + "." + path │ -│ ├── Prepends "stack." -> strips it -> gets config-internal path │ -│ └── store.PatchConsolidatedData(configPath, parsedValue) │ -│ Walks dot-separated path, creates intermediate maps, sets value │ -│ │ -│ Path D: SetApps etc. (connect_entities_gen.go) │ -│ └── writeNixEntityJSON(entity, protojsonBytes) │ -│ CamelCaseToNixJSON -> unwrap envelope -> WriteEntity │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Store Layer (apps/stack-go/pkg/nixdata/store.go) │ -│ │ -│ WriteEntity(entity, data) (line 177): │ -│ ├── If consolidated config: Read config.nix -> update entity key │ -│ │ -> WriteConsolidatedData (line 427) │ -│ │ -> nixser.SerializeWithSections(data) -> write .stack/ │ -│ │ config.nix │ -│ └── If legacy per-file: nixser.SerializeIndented(data) │ -│ -> write .stack/data/.nix │ -│ │ -│ SetKey(entity, key, value) (line 267): │ -│ readExistingMap -> set key in map -> writeMap │ -│ -> WriteConsolidatedData or per-file write │ -│ │ -│ PatchConsolidatedData(path, value) (line 456): │ -│ ReadConsolidatedData -> walk path with CamelToKebab conversion │ -│ -> set leaf value -> WriteConsolidatedData │ -└─────────┬───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────┐ -│ Nix File on Disk │ -│ │ -│ .stack/config.nix (consolidated, primary target) │ -│ Contains: { apps = {...}; variables = {...}; sst = {...}; ... } │ -│ │ -│ .stack/data/.nix (legacy per-entity files) │ -│ Contains: { key1 = {...}; key2 = {...}; } │ -│ │ -│ Serialization: Go nixser package converts Go map[string]any -> Nix │ -│ expression syntax (attrsets, strings, lists, booleans, numbers) │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - -### Key-Case Transformation Boundary - -The system maintains two naming conventions: -- **TypeScript/Proto side:** `snake_case` (matching protobuf conventions) -- **Nix side:** `kebab-case` (matching Nix conventions) - -Transformation happens in two places: -1. **TypeScript:** `snakeToKebab()` / `kebabToSnake()` in `/Users/cm/git/darkmatter/stack/apps/web/src/lib/nix-data/index.ts` (lines 48-84). Map keys (user-defined IDs like app names, variable names) are explicitly preserved using the `MAP_FIELD_NAMES` set (lines 20-42). -2. **Go:** `CamelCaseToNixJSON()` / `NixJSONToCamelCase()` in `/Users/cm/git/darkmatter/stack/apps/stack-go/pkg/nixdata/transform.go` for the Connect-RPC path. Same map-field-names exemption list. - -### SSE Feedback Loop - -After any write, the Go agent broadcasts an SSE event (`config.changed`) which the `AgentSSEProvider` in the browser receives, triggering TanStack Query cache invalidation so all UI panels automatically refresh with the new data. - - -session_id: ses_35678281cffeylSnbvcqulDIO4 - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 10.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "in_progress", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "pending", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "pending", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "pending", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "pending", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.5s) - -Let me look at a concrete add dialog example and the nix data hooks more closely to match the patterns exactly. - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/apps/add-app-dialog.tsx" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { Button } from "@ui/button"; -00004| import { -00005| Dialog, -00006| DialogContent, -00007| DialogDescription, -00008| DialogFooter, -00009| DialogHeader, -00010| DialogTitle, -00011| } from "@ui/dialog"; -00012| import { Loader2, Plus } from "lucide-react"; -00013| import { useCallback, useState } from "react"; -00014| import { toast } from "sonner"; -00015| import { useAgentContext, useAgentClient } from "@/lib/agent-provider"; -00016| import type { AppEntity } from "@/lib/types"; -00017| -00018| import { -00019| type AppForm, -00020| AppFormFields, -00021| type AppFormValues, -00022| parsePortValue, -00023| } from "./app-form-fields"; -00024| -00025| interface AddAppDialogProps { -00026| onSuccess: () => void; -00027| } -00028| -00029| export function AddAppDialog({ onSuccess }: AddAppDialogProps) { -00030| const { token } = useAgentContext(); -00031| const [dialogOpen, setDialogOpen] = useState(false); -00032| const [isSaving, setIsSaving] = useState(false); -00033| const [formRef, setFormRef] = useState(null); -00034| const [isFormValid, setIsFormValid] = useState(false); -00035| -00036| const handleFormReady = useCallback((form: AppForm) => { -00037| setFormRef(form); -00038| }, []); -00039| -00040| const handleValuesChange = useCallback((values: AppFormValues) => { -00041| const hasId = values.id?.trim().length > 0; -00042| const hasPath = values.path?.trim().length > 0; -00043| setIsFormValid(hasId && hasPath); -00044| }, []); -00045| -00046| const handleOpenChange = (open: boolean) => { -00047| setDialogOpen(open); -00048| if (!open) { -00049| // Reset form and validity when closing -00050| formRef?.reset(); -00051| setIsFormValid(false); -00052| } -00053| }; -00054| -00055| const handleSubmit = async () => { -00056| if (!formRef || !token) { -00057| toast.error("Not connected to agent"); -00058| return; -00059| } -00060| -00061| // Trigger validation -00062| const isValid = await formRef.trigger(); -00063| if (!isValid) return; -00064| -00065| const values = formRef.getValues(); -00066| -00067| if (!values.id.trim()) { -00068| toast.error("Please enter an app ID"); -00069| return; -00070| } -00071| -00072| setIsSaving(true); -00073| try { -00074| const client = useAgentClient(); -00075| if (token) client.setToken(token); -00076| const appsClient = client.nix.mapEntity("apps"); -00077| -00078| const existingApp = await appsClient.get(values.id); -00079| if (existingApp) { -00080| toast.error(`App "${values.id}" already exists`); -00081| setIsSaving(false); -00082| return; -00083| } -00084| -00085| // With simplified schema: environments is Record -00086| // Start with default "dev" environment -00087| const newApp: AppEntity = { -00088| id: values.id, -00089| name: values.name || values.id, -00090| description: values.description || undefined, -00091| path: values.path || `apps/${values.id}`, -00092| type: values.type || "bun", -00093| port: parsePortValue(values.port), -00094| domain: values.domain || undefined, -00095| environments: { -00096| dev: { name: "dev", env: {} }, -00097| }, -00098| }; -00099| -00100| await appsClient.set(values.id, newApp); -00101| toast.success( -00102| `Created app "${values.id}". You can now configure tasks and variables.`, -00103| ); -00104| handleOpenChange(false); -00105| onSuccess(); -00106| } catch (err) { -00107| toast.error(err instanceof Error ? err.message : "Failed to create app"); -00108| } finally { -00109| setIsSaving(false); -00110| } -00111| }; -00112| -00113| return ( -00114| -00115| -00123| -00124| -00125| Add New App -00126| -00127| Create a new app. You can configure tasks and variables after -00128| creation. -00129| -00130| -00131|
-00132| -00138|
-00139| -00140| -00143| -00147| -00148|
-00149|
-00150| ); -00151| } -00152| - -(End of file - total 152 lines) -
-``` -``` - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/use-agent.ts", - "offset": 1150, - "limit": 130 -} -``` -**Output:** -``` - -01151| * - aws → useAws() -01152| */ -01153| export function useNixData( -01154| entity: string, -01155| options: { initialData?: T; autoRefetch?: boolean } = {}, -01156| ) { -01157| const { initialData, autoRefetch = true } = options; -01158| const client = useAgentClient(); -01159| const queryClient = useQueryClient(); -01160| -01161| const queryKey = [...agentQueryKeys.all, "entity", entity]; -01162| -01163| const query = useQuery({ -01164| queryKey, -01165| queryFn: async () => { -01166| // Use the underlying HTTP API to fetch entity data -01167| const response = await client.get<{ -01168| success: boolean; -01169| data: { entity: string; exists: boolean; data: T }; -01170| }>(`/api/nix/data?entity=${encodeURIComponent(entity)}`); -01171| -01172| if (!response.success || !response.data.exists) { -01173| return null; -01174| } -01175| return kebabToSnake(response.data.data) as T; -01176| }, -01177| initialData, -01178| refetchOnWindowFocus: autoRefetch, -01179| }); -01180| -01181| const mutation = useMutation({ -01182| mutationFn: async (data: T) => { -01183| await client.post("/api/nix/data", { -01184| entity, -01185| data: snakeToKebab(data), -01186| }); -01187| // Return the new data -01188| return data; -01189| }, -01190| onSuccess: () => { -01191| queryClient.invalidateQueries({ queryKey }); -01192| }, -01193| }); -01194| -01195| return { -01196| ...query, -01197| mutate: mutation.mutateAsync, -01198| refetch: query.refetch, -01199| }; -01200| } -01201| -01202| /** -01203| * Generic hook for map-style Nix data entities with key-level operations. -01204| * Uses the legacy HTTP client under the hood. -01205| * -01206| * @deprecated For standard entities, prefer specific hooks: -01207| * - apps → useApps() + useSetApps() -01208| * - variables → useVariables() + useSetVariables() -01209| */ -01210| export function useNixMapData( -01211| entity: string, -01212| options: { initialData?: Record; autoRefetch?: boolean } = {}, -01213| ) { -01214| const { initialData, autoRefetch = true } = options; -01215| const client = useAgentClient(); -01216| const queryClient = useQueryClient(); -01217| const mapClient = useMemo( -01218| () => client.mapEntity(entity), -01219| [client, entity], -01220| ); -01221| -01222| const queryKey = [...agentQueryKeys.all, "mapEntity", entity]; -01223| -01224| const query = useQuery({ -01225| queryKey, -01226| queryFn: async () => { -01227| return mapClient.all(); -01228| }, -01229| initialData, -01230| refetchOnWindowFocus: autoRefetch, -01231| }); -01232| -01233| const setMutation = useMutation({ -01234| mutationFn: async ({ key, value }: { key: string; value: V }) => { -01235| await mapClient.set(key, value); -01236| }, -01237| onSuccess: () => { -01238| queryClient.invalidateQueries({ queryKey }); -01239| }, -01240| }); -01241| -01242| const updateMutation = useMutation({ -01243| mutationFn: async ({ key, updates }: { key: string; updates: Partial }) => { -01244| await mapClient.update(key, updates); -01245| }, -01246| onSuccess: () => { -01247| queryClient.invalidateQueries({ queryKey }); -01248| }, -01249| }); -01250| -01251| const removeMutation = useMutation({ -01252| mutationFn: async (key: string) => { -01253| await mapClient.remove(key); -01254| }, -01255| onSuccess: () => { -01256| queryClient.invalidateQueries({ queryKey }); -01257| }, -01258| }); -01259| -01260| return { -01261| ...query, -01262| set: (key: string, value: V) => setMutation.mutateAsync({ key, value }), -01263| update: (key: string, updates: Partial) => updateMutation.mutateAsync({ key, updates }), -01264| remove: (key: string) => removeMutation.mutateAsync(key), -01265| refetch: query.refetch, -01266| }; -01267| } -01268| -01269| // ============================================================================= -01270| // Turbo Package Graph -01271| // ============================================================================= -01272| -01273| export interface TurboTask { -01274| name: string; -01275| command?: string; -01276| } -01277| -01278| export interface TurboPackage { -01279| name: string; -01280| path: string; - -(File has more lines. Use 'offset' parameter to read beyond line 1280) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.5s) - -Now let me look at what imports are available in the UI components and how dialogs are structured: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/lib/agent-provider.tsx", - "offset": 1, - "limit": 50 -} -``` -**Output:** -``` - -00002| -00003| import { -00004| createContext, -00005| type ReactNode, -00006| useCallback, -00007| useContext, -00008| useEffect, -00009| useMemo, -00010| useRef, -00011| useState, -00012| } from "react"; -00013| import { AgentHttpClient, AGENT_AUTH_ERROR_EVENT } from "./agent"; -00014| import { useAgent, useAgentHealth } from "@/lib/use-agent"; -00015| import { useAgentSSEOptional } from "@/lib/agent-sse-provider"; -00016| -00017| const STORAGE_KEY = "stack.agent.token"; -00018| -00019| /** -00020| * Get the initial token from localStorage (runs synchronously during useState init). -00021| * This prevents a flash of "Connect to Agent" on page refresh. -00022| */ -00023| function getInitialToken(): string | null { -00024| if (typeof window === "undefined") return null; -00025| -00026| // Check for token in query parameter first -00027| const urlParams = new URLSearchParams(window.location.search); -00028| const queryToken = urlParams.get("token"); -00029| if (queryToken && queryToken.length >= 10) { -00030| return queryToken; -00031| } -00032| -00033| // Otherwise try localStorage -00034| return localStorage.getItem(STORAGE_KEY); -00035| } -00036| -00037| /** -00038| * Decode a JWT token without verifying the signature. -00039| * Returns the payload or null if invalid. -00040| */ -00041| function decodeJWT(token: string): { agent_id?: string; exp?: number } | null { -00042| try { -00043| const parts = token.split("."); -00044| if (parts.length !== 3) return null; -00045| -00046| // Decode the payload (second part) -00047| const payload = parts[1]; -00048| // Handle URL-safe base64 -00049| const base64 = payload.replace(/-/g, "+").replace(/_/g, "/"); -00050| const jsonPayload = atob(base64); -00051| return JSON.parse(jsonPayload); - -(File has more lines. Use 'offset' parameter to read beyond line 51) - -``` -``` - -``` -Tool: glob - -**Input:** -```json -{ - "pattern": "apps/web/src/components/studio/panels/deploy/*" -} -``` -**Output:** -``` -/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 36.1s) - -Good -- there's only one file in the deploy folder. Now I'll build the complete machine management UI. I'll create: - -1. A `use-machines.ts` hook for reading/writing machine config -2. A `machine-form-fields.tsx` shared form component -3. An `add-machine-dialog.tsx` for adding machines -4. An `edit-machine-dialog.tsx` for editing machines -5. Update `deploy-panel.tsx` to integrate everything - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "content": "\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";\n\n// =============================================================================\n// Types — match the Nix module schema (nix/stack/infra/modules/machines)\n// =============================================================================\n\nexport interface MachineSSH {\n\tuser: string;\n\tport: number;\n\tkey_path: string | null;\n}\n\nexport interface MachineConfig {\n\tid?: string | null;\n\tname: string | null;\n\thost: string | null;\n\tssh: MachineSSH;\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublic_ip: string | null;\n\tprivate_ip: string | null;\n\ttarget_env: string | null;\n\tlabels: Record;\n\tnixos_profile: string | null;\n\tnixos_modules: string[];\n\tenv: Record;\n\tmetadata: Record;\n}\n\nexport interface AwsFilter {\n\tname: string;\n\tvalues: string[];\n}\n\nexport interface AwsMachinesConfig {\n\tregion: string | null;\n\tinstance_ids: string[];\n\tfilters: AwsFilter[];\n\tname_tag_keys: string[];\n\trole_tag_keys: string[];\n\ttag_keys: string[];\n\tenv_tag_keys: string[];\n\thost_preference: string[];\n\tssh: MachineSSH;\n}\n\nexport interface MachinesModuleConfig {\n\tenable: boolean;\n\tsource: \"static\" | \"aws-ec2\";\n\taws: AwsMachinesConfig;\n\tmachines: Record;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nexport const DEFAULT_SSH: MachineSSH = {\n\tuser: \"root\",\n\tport: 22,\n\tkey_path: null,\n};\n\nexport const DEFAULT_MACHINE: MachineConfig = {\n\tid: null,\n\tname: null,\n\thost: null,\n\tssh: { ...DEFAULT_SSH },\n\ttags: [],\n\troles: [],\n\tprovider: null,\n\tarch: null,\n\tpublic_ip: null,\n\tprivate_ip: null,\n\ttarget_env: null,\n\tlabels: {},\n\tnixos_profile: null,\n\tnixos_modules: [],\n\tenv: {},\n\tmetadata: {},\n};\n\nconst DEFAULT_AWS_CONFIG: AwsMachinesConfig = {\n\tregion: null,\n\tinstance_ids: [],\n\tfilters: [{ name: \"instance-state-name\", values: [\"running\"] }],\n\tname_tag_keys: [\"Name\"],\n\trole_tag_keys: [\"stack:role\", \"role\"],\n\ttag_keys: [\"stack:tag\", \"tag\"],\n\tenv_tag_keys: [\"stack:env\", \"env\", \"stage\"],\n\thost_preference: [\"publicDns\", \"publicIp\", \"privateIp\"],\n\tssh: { ...DEFAULT_SSH },\n};\n\nconst DEFAULT_CONFIG: MachinesModuleConfig = {\n\tenable: false,\n\tsource: \"static\",\n\taws: DEFAULT_AWS_CONFIG,\n\tmachines: {},\n};\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_CONFIG;\n\t\tconst machines = (rawInfra as Record).machines as Partial | undefined;\n\t\tif (!machines) return DEFAULT_CONFIG;\n\t\treturn {\n\t\t\tenable: machines.enable ?? false,\n\t\t\tsource: machines.source ?? \"static\",\n\t\t\taws: {\n\t\t\t\t...DEFAULT_AWS_CONFIG,\n\t\t\t\t...(machines.aws ?? {}),\n\t\t\t},\n\t\t\tmachines: (machines.machines ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: MachinesModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\tmachines: newConfig,\n\t\t} as any);\n\t};\n\n\tconst addMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateMachine = async (key: string, machine: MachineConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.machines = { ...updated.machines, [key]: machine };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeMachine = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.machines;\n\t\tupdated.machines = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateSettings = async (settings: Partial) => {\n\t\tawait saveConfig({ ...config, ...settings });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [113:8] Variable 'client' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "export function useMachinesConfig() {\n\tconst client = useAgentClient();\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "export function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [5:10] Identifier 'useAgentClient' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "import { useNixData } from \"@/lib/use-agent\";\nimport { useAgentClient } from \"@/lib/agent-provider\";", - "newString": "import { useNixData } from \"@/lib/use-agent\";" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 29.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx", - "content": "\"use client\";\n\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport type { MachineConfig } from \"./use-machines\";\n\ninterface MachineFormFieldsProps {\n\tvalues: MachineConfig;\n\tonChange: (values: MachineConfig) => void;\n\tshowIdField?: boolean;\n\tmachineId?: string;\n\tonIdChange?: (id: string) => void;\n}\n\nconst ARCH_OPTIONS = [\n\t{ value: \"x86_64-linux\", label: \"x86_64-linux\" },\n\t{ value: \"aarch64-linux\", label: \"aarch64-linux\" },\n];\n\nconst PROVIDER_OPTIONS = [\n\t{ value: \"aws\", label: \"AWS\" },\n\t{ value: \"hetzner\", label: \"Hetzner\" },\n\t{ value: \"gcp\", label: \"GCP\" },\n\t{ value: \"azure\", label: \"Azure\" },\n\t{ value: \"digitalocean\", label: \"DigitalOcean\" },\n\t{ value: \"bare-metal\", label: \"Bare Metal\" },\n];\n\nconst ENV_OPTIONS = [\n\t{ value: \"production\", label: \"Production\" },\n\t{ value: \"staging\", label: \"Staging\" },\n\t{ value: \"development\", label: \"Development\" },\n];\n\nfunction updateField(\n\tvalues: MachineConfig,\n\tonChange: (v: MachineConfig) => void,\n\tkey: K,\n\tvalue: MachineConfig[K],\n) {\n\tonChange({ ...values, [key]: value });\n}\n\nexport function MachineFormFields({\n\tvalues,\n\tonChange,\n\tshowIdField,\n\tmachineId,\n\tonIdChange,\n}: MachineFormFieldsProps) {\n\treturn (\n\t\t
\n\t\t\t{/* Identity */}\n\t\t\t{showIdField && (\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t onIdChange?.(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUnique identifier used as the Nix attribute name. Use lowercase with hyphens.\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t)}\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\tupdateField(values, onChange, \"name\", e.target.value || null)\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t{/* Connection */}\n\t\t\t
\n\t\t\t\tConnection\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"host\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tuser: e.target.value || \"root\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tport: Number.parseInt(e.target.value, 10) || 22,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tupdateField(values, onChange, \"ssh\", {\n\t\t\t\t\t\t\t\t\t...values.ssh,\n\t\t\t\t\t\t\t\t\tkey_path: e.target.value || null,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Networking */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"public_ip\", e.target.value || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"private_ip\",\n\t\t\t\t\t\t\t\te.target.value || null,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Classification */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"arch\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ARCH_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"provider\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{PROVIDER_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(values, onChange, \"target_env\", v || null)\n\t\t\t\t\t\t}\n\t\t\t\t\t>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ENV_OPTIONS.map((opt) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Roles and Tags */}\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"roles\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for app-to-machine targeting\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t\t\tonChange,\n\t\t\t\t\t\t\t\t\"tags\",\n\t\t\t\t\t\t\t\te.target.value\n\t\t\t\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t

\n\t\t\t\t\t\tUsed for grouping and filtering\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.7s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport {\n\ttype MachineConfig,\n\tDEFAULT_MACHINE,\n\ttype useMachinesConfig,\n} from \"./use-machines\";\n\ninterface AddMachineDialogProps {\n\tmachines: ReturnType;\n}\n\nexport function AddMachineDialog({ machines }: AddMachineDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\tconst [machineId, setMachineId] = useState(\"\");\n\tconst [values, setValues] = useState({ ...DEFAULT_MACHINE });\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetMachineId(\"\");\n\t\t\tsetValues({ ...DEFAULT_MACHINE });\n\t\t}\n\t};\n\n\tconst isValid = machineId.trim().length > 0 && /^[a-z0-9-]+$/.test(machineId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = machineId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"Machine ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (machines.config.machines[id]) {\n\t\t\ttoast.error(`Machine \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.addMachine(id, values);\n\t\t\ttoast.success(`Machine \"${id}\" added`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add machine\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tAdd Machine\n\t\t\t\t\t\n\t\t\t\t\t\tAdd a new machine to your deployment inventory.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 13.9s) - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Loader2 } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { MachineFormFields } from \"./machine-form-fields\";\nimport type { MachineConfig, useMachinesConfig } from \"./use-machines\";\n\ninterface EditMachineDialogProps {\n\tmachineId: string;\n\tmachine: MachineConfig;\n\tmachines: ReturnType;\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n}\n\nexport function EditMachineDialog({\n\tmachineId,\n\tmachine,\n\tmachines,\n\topen,\n\tonOpenChange,\n}: EditMachineDialogProps) {\n\tconst [saving, setSaving] = useState(false);\n\tconst [deleting, setDeleting] = useState(false);\n\tconst [values, setValues] = useState({ ...machine });\n\n\tuseEffect(() => {\n\t\tif (open) {\n\t\t\tsetValues({ ...machine });\n\t\t}\n\t}, [open, machine]);\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateMachine(machineId, values);\n\t\t\ttoast.success(`Machine \"${machineId}\" updated`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to update machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\tsetDeleting(true);\n\t\ttry {\n\t\t\tawait machines.removeMachine(machineId);\n\t\t\ttoast.success(`Machine \"${machineId}\" removed`);\n\t\t\tonOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(\n\t\t\t\terr instanceof Error ? err.message : \"Failed to remove machine\",\n\t\t\t);\n\t\t} finally {\n\t\t\tsetDeleting(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tEdit Machine: {machineId}\n\t\t\t\t\t\n\t\t\t\t\t\tUpdate machine configuration. Changes are written to your Nix config.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{deleting && }\n\t\t\t\t\t\tDelete\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 96.6s) - -Now update the deploy panel to integrate everything -- add machine management, edit capability, settings for AWS EC2 config: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "content": "\"use client\";\n\n/**\n * Deploy Panel - Colmena-centric deployment management.\n *\n * Shows machine inventory, app-to-machine mapping, and deploy actions\n * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and\n * AWS EC2 discovery configuration.\n */\n\nimport { useState, useMemo } from \"react\";\nimport { Badge } from \"@ui/badge\";\nimport { Button } from \"@ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@ui/card\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@ui/tabs\";\nimport {\n\tActivity,\n\tAlertCircle,\n\tCheckCircle,\n\tCloudOff,\n\tCpu,\n\tHardDrive,\n\tLoader2,\n\tNetwork,\n\tPencil,\n\tPlay,\n\tRefreshCw,\n\tRocket,\n\tServer,\n\tSettings,\n\tShield,\n\tXCircle,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { useAgentContext } from \"@/lib/agent-provider\";\nimport { useNixConfig } from \"@/lib/use-agent\";\nimport { PanelHeader } from \"../shared/panel-header\";\nimport { cn } from \"@/lib/utils\";\nimport { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface MachineInfo {\n\tid: string;\n\tname: string;\n\thost: string | null;\n\tssh: {\n\t\tuser: string;\n\t\tport: number;\n\t\tkeyPath: string | null;\n\t};\n\ttags: string[];\n\troles: string[];\n\tprovider: string | null;\n\tarch: string | null;\n\tpublicIp: string | null;\n\tprivateIp: string | null;\n\ttargetEnv: string | null;\n\tlabels: Record;\n}\n\ninterface AppDeployMapping {\n\tenable: boolean;\n\ttargets: string[];\n\tresolvedTargets: string[];\n\trole: string | null;\n\tnixosModules: string[];\n\tsystem: string | null;\n}\n\ninterface ColmenaConfig {\n\tenable: boolean;\n\tmachineSource: string;\n\tgenerateHive: boolean;\n\tconfig: string;\n\tmachineCount: number;\n\tmachineIds: string[];\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nfunction useColmenaData() {\n\tconst { data: nixConfig, isLoading, refetch } = useNixConfig();\n\n\tconst result = useMemo(() => {\n\t\tconst cfg = nixConfig as Record | null | undefined;\n\t\tif (!cfg) return { machines: {}, appDeploy: {}, colmenaConfig: null };\n\n\t\tconst serializable = cfg.serializable as Record | undefined;\n\t\tconst colmenaConfig = (serializable?.colmena ?? null) as ColmenaConfig | null;\n\n\t\tconst colmenaData = cfg.colmena as Record | undefined;\n\t\tconst machinesComputed = (colmenaData?.machinesComputed ?? {}) as Record;\n\n\t\tconst rawApps = (cfg.apps ?? cfg.appsComputed ?? {}) as Record>;\n\t\tconst appDeploy: Record = {};\n\n\t\tfor (const [appName, appCfg] of Object.entries(rawApps)) {\n\t\t\tconst deploy = appCfg.deploy as Record | undefined;\n\t\t\tif (deploy?.enable) {\n\t\t\t\tappDeploy[appName] = {\n\t\t\t\t\tenable: true,\n\t\t\t\t\ttargets: (deploy.targets as string[]) ?? [],\n\t\t\t\t\tresolvedTargets: (deploy.resolvedTargets as string[]) ?? [],\n\t\t\t\t\trole: (deploy.role as string | null) ?? null,\n\t\t\t\t\tnixosModules: (deploy.nixosModules as string[]) ?? [],\n\t\t\t\t\tsystem: (deploy.system as string | null) ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { machines: machinesComputed, appDeploy, colmenaConfig };\n\t}, [nixConfig]);\n\n\treturn { ...result, isLoading, refetch };\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\nfunction MachineCard({\n\tmachine,\n\tonEdit,\n}: {\n\tmachine: MachineInfo;\n\tonEdit?: () => void;\n}) {\n\tconst isReachable = machine.host !== null;\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.name || machine.id}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{onEdit && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{isReachable ? (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machine.host}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tno host\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t{machine.ssh.user}@{machine.ssh.port}\n\t\t\t\t\t
\n\t\t\t\t\t{machine.arch && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.arch}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.provider && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.provider}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t\t{machine.targetEnv && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machine.targetEnv}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{(machine.tags.length > 0 || machine.roles.length > 0) && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{machine.roles.map((role) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{role}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{machine.tags.map((tag) => (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{tag}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t))}\n\t\t\t\t\t
\n\t\t\t\t)}\n\t\t\t
\n\t\t\n\t);\n}\n\nfunction AppTargetRow({\n\tappName,\n\tdeploy,\n\tmachines,\n}: {\n\tappName: string;\n\tdeploy: AppDeployMapping;\n\tmachines: Record;\n}) {\n\tconst resolvedNames = deploy.resolvedTargets.map(\n\t\t(id) => machines[id]?.name ?? id,\n\t);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appName}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{deploy.targets.length > 0\n\t\t\t\t\t\t\t? `Targets: ${deploy.targets.join(\", \")}`\n\t\t\t\t\t\t\t: deploy.role\n\t\t\t\t\t\t\t\t? `Role: ${deploy.role}`\n\t\t\t\t\t\t\t\t: \"No targets defined\"}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{deploy.role && (\n\t\t\t\t\t\n\t\t\t\t\t\t{deploy.role}\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{resolvedNames.length} machine{resolvedNames.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Settings: AWS EC2 Discovery Configuration\n// =============================================================================\n\nfunction AwsEc2Settings({ machines }: { machines: ReturnType }) {\n\tconst cfg = machines.config.aws;\n\tconst [saving, setSaving] = useState(false);\n\tconst [region, setRegion] = useState(cfg.region ?? \"\");\n\tconst [sshUser, setSshUser] = useState(cfg.ssh.user);\n\tconst [sshPort, setSshPort] = useState(cfg.ssh.port);\n\tconst [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? \"\");\n\tconst [hostPref, setHostPref] = useState(cfg.host_preference.join(\", \"));\n\n\tconst handleSave = async () => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait machines.updateSettings({\n\t\t\t\tsource: \"aws-ec2\",\n\t\t\t\tenable: true,\n\t\t\t\taws: {\n\t\t\t\t\t...cfg,\n\t\t\t\t\tregion: region || null,\n\t\t\t\t\tssh: {\n\t\t\t\t\t\tuser: sshUser || \"root\",\n\t\t\t\t\t\tport: sshPort || 22,\n\t\t\t\t\t\tkey_path: sshKeyPath || null,\n\t\t\t\t\t},\n\t\t\t\t\thost_preference: hostPref.split(\",\").map((s) => s.trim()).filter(Boolean),\n\t\t\t\t},\n\t\t\t});\n\t\t\ttoast.success(\"AWS EC2 settings saved\");\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to save\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\tAWS EC2 Discovery\n\t\t\t\t\n\t\t\t\t\tAuto-discover machines from running EC2 instances\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRegion(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setHostPref(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\tDefault SSH\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshUser(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshPort(Number.parseInt(e.target.value, 10) || 22)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setSshKeyPath(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport function DeployPanel() {\n\tconst { isConnected } = useAgentContext();\n\tconst { machines: computedMachines, appDeploy, colmenaConfig, isLoading, refetch } = useColmenaData();\n\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);\n\tconst [editingMachine, setEditingMachine] = useState<{ id: string; config: MachineConfig } | null>(null);\n\n\t// Merge computed machines (from colmena/infra) with static config machines\n\tconst configMachines = machinesConfig.config.machines;\n\tconst computedList = Object.values(computedMachines);\n\tconst configList = Object.entries(configMachines);\n\n\t// Build combined machine list: computed takes precedence, config fills gaps\n\tconst allMachineEntries = useMemo(() => {\n\t\tconst seen = new Set();\n\t\tconst entries: Array<{ id: string; info: MachineInfo; configurable: boolean }> = [];\n\n\t\t// Add computed machines\n\t\tfor (const m of computedList) {\n\t\t\tseen.add(m.id);\n\t\t\tentries.push({ id: m.id, info: m, configurable: !!configMachines[m.id] });\n\t\t}\n\n\t\t// Add config-only machines not in computed\n\t\tfor (const [key, cfg] of configList) {\n\t\t\tif (!seen.has(key)) {\n\t\t\t\tentries.push({\n\t\t\t\t\tid: key,\n\t\t\t\t\tinfo: {\n\t\t\t\t\t\tid: key,\n\t\t\t\t\t\tname: cfg.name ?? key,\n\t\t\t\t\t\thost: cfg.host,\n\t\t\t\t\t\tssh: { user: cfg.ssh.user, port: cfg.ssh.port, keyPath: cfg.ssh.key_path },\n\t\t\t\t\t\ttags: cfg.tags,\n\t\t\t\t\t\troles: cfg.roles,\n\t\t\t\t\t\tprovider: cfg.provider,\n\t\t\t\t\t\tarch: cfg.arch,\n\t\t\t\t\t\tpublicIp: cfg.public_ip,\n\t\t\t\t\t\tprivateIp: cfg.private_ip,\n\t\t\t\t\t\ttargetEnv: cfg.target_env,\n\t\t\t\t\t\tlabels: cfg.labels,\n\t\t\t\t\t},\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}, [computedList, configList, configMachines]);\n\n\tconst machineCount = allMachineEntries.length;\n\tconst appDeployEntries = Object.entries(appDeploy);\n\tconst healthyCount = allMachineEntries.filter((m) => m.info.host !== null).length;\n\tconst unhealthyCount = machineCount - healthyCount;\n\n\tconst handleRefresh = async () => {\n\t\tsetIsRefreshing(true);\n\t\ttry {\n\t\t\tawait Promise.all([refetch(), machinesConfig.refetch()]);\n\t\t} finally {\n\t\t\tsetIsRefreshing(false);\n\t\t}\n\t};\n\n\tif (!isConnected) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Agent Not Connected

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tConnect to the stack agent to manage deployments.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t);\n\t}\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t);\n\t}\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRefreshing ? \"Refreshing...\" : \"Refresh\"}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t}\n\t\t\t/>\n\n\t\t\t{/* Status overview */}\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Machines

\n\t\t\t\t\t\t\t

{machineCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-green-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-green-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Reachable

\n\t\t\t\t\t\t\t

{healthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-amber-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-amber-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Unreachable

\n\t\t\t\t\t\t\t

{unhealthyCount}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t
0 ? \"bg-blue-500/10\" : \"bg-secondary\",\n\t\t\t\t\t\t)}>\n\t\t\t\t\t\t\t 0 ? \"text-blue-500\" : \"text-muted-foreground\",\n\t\t\t\t\t\t\t)} />\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Deploy-enabled apps

\n\t\t\t\t\t\t\t

{appDeployEntries.length}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t\n\n\t\t\t\t{/* Machines Tab */}\n\t\t\t\t\n\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Machines

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tAdd machines manually using the Add Machine button above,\n\t\t\t\t\t\t\t\t\t\tor configure AWS EC2 discovery in Settings and run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:deploy.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{allMachineEntries.map((entry) => (\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst cfg = configMachines[entry.id];\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (cfg) setEditingMachine({ id: entry.id, config: cfg });\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t\n\t\t\t\t\t{appDeployEntries.length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No App Targets

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tNo apps have deployment enabled. Add deploy.enable = true and deploy.targets to your app config.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{appDeployEntries.map(([appName, deploy]) => (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* Actions Tab */}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tColmena Actions\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tRun Colmena commands against your fleet\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{machineCount === 0 ? (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\tNo machines in inventory. Add machines or configure EC2 discovery first.\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tActions run the generated wrapper scripts with your configured defaults.\n\t\t\t\t\t\t\t\t\t\tUse the CLI for advanced options: colmena-apply --on tag:prod\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t{/* Settings Tab */}\n\t\t\t\t\n\t\t\t\t\t{/* Module enable + source selection */}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tMachines Module\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tConfigure how machines are sourced for deployment\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tEnable the machines infra module\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({ enable: checked })\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tmachinesConfig.updateSettings({\n\t\t\t\t\t\t\t\t\t\t\tsource: v as \"static\" | \"aws-ec2\",\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tStatic (defined in config)\n\t\t\t\t\t\t\t\t\t\tAWS EC2 (auto-discover)\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t{colmenaConfig && (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Hive Config

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.config ?? \".stack/state/colmena/hive.nix\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

Generate Hive

\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{colmenaConfig.generateHive ? \"Yes\" : \"No\"}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* AWS EC2 settings (shown when source is aws-ec2) */}\n\t\t\t\t\t{machinesConfig.config.source === \"aws-ec2\" && (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{/* Edit dialog */}\n\t\t\t{editingMachine && (\n\t\t\t\t {\n\t\t\t\t\t\tif (!open) setEditingMachine(null);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.9s) - -No LSP errors in our new files. The pre-existing errors in `use-sst-config.ts`, `dashboard-sidebar.tsx`, and `use-configuration.ts` are unrelated. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Study existing add/edit dialog patterns (add-app-dialog, add-variable-dialog, infra configure tab)", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Read deploy-panel.tsx to understand current structure", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddMachineDialog component for adding static machines", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Build EditMachineDialog component for editing existing machines", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Build MachineConfigForm shared form component", - "status": "completed", - "priority": "high" - }, - { - "id": "6", - "content": "Add machine CRUD to deploy panel (add, edit, delete buttons)", - "status": "completed", - "priority": "high" - }, - { - "id": "7", - "content": "Add AWS EC2 discovery configuration section to Settings tab", - "status": "completed", - "priority": "high" - }, - { - "id": "8", - "content": "Wire up useNixData for machines entity read/write", - "status": "completed", - "priority": "high" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.0s) - -Here's what was built: - -## New Files - -**`apps/web/src/components/studio/panels/deploy/use-machines.ts`** -- `useMachinesConfig()` hook that reads/writes the `infra.machines` config via `useNixData<>("infra")` -- Full CRUD: `addMachine()`, `updateMachine()`, `removeMachine()`, `updateSettings()` -- Types matching the Nix module schema: `MachineConfig`, `MachineSSH`, `AwsMachinesConfig`, `MachinesModuleConfig` - -**`apps/web/src/components/studio/panels/deploy/machine-form-fields.tsx`** -- Shared form component used by both add and edit dialogs -- Fields: ID (optional), display name, host, SSH (user/port/key), public/private IP, architecture (select), provider (select), environment (select), roles (comma-separated), tags (comma-separated) -- Grouped into logical fieldsets (Connection, Networking, Classification) - -**`apps/web/src/components/studio/panels/deploy/add-machine-dialog.tsx`** -- Dialog with "Add Machine" button that appears in the panel header -- Validates machine ID (lowercase + hyphens), checks for duplicates -- Writes to Nix config via `machines.addMachine()` - -**`apps/web/src/components/studio/panels/deploy/edit-machine-dialog.tsx`** -- Opens when clicking a machine card or pencil icon -- Full edit form + destructive "Delete" button -- Saves via `machines.updateMachine()` / `machines.removeMachine()` - -## Updated Files - -**`apps/web/src/components/studio/panels/deploy/deploy-panel.tsx`** -- Rewritten to integrate: -- **Machines tab**: Cards are now clickable to edit, show pencil icon, merge computed + config machines -- **Header**: "Add Machine" button next to Refresh -- **Settings tab**: New sections: - - Machines module enable/disable toggle - - Source selector (Static vs AWS EC2) - - Full AWS EC2 discovery config (region, host preference, default SSH) shown when source is `aws-ec2` - - Colmena hive config display -- **Edit dialog**: Wired up with state management, opens on card click - -## Data Flow - -UI form -> `useMachinesConfig()` hook -> `useNixData("infra").mutate()` -> `POST /api/nix/data { entity: "infra", data }` -> Go agent writes `.stack/config.nix` -> SSE `config.changed` event -> UI auto-refreshes - ---- - -## User - -how do i provision the machine as well? - ---- - -## Assistant (Build · claude-opus-4-6 · 175.2s) - -_Thinking:_ - -The user wants to provision machines (create EC2 instances, not just discover existing ones) from the Studio UI. Currently the machines module only supports "static" (manually defined) and "aws-ec2" (discover existing instances). There's no provisioning path. - -Let me look at the existing AWS EC2 infra module to understand what's already there for actually creating EC2 instances. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore EC2 provisioning infra modules", - "prompt": "Search thoroughly for everything related to EC2 instance provisioning (creating instances, not just discovering them):\n\n1. Read nix/stack/infra/modules/aws-ec2/module.nix - understand how EC2 instances are provisioned\n2. Read nix/stack/infra/modules/aws-ec2/index.ts - the Alchemy module that creates EC2 instances\n3. Read nix/stack/infra/modules/aws-ec2-app/module.nix - per-app EC2 resources\n4. Read nix/stack/infra/modules/aws-network/module.nix - VPC/subnet provisioning\n5. Read nix/stack/infra/modules/aws-security-groups/module.nix - security groups\n6. Read nix/stack/infra/modules/aws-key-pairs/module.nix - SSH key pairs\n7. Read nix/stack/infra/modules/aws-iam/module.nix - IAM for EC2\n8. Check what custom Alchemy resources exist for EC2 in nix/stack/infra/templates/ (ec2-instance.ts, etc.)\n9. Check .stack/config.nix for any existing aws-ec2/aws-network config sections\n\nReturn for each module:\n- Whether it exists and what it provisions\n- The Nix options it defines (option names, types, defaults)\n- How it relates to other modules (dependencies)\n- The current config values in .stack/config.nix if any\n- Key line numbers for important sections", - "subagent_type": "explore" -} -``` -**Output:** -``` -I now have complete information. Here is the comprehensive report. - ---- - -# EC2 Instance Provisioning -- Complete Analysis - -## Overview - -The EC2 provisioning system is a layered architecture with **7 Nix modules**, **13 custom Alchemy resources** (TypeScript), and a **codegen engine** that wires them together into a deployable `packages/infra` workspace. There are two approaches to EC2 provisioning: a low-level `aws-ec2` module and a high-level `aws-ec2-app` module. - ---- - -## 1. aws-ec2 (Low-Level EC2 Provisioning) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix` -**Exists: YES (207 lines)** -**What it provisions:** Raw EC2 instances from explicit definitions. No automatic VPC/subnet/AMI resolution. - -**Nix Options** (under `options.stack.infra.aws-ec2`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 145-148 | -| `instances` | `listOf instanceType` | `[]` | 151-154 | -| `defaults` | `instanceType` | `{name="default"; ami=""; instance-type="t3.micro"; subnet-id="";}` | 157-166 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines"]` | 168-172 | - -**instanceType submodule** (lines 70-141): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `name` | `str` | *(required)* | 72-75 | -| `ami` | `str` | *(required)* | 77-80 | -| `instance-type` | `str` | `"t3.micro"` | 82-86 | -| `subnet-id` | `str` | *(required)* | 88-91 | -| `security-group-ids` | `listOf str` | `[]` | 93-97 | -| `key-name` | `nullOr str` | `null` | 99-103 | -| `iam-instance-profile` | `nullOr str` | `null` | 105-109 | -| `user-data` | `nullOr str` | `null` | 111-115 | -| `root-volume-size` | `nullOr int` | `null` | 117-121 | -| `associate-public-ip` | `bool` | `true` | 123-127 | -| `tags` | `attrsOf str` | `{}` | 129-133 | -| `machine` | `machineMetaType` | `{}` | 135-139 | - -**machineMetaType** (lines 36-68): `tags` (listOf str), `roles` (listOf str), `target-env` (nullOr str), `arch` (nullOr str), `ssh` (submodule: `user`=root, `port`=22, `key-path`=null) - -**Dependencies:** Registers into `stack.infra.modules.aws-ec2` (line 178). Requires `@aws-sdk/client-ec2`. Auto-enables `stack.infra.enable` (line 176). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines` -- all JSON strings. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/index.ts` -**119 lines.** Simple loop: merges `defaults` into each `instance`, calls `Ec2Instance()` Alchemy resource, collects outputs. Emits machine inventory with `provider: "aws-ec2"`. - -**Key lines:** Instance creation at line 67-79. Machine inventory build at 88-109. Export at 112-118. - ---- - -## 2. aws-ec2-app (High-Level App-Centric EC2) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix` -**Exists: YES (918 lines)** -**What it provisions:** Per-app EC2 infrastructure including: security groups, key pairs, IAM roles + instance profiles, ALBs + target groups + listeners + host-based routing, ECR repos + GitHub OIDC roles, SSM parameters, and EC2 instances with automatic AMI resolution (Ubuntu 24.04 or NixOS). - -**Top-level options** (under `options.stack.infra.aws-ec2-app`, lines 852-876): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 853-856 | -| `defaults` | `appType` | `{}` | 859-863 | -| `apps` | `attrsOf appType` | `{}` | 865-869 | -| `sync-outputs` | `listOf str` | `["instanceIds" "publicIps" "publicDns" "privateIps" "machines" "albOutputs" "ecrOutputs" "ssmOutputs"]` | 871-875 | - -**appType submodule** (lines 715-849, ~25 options): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `instance-count` | `int` | `1` | 717-720 | -| `instances` | `listOf instanceType` | `[]` | 722-726 | -| `ami` | `nullOr str` | `null` | 728-733 | -| `os-type` | `enum ["ubuntu" "nixos"]` | `"ubuntu"` | 735-739 | -| `nixos` | `nixosType` | `{}` | 741-745 | -| `instance-type` | `nullOr str` | `null` | 747-751 | -| `vpc-id` | `nullOr str` | `null` | 753-757 | -| `subnet-ids` | `listOf str` | `[]` | 759-763 | -| `security-group-ids` | `listOf str` | `[]` | 765-769 | -| `security-group` | `securityGroupType` | `{}` | 771-775 | -| `key-name` | `nullOr str` | `null` | 777-781 | -| `key-pair` | `keyPairType` | `{}` | 783-787 | -| `iam` | `iamType` | `{}` | 789-793 | -| `iam-instance-profile` | `nullOr str` | `null` | 795-799 | -| `user-data` | `nullOr str` | `null` | 801-805 | -| `root-volume-size` | `nullOr int` | `null` | 807-811 | -| `associate-public-ip` | `bool` | `true` | 813-816 | -| `tags` | `attrsOf str` | `{}` | 819-823 | -| `alb` | `albType` | `{}` | 825-829 | -| `ecr` | `ecrType` | `{}` | 831-835 | -| `ssm` | `ssmType` | `{}` | 837-841 | -| `machine` | `machineMetaType` | `{}` | 843-847 | - -**Major nested submodule types:** - -- **securityGroupType** (lines 80-118): `create`, `name`, `description`, `ingress`/`egress` (listOf ruleType), `tags` -- **ruleType** (lines 36-78): `from-port`, `to-port`, `protocol`, `cidr-blocks`, `ipv6-cidr-blocks`, `security-group-ids`, `description` -- **keyPairType** (lines 120-152): `create`, `name`, `public-key`, `tags`, `destroy-on-delete` -- **iamType** (lines 168-221): `enable`, `role-name`, `assume-role-policy`, `managed-policy-arns` (defaults include SSM + ECR policies), `inline-policies`, `tags`, `instance-profile-name`, `instance-profile-tags` -- **nixosType** (lines 223-249): `ami-id`, `flake-url`, `host-config`, `flake-version` -- **albType** (lines 331-429): `enable`, `create`, `name`, `scheme`, `ip-address-type`, `subnet-ids`, `security-group-ids`, `http`, `https`, `certificate-arn`, `ssl-policy`, `existing-listener-http-arn`, `existing-listener-https-arn`, `hostnames`, `host-rule-priority`, `target-group` -- **albTargetGroupType** (lines 309-329): `port`, `protocol`, `health-check` -- **albHealthCheckType** (lines 251-307): `enabled`, `path`, `protocol`, `port`, `interval`, `timeout`, `healthy-threshold`, `unhealthy-threshold`, `matcher` -- **ecrType** (lines 489-533): `enable`, `create`, `repo-name`, `image-tag-mutability`, `scan-on-push`, `lifecycle-policy`, `github` -- **githubOidcType** (lines 431-487): `enable`, `repo-owner`, `repo-name`, `allowed-branches`, `allowed-workflows`, `allow-tags`, `role-name`, `oidc-provider-arn`, `create-oidc-provider` -- **ssmType** (lines 535-591): `enable`, `region`, `path-prefix`, `parameters`, `secure-parameters`, `env-file-path`, `refresh-script-path`, `install-cli`, `use-chamber` -- **instanceType** (per-instance override, lines 627-713): `name`, `ami`, `os-type`, `nixos`, `instance-type`, `subnet-id`, `security-group-ids`, `key-name`, `iam-instance-profile`, `user-data`, `root-volume-size`, `associate-public-ip`, `tags`, `machine` - -**Dependencies:** `@aws-sdk/client-ec2`, `@aws-sdk/client-iam`, `@aws-sdk/client-elastic-load-balancing-v2`, `@aws-sdk/client-ecr`, `@aws-sdk/client-ssm`, `@aws-sdk/client-sts` (lines 890-896). - -**Outputs:** `instanceIds`, `publicIps`, `publicDns`, `privateIps`, `machines`, `albOutputs`, `ecrOutputs`, `ssmOutputs` (lines 906-914). - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/index.ts` -**962 lines.** The most complex module. Key logic: - -- **Default VPC resolution** (lines 233-255): Auto-discovers default VPC and subnets via DescribeVpcs/DescribeSubnets -- **AMI resolution** (lines 297-342): Auto-resolves Ubuntu 24.04 (Canonical, owner `099720109477`) or NixOS (Determinate Systems, owner `535002876703`, `determinate/nixos/epoch-1/*`) -- **NixOS user data** (lines 344-358): Generates bootstrap script using `fh apply nixos` with FlakeHub URL -- **Per-app loop** (lines 391-946): - 1. Security group creation (lines 406-433) - 2. Key pair import (lines 435-450) - 3. IAM role + instance profile with EC2 trust policy (lines 452-488) - 4. SSM parameter creation (lines 490-523) - 5. ECR repo + GitHub OIDC role for CI push (lines 525-698) - 6. EC2 instance creation with round-robin subnet assignment (lines 700-797) - 7. ALB + target group + listeners + host-based routing (lines 799-945) - ---- - -## 3. aws-network (VPC/Subnet Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix` -**Exists: YES (103 lines)** -**What it provisions:** Nothing -- it *discovers* the default VPC and subnets. This is a read-only module. - -**Nix Options** (under `options.stack.infra.aws-network`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 19-23 | -| `region` | `nullOr str` | `config.stack.aws.roles-anywhere.region or null` | 25-29 | -| `vpc.id` | `nullOr str` | `null` | 32-36 | -| `vpc.use-default` | `bool` | `true` | 38-42 | -| `subnets.ids` | `listOf str` | `[]` | 46-49 | -| `subnets.use-default` | `bool` | `true` | 52-56 | -| `sync-outputs` | `listOf str` | `["vpcId" "subnetIds"]` | 59-63 | - -**Outputs:** `vpcId` (string), `subnetIds` (JSON array), `subnetAzs` (JSON array). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Standalone network discovery. Not required by `aws-ec2-app` which has its own built-in VPC discovery (lines 233-255 of index.ts), but useful for `aws-security-groups` and `aws-ec2` which require explicit VPC/subnet IDs. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/index.ts` -**66 lines.** Simple: discovers default VPC, then subnets in that VPC. Falls back to explicit inputs. - ---- - -## 4. aws-security-groups - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix` -**Exists: YES (145 lines)** -**What it provisions:** Standalone security groups in a given VPC. - -**Nix Options** (under `options.stack.infra.aws-security-groups`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 93-97 | -| `vpc-id` | `str` | *(required)* | 99-102 | -| `groups` | `listOf groupType` | `[]` | 104-108 | -| `sync-outputs` | `listOf str` | `["groupIds"]` | 110-114 | - -**groupType**: `name` (str, required), `description` (nullOr str), `ingress`/`egress` (listOf ruleType), `tags` (attrsOf str). - -**Outputs:** `groupIds` (JSON: `{groupName: groupId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent module. Output group IDs can be manually referenced in `aws-ec2.instances[].security-group-ids`. Not automatically wired to aws-ec2 or aws-ec2-app. The `aws-ec2-app` module has its own inline security group creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/index.ts` -**69 lines.** Loops over groups, creates each via `SecurityGroup()` resource. - ---- - -## 5. aws-key-pairs - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix` -**Exists: YES (84 lines)** -**What it provisions:** EC2 SSH key pairs (imports public keys). - -**Nix Options** (under `options.stack.infra.aws-key-pairs`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 37-40 | -| `keys` | `attrsOf keyType` | `{}` | 43-47 | -| `sync-outputs` | `listOf str` | `["keyNames" "keyPairIds"]` | 49-53 | - -**keyType**: `public-key` (str, required), `tags` (attrsOf str), `destroy-on-delete` (bool, default false). - -**Outputs:** `keyNames` (JSON array), `keyPairIds` (JSON: `{keyName: keyPairId}`). - -**Dependencies:** `@aws-sdk/client-ec2`. - -**Relationship:** Independent. Output key names can be referenced in `aws-ec2.instances[].key-name`. The `aws-ec2-app` module has its own inline key pair creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/index.ts` -**42 lines.** Loops over keys, imports each via `KeyPair()` resource. - ---- - -## 6. aws-iam (EC2-Oriented IAM) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix` -**Exists: YES (128 lines)** -**What it provisions:** A single IAM role with EC2 trust policy + instance profile. - -**Nix Options** (under `options.stack.infra.aws-iam`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 30-33 | -| `role.name` | `str` | *(required)* | 37-39 | -| `role.assume-role-policy` | `nullOr (attrsOf anything)` | `null` (defaults to EC2 trust) | 42-46 | -| `role.managed-policy-arns` | `listOf str` | `[]` | 48-52 | -| `role.inline-policies` | `listOf inlinePolicyType` | `[]` | 54-58 | -| `role.tags` | `attrsOf str` | `{}` | 60-64 | -| `instance-profile.name` | `nullOr str` | `null` (defaults to `-profile`) | 68-72 | -| `instance-profile.tags` | `attrsOf str` | `{}` | 74-78 | -| `sync-outputs` | `listOf str` | `["roleArn" "roleName" "instanceProfileArn" "instanceProfileName"]` | 81-85 | - -**Outputs:** `roleArn`, `roleName`, `instanceProfileArn`, `instanceProfileName`. - -**Dependencies:** `@aws-sdk/client-iam`. - -**Relationship:** Independent standalone module. Output `instanceProfileName` can be referenced in `aws-ec2.instances[].iam-instance-profile`. The `aws-ec2-app` module has its own inline IAM creation. - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/index.ts` -**71 lines.** Creates role with EC2 trust policy (line 38), attaches managed policies, creates instance profile. - ---- - -## 7. machines (Machine Inventory Discovery) - -### File: `/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix` -**Exists: YES (307 lines)** -**What it provisions:** Nothing -- it *discovers* existing EC2 instances and emits a machine inventory JSON for Colmena. - -**Nix Options** (under `options.stack.infra.machines`): - -| Option | Type | Default | Line | -|--------|------|---------|------| -| `enable` | `bool` | `false` | 157-161 | -| `source` | `enum ["static" "aws-ec2"]` | `"static"` | 163-169 | -| `aws.region` | `nullOr str` | inherited from `aws.roles-anywhere.region` | 173-177 | -| `aws.instance-ids` | `listOf str` | `[]` | 179-183 | -| `aws.filters` | `listOf awsFilterType` | `[{name="instance-state-name"; values=["running"]}]` | 185-194 | -| `aws.name-tag-keys` | `listOf str` | `["Name"]` | 196-200 | -| `aws.role-tag-keys` | `listOf str` | `["stack:role" "role"]` | 202-209 | -| `aws.tag-keys` | `listOf str` | `["stack:tag" "tag"]` | 211-218 | -| `aws.env-tag-keys` | `listOf str` | `["stack:env" "env" "stage"]` | 220-228 | -| `aws.host-preference` | `listOf enum` | `["publicDns" "publicIp" "privateIp"]` | 230-242 | -| `aws.ssh` | `sshConfigType` | `{user="root"; port=22; keyPath=null}` | 244-248 | -| `machines` | `attrsOf machineType` | `{}` | 251-255 | - -**Outputs:** `machines` (JSON string of machine inventory). - -**Dependencies:** `@aws-sdk/client-ec2` (only when `source == "aws-ec2"`). - -**Relationship:** This is a *discovery* module, not a provisioning module. Its output feeds into Colmena deployment (see below). - ---- - -## 8. Custom Alchemy Resources (Templates) - -All in `/Users/cm/git/darkmatter/stack/nix/stack/infra/templates/`: - -| File | Resource Type | Lines | What it does | -|------|-------------|-------|-------------| -| `ec2-instance.ts` | `stack::Ec2Instance` | 145 | `RunInstancesCommand` to create, `TerminateInstancesCommand` to delete. gp3 volumes. Base64 user data encoding. | -| `security-group.ts` | `stack::SecurityGroup` | 182 | Creates SG, authorizes ingress/egress rules. Adopts existing SGs by name+VPC. | -| `key-pair.ts` | `stack::KeyPair` | 101 | Imports public key via `ImportKeyPairCommand`. Adopts existing by name. | -| `iam-role.ts` | `stack::IamRole` | 153 | Creates/adopts role, updates trust policy, attaches managed + inline policies. | -| `iam-instance-profile.ts` | `stack::IamInstanceProfile` | 137 | Creates/adopts profile, adds role. Full lifecycle (remove role + delete on destroy). | -| `application-load-balancer.ts` | `stack::ApplicationLoadBalancer` | 121 | Creates ALB, returns ARN + DNS + zone ID. | -| `target-group.ts` | `stack::TargetGroup` | 136 | Creates target group with health checks. | -| `listener.ts` | `stack::Listener` | 108 | Creates HTTP/HTTPS listener with SSL policy. | -| `listener-rule.ts` | `stack::ListenerRule` | 105 | Creates host-header routing rules. | -| `target-group-attachment.ts` | `stack::TargetGroupAttachment` | 63 | Registers/deregisters instance to target group. | -| `ecr-repository.ts` | `stack::EcrRepository` | 127 | Creates ECR repo with lifecycle policy. | - -All resources follow the Alchemy `Resource()` pattern with create-or-adopt semantics and `this.phase === "delete"` handling. They are **read at Nix eval time** and written to `packages/infra/src/resources/` by the codegen engine (`codegen.nix`, lines 561-602). - ---- - -## 9. Current config.nix Values - -### File: `/Users/cm/git/darkmatter/stack/.stack/config.nix` - -**Lines 447-475** -- the `infra` section: - -```nix -infra = { - enable = true; # line 448 - storage-backend = { - type = "sops"; # line 450 - sops.group = "dev"; # line 451 - }; - database = { # line 453 - enable = true; - provider = "neon"; - neon = { - region = "aws-us-east-1"; - api-key-ssm-path = "/common/neon-api-key"; - }; - }; - machines = { # line 461 - enable = true; - source = "aws-ec2"; # Discovery mode, not provisioning - aws = { - region = "us-west-2"; - filters = [{ - name = "instance-state-name"; - values = [ "running" ]; - }]; - }; - machines = { }; - }; -}; -``` - -**Key observations about current config:** -- **`infra.enable = true`** -- the infra system is active -- **`machines.enable = true` with `source = "aws-ec2"`** -- EC2 instance *discovery* is enabled (not provisioning) -- **No `aws-ec2` or `aws-ec2-app` sections** -- EC2 provisioning is NOT currently configured -- **No `aws-network` section** -- VPC discovery is not independently enabled -- **No `aws-security-groups` section** -- standalone SG provisioning is not enabled -- **No `aws-key-pairs` section** -- standalone key pair provisioning is not enabled -- **No `aws-iam` section** -- standalone IAM provisioning is not enabled -- Storage backend is SOPS (dev group) -- AWS region from `aws.roles-anywhere.region` = `"us-west-2"` (line 149) - ---- - -## 10. Module Dependency Graph - -``` - ┌─────────────────┐ - │ config.nix │ - │ (infra section) │ - └────────┬────────┘ - │ - ┌────────────┼────────────┐ - │ │ │ - ┌─────▼─────┐ ┌───▼────┐ ┌────▼─────┐ - │ aws-ec2 │ │aws-ec2 │ │ machines │ (discovery) - │ -app │ │(basic) │ │ │ - └─────┬─────┘ └───┬────┘ └──────────┘ - │ │ - ┌───────┬───────┼───────┬───┤ - │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ - SG KeyPair IAM EC2 ALB+TG+Listener+ECR+SSM - (inline) (inline)(inline)(Ec2Instance resource) - │ │ │ │ - ▼ ▼ ▼ ▼ - Alchemy Resources (templates/*.ts) -``` - -**Two provisioning paths:** -1. **Composable (low-level):** `aws-network` -> `aws-security-groups` -> `aws-key-pairs` -> `aws-iam` -> `aws-ec2` (each module independent, manually wire outputs) -2. **All-in-one (high-level):** `aws-ec2-app` (creates SG, key pairs, IAM, ALB, ECR, SSM, and EC2 instances all in one module) - -**Consumer:** The Colmena module (`nix/stack/modules/colmena/module.nix`, lines 577-580) reads machine inventory from `infra.outputs."aws-ec2".machines` OR `infra.outputs."aws-ec2-app".machines` OR `infra.outputs.machines.machines` (the discovery module). - ---- - -## 11. Codegen Wiring - -The codegen engine (`/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix`): -- **Line 225**: Reads `ec2-instance.ts` template -- **Lines 561-566**: Writes it to `${outputDir}/src/resources/ec2-instance.ts` -- **Lines 462-489**: Copies each module's `index.ts` to `${outputDir}/modules/.ts` -- **Lines 261-340**: Generates `alchemy.run.ts` orchestrator that imports all enabled modules -- **Lines 345-348**: Merges all module dependencies into the package.json - -The infra module system entry point is `/Users/cm/git/darkmatter/stack/nix/stack/infra/default.nix` (line 34-35) which imports both `aws-ec2/module.nix` and `aws-ec2-app/module.nix`. - - -session_id: ses_3565ab3f8ffehVv05KuQ9ZqSdj - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 14.9s) - -You already have a complete EC2 provisioning pipeline built out -- two of them actually: - -1. **`aws-ec2`** -- Low-level: you define instances with explicit AMI, subnet, security groups -2. **`aws-ec2-app`** -- High-level: per-app EC2 with auto-AMI resolution, inline SG/IAM/ALB/ECR creation - -Neither is wired into the Studio UI yet. The Deploy panel currently only manages machine *inventory* (static + discovery), not provisioning. - -Let me add EC2 provisioning configuration to the Settings tab so you can define instances and deploy them from the UI. - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "in_progress", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "pending", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "pending", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "pending", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "pending", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 6.3s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts" -} -``` -**Output:** -``` - -00001| "use client"; -00002| -00003| import { useMemo } from "react"; -00004| import { useNixData } from "@/lib/use-agent"; -00005| -00006| // ============================================================================= -00007| // Types — match the Nix module schema (nix/stack/infra/modules/machines) -00008| // ============================================================================= -00009| -00010| export interface MachineSSH { -00011| user: string; -00012| port: number; -00013| key_path: string | null; -00014| } -00015| -00016| export interface MachineConfig { -00017| id?: string | null; -00018| name: string | null; -00019| host: string | null; -00020| ssh: MachineSSH; -00021| tags: string[]; -00022| roles: string[]; -00023| provider: string | null; -00024| arch: string | null; -00025| public_ip: string | null; -00026| private_ip: string | null; -00027| target_env: string | null; -00028| labels: Record; -00029| nixos_profile: string | null; -00030| nixos_modules: string[]; -00031| env: Record; -00032| metadata: Record; -00033| } -00034| -00035| export interface AwsFilter { -00036| name: string; -00037| values: string[]; -00038| } -00039| -00040| export interface AwsMachinesConfig { -00041| region: string | null; -00042| instance_ids: string[]; -00043| filters: AwsFilter[]; -00044| name_tag_keys: string[]; -00045| role_tag_keys: string[]; -00046| tag_keys: string[]; -00047| env_tag_keys: string[]; -00048| host_preference: string[]; -00049| ssh: MachineSSH; -00050| } -00051| -00052| export interface MachinesModuleConfig { -00053| enable: boolean; -00054| source: "static" | "aws-ec2"; -00055| aws: AwsMachinesConfig; -00056| machines: Record; -00057| } -00058| -00059| // ============================================================================= -00060| // Defaults -00061| // ============================================================================= -00062| -00063| export const DEFAULT_SSH: MachineSSH = { -00064| user: "root", -00065| port: 22, -00066| key_path: null, -00067| }; -00068| -00069| export const DEFAULT_MACHINE: MachineConfig = { -00070| id: null, -00071| name: null, -00072| host: null, -00073| ssh: { ...DEFAULT_SSH }, -00074| tags: [], -00075| roles: [], -00076| provider: null, -00077| arch: null, -00078| public_ip: null, -00079| private_ip: null, -00080| target_env: null, -00081| labels: {}, -00082| nixos_profile: null, -00083| nixos_modules: [], -00084| env: {}, -00085| metadata: {}, -00086| }; -00087| -00088| const DEFAULT_AWS_CONFIG: AwsMachinesConfig = { -00089| region: null, -00090| instance_ids: [], -00091| filters: [{ name: "instance-state-name", values: ["running"] }], -00092| name_tag_keys: ["Name"], -00093| role_tag_keys: ["stack:role", "role"], -00094| tag_keys: ["stack:tag", "tag"], -00095| env_tag_keys: ["stack:env", "env", "stage"], -00096| host_preference: ["publicDns", "publicIp", "privateIp"], -00097| ssh: { ...DEFAULT_SSH }, -00098| }; -00099| -00100| const DEFAULT_CONFIG: MachinesModuleConfig = { -00101| enable: false, -00102| source: "static", -00103| aws: DEFAULT_AWS_CONFIG, -00104| machines: {}, -00105| }; -00106| -00107| // ============================================================================= -00108| // Hook -00109| // ============================================================================= -00110| -00111| export function useMachinesConfig() { -00112| const { data: rawInfra, mutate: setInfra, refetch } = useNixData>("infra"); -00113| -00114| const config = useMemo(() => { -00115| if (!rawInfra) return DEFAULT_CONFIG; -00116| const machines = (rawInfra as Record).machines as Partial | undefined; -00117| if (!machines) return DEFAULT_CONFIG; -00118| return { -00119| enable: machines.enable ?? false, -00120| source: machines.source ?? "static", -00121| aws: { -00122| ...DEFAULT_AWS_CONFIG, -00123| ...(machines.aws ?? {}), -00124| }, -00125| machines: (machines.machines ?? {}) as Record, -00126| }; -00127| }, [rawInfra]); -00128| -00129| const saveConfig = async (newConfig: MachinesModuleConfig) => { -00130| const currentInfra = (rawInfra ?? {}) as Record; -00131| await setInfra({ -00132| ...currentInfra, -00133| machines: newConfig, -00134| } as any); -00135| }; -00136| -00137| const addMachine = async (key: string, machine: MachineConfig) => { -00138| const updated = { ...config }; -00139| updated.machines = { ...updated.machines, [key]: machine }; -00140| if (!updated.enable) updated.enable = true; -00141| await saveConfig(updated); -00142| }; -00143| -00144| const updateMachine = async (key: string, machine: MachineConfig) => { -00145| const updated = { ...config }; -00146| updated.machines = { ...updated.machines, [key]: machine }; -00147| await saveConfig(updated); -00148| }; -00149| -00150| const removeMachine = async (key: string) => { -00151| const updated = { ...config }; -00152| const { [key]: _, ...rest } = updated.machines; -00153| updated.machines = rest; -00154| await saveConfig(updated); -00155| }; -00156| -00157| const updateSettings = async (settings: Partial) => { -00158| await saveConfig({ ...config, ...settings }); -00159| }; -00160| -00161| return { -00162| config, -00163| addMachine, -00164| updateMachine, -00165| removeMachine, -00166| updateSettings, -00167| refetch, -00168| }; -00169| } -00170| - -(End of file - total 170 lines) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 19.1s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");", - "newString": "// =============================================================================\n// EC2 Provisioning Types — match aws-ec2-app Nix module\n// =============================================================================\n\nexport interface Ec2MachineMeta {\n\ttags: string[];\n\troles: string[];\n\ttarget_env: string | null;\n\tarch: string | null;\n\tssh: MachineSSH;\n}\n\nexport interface Ec2SecurityGroupRule {\n\tfrom_port: number;\n\tto_port: number;\n\tprotocol: string;\n\tcidr_blocks: string[];\n\tdescription: string;\n}\n\nexport interface Ec2SecurityGroup {\n\tcreate: boolean;\n\tname: string | null;\n\tdescription: string | null;\n\tingress: Ec2SecurityGroupRule[];\n\tegress: Ec2SecurityGroupRule[];\n}\n\nexport interface Ec2KeyPair {\n\tcreate: boolean;\n\tname: string | null;\n\tpublic_key: string | null;\n}\n\nexport interface Ec2IamConfig {\n\tenable: boolean;\n\trole_name: string | null;\n}\n\nexport interface Ec2AppInstance {\n\tname: string;\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tsubnet_id: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppConfig {\n\tinstance_count: number;\n\tinstances: Ec2AppInstance[];\n\tami: string | null;\n\tos_type: \"ubuntu\" | \"nixos\";\n\tinstance_type: string | null;\n\tvpc_id: string | null;\n\tsubnet_ids: string[];\n\tsecurity_group_ids: string[];\n\tsecurity_group: Ec2SecurityGroup;\n\tkey_name: string | null;\n\tkey_pair: Ec2KeyPair;\n\tiam: Ec2IamConfig;\n\tuser_data: string | null;\n\troot_volume_size: number | null;\n\tassociate_public_ip: boolean;\n\ttags: Record;\n\tmachine: Ec2MachineMeta;\n}\n\nexport interface Ec2AppModuleConfig {\n\tenable: boolean;\n\tdefaults: Partial;\n\tapps: Record;\n}\n\nexport const DEFAULT_EC2_MACHINE_META: Ec2MachineMeta = {\n\ttags: [],\n\troles: [],\n\ttarget_env: null,\n\tarch: null,\n\tssh: { ...DEFAULT_SSH },\n};\n\nexport const DEFAULT_EC2_APP: Ec2AppConfig = {\n\tinstance_count: 1,\n\tinstances: [],\n\tami: null,\n\tos_type: \"ubuntu\",\n\tinstance_type: \"t3.micro\",\n\tvpc_id: null,\n\tsubnet_ids: [],\n\tsecurity_group_ids: [],\n\tsecurity_group: {\n\t\tcreate: true,\n\t\tname: null,\n\t\tdescription: null,\n\t\tingress: [\n\t\t\t{ from_port: 22, to_port: 22, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"SSH\" },\n\t\t\t{ from_port: 80, to_port: 80, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTP\" },\n\t\t\t{ from_port: 443, to_port: 443, protocol: \"tcp\", cidr_blocks: [\"0.0.0.0/0\"], description: \"HTTPS\" },\n\t\t],\n\t\tegress: [\n\t\t\t{ from_port: 0, to_port: 0, protocol: \"-1\", cidr_blocks: [\"0.0.0.0/0\"], description: \"All outbound\" },\n\t\t],\n\t},\n\tkey_name: null,\n\tkey_pair: { create: false, name: null, public_key: null },\n\tiam: { enable: true, role_name: null },\n\tuser_data: null,\n\troot_volume_size: null,\n\tassociate_public_ip: true,\n\ttags: {},\n\tmachine: { ...DEFAULT_EC2_MACHINE_META },\n};\n\nconst DEFAULT_EC2_MODULE: Ec2AppModuleConfig = {\n\tenable: false,\n\tdefaults: {},\n\tapps: {},\n};\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nexport function useMachinesConfig() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [224:7] Variable 'DEFAULT_EC2_MODULE' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now add the provisioning hook alongside the machines hook: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/use-machines.ts", - "oldString": "\treturn {\n\t\tconfig,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}", - "newString": "\treturn {\n\t\tconfig,\n\t\trawInfra,\n\t\tsetInfra,\n\t\taddMachine,\n\t\tupdateMachine,\n\t\tremoveMachine,\n\t\tupdateSettings,\n\t\trefetch,\n\t};\n}\n\nexport function useEc2Provisioning() {\n\tconst { data: rawInfra, mutate: setInfra, refetch } = useNixData>(\"infra\");\n\n\tconst config = useMemo(() => {\n\t\tif (!rawInfra) return DEFAULT_EC2_MODULE;\n\t\tconst ec2App = (rawInfra as Record)[\"aws_ec2_app\"] as Partial | undefined;\n\t\tif (!ec2App) return DEFAULT_EC2_MODULE;\n\t\treturn {\n\t\t\tenable: ec2App.enable ?? false,\n\t\t\tdefaults: ec2App.defaults ?? {},\n\t\t\tapps: (ec2App.apps ?? {}) as Record,\n\t\t};\n\t}, [rawInfra]);\n\n\tconst saveConfig = async (newConfig: Ec2AppModuleConfig) => {\n\t\tconst currentInfra = (rawInfra ?? {}) as Record;\n\t\tawait setInfra({\n\t\t\t...currentInfra,\n\t\t\t\"aws_ec2_app\": newConfig,\n\t\t} as any);\n\t};\n\n\tconst addApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tif (!updated.enable) updated.enable = true;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst updateApp = async (key: string, app: Ec2AppConfig) => {\n\t\tconst updated = { ...config };\n\t\tupdated.apps = { ...updated.apps, [key]: app };\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst removeApp = async (key: string) => {\n\t\tconst updated = { ...config };\n\t\tconst { [key]: _, ...rest } = updated.apps;\n\t\tupdated.apps = rest;\n\t\tawait saveConfig(updated);\n\t};\n\n\tconst setEnabled = async (enabled: boolean) => {\n\t\tawait saveConfig({ ...config, enable: enabled });\n\t};\n\n\treturn {\n\t\tconfig,\n\t\taddApp,\n\t\tupdateApp,\n\t\tremoveApp,\n\t\tsetEnabled,\n\t\trefetch,\n\t};\n}" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 45.9s) - -Now create the provisioning UI components: - -``` -Tool: write - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "content": "\"use client\";\n\nimport { Button } from \"@ui/button\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@ui/dialog\";\nimport { Input } from \"@ui/input\";\nimport { Label } from \"@ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@ui/select\";\nimport { Switch } from \"@ui/switch\";\nimport { Loader2, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n\ttype Ec2AppConfig,\n\tDEFAULT_EC2_APP,\n\tDEFAULT_EC2_MACHINE_META,\n\ttype useEc2Provisioning,\n} from \"./use-machines\";\n\ninterface ProvisionInstanceDialogProps {\n\tec2: ReturnType;\n}\n\nconst INSTANCE_TYPES = [\n\t\"t3.micro\",\n\t\"t3.small\",\n\t\"t3.medium\",\n\t\"t3.large\",\n\t\"t3.xlarge\",\n\t\"t3.2xlarge\",\n\t\"m6i.large\",\n\t\"m6i.xlarge\",\n\t\"m6i.2xlarge\",\n\t\"c6i.large\",\n\t\"c6i.xlarge\",\n\t\"c6i.2xlarge\",\n\t\"r6i.large\",\n\t\"r6i.xlarge\",\n];\n\nconst AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog({ ec2 }: ProvisionInstanceDialogProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [saving, setSaving] = useState(false);\n\n\t// Form state\n\tconst [appId, setAppId] = useState(\"\");\n\tconst [instanceCount, setInstanceCount] = useState(1);\n\tconst [instanceType, setInstanceType] = useState(\"t3.micro\");\n\tconst [osType, setOsType] = useState<\"ubuntu\" | \"nixos\">(\"ubuntu\");\n\tconst [vpcId, setVpcId] = useState(\"\");\n\tconst [subnetIds, setSubnetIds] = useState(\"\");\n\tconst [rootVolumeSize, setRootVolumeSize] = useState(\"\");\n\tconst [associatePublicIp, setAssociatePublicIp] = useState(true);\n\tconst [createSg, setCreateSg] = useState(true);\n\tconst [createKeyPair, setCreateKeyPair] = useState(false);\n\tconst [publicKey, setPublicKey] = useState(\"\");\n\tconst [enableIam, setEnableIam] = useState(true);\n\tconst [roles, setRoles] = useState(\"\");\n\tconst [tags, setTags] = useState(\"\");\n\tconst [targetEnv, setTargetEnv] = useState(\"\");\n\n\tconst handleOpenChange = (isOpen: boolean) => {\n\t\tsetOpen(isOpen);\n\t\tif (!isOpen) {\n\t\t\tsetAppId(\"\");\n\t\t\tsetInstanceCount(1);\n\t\t\tsetInstanceType(\"t3.micro\");\n\t\t\tsetOsType(\"ubuntu\");\n\t\t\tsetVpcId(\"\");\n\t\t\tsetSubnetIds(\"\");\n\t\t\tsetRootVolumeSize(\"\");\n\t\t\tsetAssociatePublicIp(true);\n\t\t\tsetCreateSg(true);\n\t\t\tsetCreateKeyPair(false);\n\t\t\tsetPublicKey(\"\");\n\t\t\tsetEnableIam(true);\n\t\t\tsetRoles(\"\");\n\t\t\tsetTags(\"\");\n\t\t\tsetTargetEnv(\"\");\n\t\t}\n\t};\n\n\tconst isValid = appId.trim().length > 0 && /^[a-z0-9-]+$/.test(appId.trim());\n\n\tconst handleSubmit = async () => {\n\t\tconst id = appId.trim();\n\t\tif (!id) {\n\t\t\ttoast.error(\"App ID is required\");\n\t\t\treturn;\n\t\t}\n\t\tif (ec2.config.apps[id]) {\n\t\t\ttoast.error(`EC2 app \"${id}\" already exists`);\n\t\t\treturn;\n\t\t}\n\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst appConfig: Ec2AppConfig = {\n\t\t\t\t...DEFAULT_EC2_APP,\n\t\t\t\tinstance_count: instanceCount,\n\t\t\t\tinstance_type: instanceType,\n\t\t\t\tos_type: osType,\n\t\t\t\tvpc_id: vpcId || null,\n\t\t\t\tsubnet_ids: subnetIds\n\t\t\t\t\t? subnetIds.split(\",\").map((s) => s.trim()).filter(Boolean)\n\t\t\t\t\t: [],\n\t\t\t\troot_volume_size: rootVolumeSize ? Number.parseInt(rootVolumeSize, 10) : null,\n\t\t\t\tassociate_public_ip: associatePublicIp,\n\t\t\t\tsecurity_group: {\n\t\t\t\t\t...DEFAULT_EC2_APP.security_group,\n\t\t\t\t\tcreate: createSg,\n\t\t\t\t},\n\t\t\t\tkey_pair: {\n\t\t\t\t\tcreate: createKeyPair,\n\t\t\t\t\tname: createKeyPair ? `${id}-key` : null,\n\t\t\t\t\tpublic_key: createKeyPair ? publicKey || null : null,\n\t\t\t\t},\n\t\t\t\tiam: {\n\t\t\t\t\tenable: enableIam,\n\t\t\t\t\trole_name: enableIam ? `${id}-ec2-role` : null,\n\t\t\t\t},\n\t\t\t\ttags: {\n\t\t\t\t\tName: id,\n\t\t\t\t\tManagedBy: \"stack-infra\",\n\t\t\t\t},\n\t\t\t\tmachine: {\n\t\t\t\t\t...DEFAULT_EC2_MACHINE_META,\n\t\t\t\t\troles: roles ? roles.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttags: tags ? tags.split(\",\").map((s) => s.trim()).filter(Boolean) : [],\n\t\t\t\t\ttarget_env: targetEnv || null,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait ec2.addApp(id, appConfig);\n\t\t\ttoast.success(`EC2 app \"${id}\" configured with ${instanceCount} instance(s). Run infra:deploy to provision.`);\n\t\t\thandleOpenChange(false);\n\t\t} catch (err) {\n\t\t\ttoast.error(err instanceof Error ? err.message : \"Failed to add EC2 app\");\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tProvision EC2 Instances\n\t\t\t\t\t\n\t\t\t\t\t\tDefine EC2 instances to provision. Run infra:deploy after to create them.\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t
\n\t\t\t\t\t{/* Identity */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setAppId(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\tUnique identifier for this group of instances\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Instance sizing */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setInstanceCount(Number.parseInt(e.target.value, 10) || 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Networking */}\n\t\t\t\t\t
\n\t\t\t\t\t\tNetworking\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setVpcId(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setSubnetIds(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Associate a public IPv4 address

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Auto-create with SSH, HTTP, HTTPS ingress

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Storage */}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setRootVolumeSize(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* SSH Key */}\n\t\t\t\t\t
\n\t\t\t\t\t\tSSH Key Pair\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t

Import your public key to AWS

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{createKeyPair && (\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setPublicKey(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* IAM */}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Create role with SSM + ECR access

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{/* Machine metadata */}\n\t\t\t\t\t
\n\t\t\t\t\t\tMachine Metadata\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setRoles(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t setTags(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n" -} -``` -**Output:** -``` -Wrote file successfully. - -LSP errors detected in other files: - -ERROR [237:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [264:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. -ERROR [292:5] React Hook useCallback has a missing dependency: 'agentClient' -help: Either include it or remove the dependency array. - - -LSP errors detected in other files: - -ERROR [38:3] Identifier 'Cog' is imported but never used. -help: Consider removing this import. -ERROR [73:15] Type 'Module' is imported but never used. -help: Consider removing this import. -ERROR [334:38] Variable 'isLoading' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [338:9] Variable 'activeModuleMeta' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [346:9] Variable 'activeAppIds' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. -ERROR [655:32] Missing "key" prop for element in iterator. -help: Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key). - - -LSP errors detected in other files: - -ERROR [236:33] React Hook useEffect has a complex expression in the dependency array. -help: Extract the expression to a separate variable so it can be statically checked. -ERROR [236:6] React Hook useEffect has a missing dependency: 'usersSettingsData' -help: Either include it or remove the dependency array. -ERROR [256:6] React Hook useEffect has missing dependencies: 'setStepCaFingerprint', and 'setStepCaUrl' -help: Either include it or remove the dependency array. -ERROR [256:19] React hook useEffect depends on `stepConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [285:16] React hook useEffect depends on `awsConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [295:18] React hook useEffect depends on `themeConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [318:16] React hook useEffect depends on `ideConfig`, which changes every render -help: Try memoizing this variable with `useRef` or `useCallback`. -ERROR [410:6] React Hook useCallback has a missing dependency: 'setStepCaFingerprint' -help: Either include it or remove the dependency array. - - -LSP errors detected in this file, please fix: - -ERROR [53:7] Variable 'AWS_REGIONS' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.0s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/provision-instance-dialog.tsx", - "oldString": "const AWS_REGIONS = [\n\t\"us-east-1\",\n\t\"us-east-2\",\n\t\"us-west-1\",\n\t\"us-west-2\",\n\t\"eu-west-1\",\n\t\"eu-central-1\",\n\t\"ap-southeast-1\",\n\t\"ap-northeast-1\",\n];\n\nexport function ProvisionInstanceDialog", - "newString": "export function ProvisionInstanceDialog" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.6s) - -Now add the Provision tab to the deploy panel: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 1, - "limit": 15 -} -``` -**Output:** -``` - -00002| -00003| /** -00004| * Deploy Panel - Colmena-centric deployment management. -00005| * -00006| * Shows machine inventory, app-to-machine mapping, and deploy actions -00007| * (eval/build/apply). Supports full machine CRUD (add/edit/delete) and -00008| * AWS EC2 discovery configuration. -00009| */ -00010| -00011| import { useState, useMemo } from "react"; -00012| import { Badge } from "@ui/badge"; -00013| import { Button } from "@ui/button"; -00014| import { -00015| Card, -00016| CardContent, - -(File has more lines. Use 'offset' parameter to read beyond line 16) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 9.3s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { useMachinesConfig, type MachineConfig } from \"./use-machines\";", - "newString": "import { AddMachineDialog } from \"./add-machine-dialog\";\nimport { EditMachineDialog } from \"./edit-machine-dialog\";\nimport { ProvisionInstanceDialog } from \"./provision-instance-dialog\";\nimport {\n\tuseMachinesConfig,\n\tuseEc2Provisioning,\n\ttype MachineConfig,\n\ttype Ec2AppConfig,\n} from \"./use-machines\";" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [62:7] Type 'Ec2AppConfig' is imported but never used. -help: Consider removing this import. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.7s) - -Now integrate the provisioning hook and tab into the main component. Let me find the right insertion points: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 280, - "limit": 30 -} -``` -**Output:** -``` - -00281| // ============================================================================= -00282| // Settings: AWS EC2 Discovery Configuration -00283| // ============================================================================= -00284| -00285| function AwsEc2Settings({ machines }: { machines: ReturnType }) { -00286| const cfg = machines.config.aws; -00287| const [saving, setSaving] = useState(false); -00288| const [region, setRegion] = useState(cfg.region ?? ""); -00289| const [sshUser, setSshUser] = useState(cfg.ssh.user); -00290| const [sshPort, setSshPort] = useState(cfg.ssh.port); -00291| const [sshKeyPath, setSshKeyPath] = useState(cfg.ssh.key_path ?? ""); -00292| const [hostPref, setHostPref] = useState(cfg.host_preference.join(", ")); -00293| -00294| const handleSave = async () => { -00295| setSaving(true); -00296| try { -00297| await machines.updateSettings({ -00298| source: "aws-ec2", -00299| enable: true, -00300| aws: { -00301| ...cfg, -00302| region: region || null, -00303| ssh: { -00304| user: sshUser || "root", -00305| port: sshPort || 22, -00306| key_path: sshKeyPath || null, -00307| }, -00308| host_preference: hostPref.split(",").map((s) => s.trim()).filter(Boolean), -00309| }, -00310| }); - -(File has more lines. Use 'offset' parameter to read beyond line 310) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 7.0s) - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "offset": 356, - "limit": 30 -} -``` -**Output:** -``` - -00357| -00358|
-00359| -00360| setSshPort(Number.parseInt(e.target.value, 10) || 22)} -00364| /> -00365|
-00366|
-00367| -00368| setSshKeyPath(e.target.value)} -00372| /> -00373|
-00374| -00375| -00376| -00377| -00381| -00382| -00383| ); -00384| } -00385| -00386| // ============================================================================= - -(File has more lines. Use 'offset' parameter to read beyond line 386) -
-``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.9s) - -Now I'll add the EC2 provisioning list component and wire it into the panel: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "// =============================================================================\n// Main Component\n// =============================================================================", - "newString": "// =============================================================================\n// Provisioning: EC2 Instance List\n// =============================================================================\n\nfunction Ec2AppRow({\n\tappId,\n\tapp,\n\tonRemove,\n}: {\n\tappId: string;\n\tapp: Ec2AppConfig;\n\tonRemove: () => void;\n}) {\n\tconst [removing, setRemoving] = useState(false);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{appId}

\n\t\t\t\t\t

\n\t\t\t\t\t\t{app.instance_count} x {app.instance_type ?? \"t3.micro\"} · {app.os_type}\n\t\t\t\t\t\t{app.machine.roles.length > 0 && ` · roles: ${app.machine.roles.join(\", \")}`}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{app.security_group.create && (\n\t\t\t\t\tSG\n\t\t\t\t)}\n\t\t\t\t{app.iam.enable && (\n\t\t\t\t\tIAM\n\t\t\t\t)}\n\t\t\t\t{app.key_pair.create && (\n\t\t\t\t\tKey\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t{app.associate_public_ip ? \"public\" : \"private\"}\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsetRemoving(true);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait onRemove();\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tsetRemoving(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t\tdisabled={removing}\n\t\t\t\t>\n\t\t\t\t\t{removing ? (\n\t\t\t\t\t\t\n\t\t\t\t\t) : (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t);\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [60:2] Identifier 'useEc2Provisioning' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -Good, now wire the hook and Provision tab into the DeployPanel component: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\tconst machinesConfig = useMachinesConfig();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);", - "newString": "\tconst machinesConfig = useMachinesConfig();\n\tconst ec2Provisioning = useEc2Provisioning();\n\tconst [isRefreshing, setIsRefreshing] = useState(false);" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. -ERROR [461:8] Variable 'ec2Provisioning' is declared but never used. Unused variables should start with a '_'. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 15.2s) - -Now add the Provision tab alongside the other tabs. Let me find the tabs section: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t", - "newString": "\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tMachines\n\t\t\t\t\t\t{machineCount > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{machineCount}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tProvision\n\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tApp Targets\n\t\t\t\t\t\t{appDeployEntries.length > 0 && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{appDeployEntries.length}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t\n\t\t\t\t\tActions\n\t\t\t\t\tSettings\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. - -LSP errors detected in this file, please fix: - -ERROR [57:10] Identifier 'ProvisionInstanceDialog' is imported but never used. -help: Consider removing this import. -ERROR [390:10] Function 'Ec2AppRow' is declared but never used. -help: Consider removing this declaration. - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.8s) - -Now add the Provision tab content. Let me find the right place -- after the Machines tab content: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/web/src/components/studio/panels/deploy/deploy-panel.tsx", - "oldString": "\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t", - "newString": "\t\t\t\t{/* Provision Tab */}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

EC2 Instances to Provision

\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\tDefine instance groups here, then run infra:deploy to create them.\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{Object.keys(ec2Provisioning.config.apps).length === 0 ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

No Instances Configured

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tClick Provision EC2 to define instance groups.\n\t\t\t\t\t\t\t\t\t\tEach group gets its own security group, IAM role, and optional key pair.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{Object.entries(ec2Provisioning.config.apps).map(([id, app]) => (\n\t\t\t\t\t\t\t\t ec2Provisioning.removeApp(id)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\tRun infra:deploy to\n\t\t\t\t\t\t\t\t\t\tprovision these instances. After provisioning, run{\" \"}\n\t\t\t\t\t\t\t\t\t\tinfra:pull-outputs and\n\t\t\t\t\t\t\t\t\t\treload the shell to see them in the Machines tab.\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\n\t\t\t\t{/* App Targets Tab */}\n\t\t\t\t" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 16.1s) - -No LSP errors in our files. Let me verify: - -``` -Tool: bash - -**Input:** -```json -{ - "command": "cd apps/web && npx tsc --noEmit --pretty 2>&1 | rg \"deploy/\" | head -20", - "workdir": "/Users/cm/git/darkmatter/stack", - "timeout": 60000, - "description": "Typecheck deploy panel components" -} -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 8.9s) - -Zero type errors in our deploy files. Now update the docs: - -``` -Tool: read - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "offset": 230, - "limit": 30 -} -``` -**Output:** -``` - -00231| ssh = { -00232| user = "ec2-user"; -00233| port = 22; -00234| keyPath = "~/.ssh/aws_deploy"; -00235| }; -00236| }; -00237| ``` -00238| -00239| ### Filtering by Instance IDs -00240| -00241| Target specific instances instead of using filters: -00242| -00243| ```nix -00244| stack.infra.machines.aws = { -00245| instance-ids = [ -00246| "i-0abc123def456789a" -00247| "i-0def456789abc1230" -00248| ]; -00249| }; -00250| ``` -00251| -00252| ## Studio UI: Deploy Panel -00253| -00254| The Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs: -00255| -00256| ### Machines Tab -00257| -00258| Displays a card grid of all machines in your inventory. Each card shows: -00259| -00260| - Machine name and hostname - -(File has more lines. Use 'offset' parameter to read beyond line 260) - -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 22.9s) - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has four tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:", - "newString": "## Provisioning EC2 Instances\n\nBeyond discovering existing machines, Stack can **provision new EC2 instances** via the `aws-ec2-app` infra module. This creates instances with auto-resolved AMIs (Ubuntu 24.04 or NixOS), security groups, IAM roles, and key pairs.\n\n### From the Studio UI\n\n1. Open the **Deploy** panel and go to the **Provision** tab\n2. Click **Provision EC2** to open the configuration dialog\n3. Fill in the instance group: app ID, count, instance type, OS, networking, SSH key, IAM\n4. Click **Add to Config** -- this writes to your Nix config\n5. Run `infra:deploy` from the terminal to create the instances\n6. Run `infra:pull-outputs` and reload the shell to see them in the Machines tab\n\n### From Nix Config\n\n```nix\nstackpanel.infra.aws-ec2-app = {\n enable = true;\n\n apps = {\n web-server = {\n instance-count = 2;\n instance-type = \"t3.small\";\n os-type = \"ubuntu\"; # or \"nixos\"\n associate-public-ip = true;\n\n # Auto-creates a security group with SSH + HTTP + HTTPS\n security-group.create = true;\n\n # Auto-creates IAM role with SSM + ECR access\n iam.enable = true;\n\n # Import your SSH public key\n key-pair = {\n create = true;\n public-key = \"ssh-ed25519 AAAA...\";\n };\n\n # Machine metadata for Colmena targeting\n machine = {\n roles = [ \"web\" ];\n tags = [ \"production\" ];\n target-env = \"production\";\n };\n };\n };\n};\n```\n\nThen deploy:\n\n```bash\ninfra:deploy\ninfra:pull-outputs\nexit && nix develop --impure\n```\n\nThe provisioned instances automatically appear in your machine inventory with their public IPs, and can be targeted by Colmena for NixOS deployments.\n\n### What Gets Created\n\nFor each app group, `aws-ec2-app` provisions:\n\n| Resource | Created when |\n|----------|-------------|\n| Security Group | `security-group.create = true` (default) |\n| SSH Key Pair | `key-pair.create = true` |\n| IAM Role + Instance Profile | `iam.enable = true` (default) |\n| EC2 Instances | Always (count from `instance-count`) |\n| ALB + Target Group | `alb.enable = true` |\n| ECR Repository | `ecr.enable = true` |\n| SSM Parameters | `ssm.enable = true` |\n\nVPC and subnets are auto-discovered from your default VPC unless you specify `vpc-id` and `subnet-ids` explicitly.\n\n\nThe `aws-ec2-app` module auto-resolves AMIs. For Ubuntu, it finds the latest Ubuntu 24.04 LTS from Canonical. For NixOS, it finds the latest Determinate Systems NixOS image. You can override with an explicit `ami` value.\n\n\n## Studio UI: Deploy Panel\n\nThe Studio UI's **Deploy** panel (`/studio/deploy`) provides a visual interface for your machine inventory. It has five tabs:\n\n### Machines Tab\n\nDisplays a card grid of all machines in your inventory. Each card shows:" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 12.8s) - -Now update the rest of the Deploy panel docs section to reflect the new Provision tab: - -``` -Tool: edit - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/apps/docs/content/docs/deployment/machines.mdx", - "oldString": "When the inventory is empty, it shows guidance:\n\n> Run `infra:deploy` and `infra:pull-outputs` to populate it, then reload the shell.\n\n### App Targets Tab", - "newString": "Click a machine card to edit its configuration. Use the **Add Machine** button in the header to add static machines.\n\nWhen the inventory is empty, it shows guidance to add machines or configure EC2 discovery.\n\n### Provision Tab\n\nConfigure EC2 instances to create. Each row shows the instance group with its count, type, OS, and what resources will be created (SG, IAM, Key). Click **Provision EC2** to define a new group. After configuring, run `infra:deploy` to create the actual instances.\n\n### App Targets Tab" -} -``` -**Output:** -``` -Edit applied successfully. -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 11.5s) - -``` -Tool: todowrite - -**Input:** -```json -{ - "todos": [ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } - ] -} -``` -**Output:** -``` -[ - { - "id": "1", - "content": "Add aws-ec2 provisioning types and config to use-machines.ts hook", - "status": "completed", - "priority": "high" - }, - { - "id": "2", - "content": "Build ProvisionInstanceForm component for EC2 instance definition", - "status": "completed", - "priority": "high" - }, - { - "id": "3", - "content": "Build AddInstanceDialog for adding EC2 instances to provision", - "status": "completed", - "priority": "high" - }, - { - "id": "4", - "content": "Add Provisioning tab to deploy panel with instance list + deploy button", - "status": "completed", - "priority": "high" - }, - { - "id": "5", - "content": "Update docs to cover provisioning from Studio UI", - "status": "completed", - "priority": "medium" - } -] -``` -``` - ---- - -## Assistant (Build · claude-opus-4-6 · 17.6s) - -Here's a summary of everything that was added: - -## How to Provision Machines - -The Deploy panel now has a **Provision** tab (between Machines and App Targets). The flow is: - -1. **Deploy panel -> Provision tab -> "Provision EC2" button** opens a dialog where you configure: - - App ID (instance group name) - - Count, instance type, OS (Ubuntu 24.04 / NixOS) - - Networking: VPC, subnets (auto-detected if blank), public IP toggle, auto-created security group - - Storage: root volume size - - SSH: optionally create + import a key pair - - IAM: auto-create role with SSM + ECR access - - Machine metadata: roles, tags, environment (for Colmena targeting) - -2. **"Add to Config"** writes to `.stack/config.nix` under `stack.infra.aws-ec2-app` - -3. **`infra:deploy`** in terminal provisions the actual EC2 instances (with SG, IAM, key pair) - -4. **`infra:pull-outputs`** + shell reload populates the Machines tab with the new instances - -## New/Updated Files - -| File | What | -|------|------| -| `deploy/use-machines.ts` | Added `Ec2AppConfig`, `Ec2AppModuleConfig` types + `useEc2Provisioning()` hook for CRUD on `infra.aws-ec2-app` | -| `deploy/provision-instance-dialog.tsx` | New dialog for configuring EC2 instance groups | -| `deploy/deploy-panel.tsx` | Added **Provision** tab with `Ec2AppRow` list, wired `ProvisionInstanceDialog` + `useEc2Provisioning` | -| `docs/deployment/machines.mdx` | Added "Provisioning EC2 Instances" section covering both Studio UI and Nix config approaches, plus the what-gets-created table | - -Zero type errors across all new files (verified with `tsc --noEmit`). - ---- - -## User - -the machine provisioning is great. but we should create some preset based on the user's apps, since it asks for thihngs like roles, environments, number of machines, etc. we could generate buttons that would fill in sane defaults for provisioning - ---- - -## Assistant (Build · claude-opus-4-6) - -_Thinking:_ - -The user wants preset buttons that auto-fill the provisioning dialog based on their configured apps. For example, if they have a `web` app and a `server` app, we could generate presets like "Web Server (2x t3.small)" or "API Server (1x t3.micro)" that pre-fill the form with sensible defaults. - -Let me look at how apps are structured in the nix config to understand what data we have available, then build presets from that. - -``` -Tool: task - -**Input:** -```json -{ - "description": "Explore app config shapes for presets", - "prompt": "I need to understand what app data is available in the Studio UI to generate EC2 provisioning presets. Search for:\n\n1. The MachineInfo type and AppDeployMapping type in deploy-panel.tsx - what fields are available\n2. How apps are loaded in the deploy panel - look at useColmenaData and useNixConfig\n3. The app type definitions - what fields does an app have (framework, deployment.host, container config, etc.)\n4. Look at .stack/config.nix apps section to see real app examples with their frameworks, deployment hosts, container types\n5. What app data is available via useNixConfig() - specifically the apps/appsComputed shape\n\nReturn:\n- The full app type shape available from nixConfig (framework, deployment, container fields)\n- The real app configs from .stack/config.nix\n- How to access the app list from within the deploy panel", - "subagent_type": "explore" -} -``` -``` - ---- - diff --git a/docs/sess-infra.md b/docs/sess-infra.md deleted file mode 100644 index 585d06a6..00000000 --- a/docs/sess-infra.md +++ /dev/null @@ -1,7702 +0,0 @@ -# Nix develop: stack package merge and infra catalog error - -**Session ID:** ses_3522ddf88ffeQLC51HskErOGjv -**Created:** 3/2/2026, 1:12:35 AM -**Updated:** 3/2/2026, 1:52:13 AM - ---- - -## User - -whats causing this nix develop -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -error: - … while evaluating the attribute 'packages' - at /Users/cm/git/darkmatter/stack/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /Users/cm/git/darkmatter/stack/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the option `stack.outputs': - - … while evaluating definitions from `/nix/store/5kkls2qggxhsxmji0nr3h765fqg4hy1x-source/nix/stack/devshell/scripts.nix': - - … while evaluating the option `stack.scripts': - - … while evaluating definitions from `/nix/store/5kkls2qggxhsxmji0nr3h765fqg4hy1x-source/nix/stack/files': - - … while evaluating the option `stack.files.entries': - - … while evaluating definitions from `/nix/store/5kkls2qggxhsxmji0nr3h765fqg4hy1x-source/nix/stack/modules/bun/catalog.nix': - - … while evaluating the option `stack.bun.catalog': - - … while evaluating definitions from `/nix/store/5kkls2qggxhsxmji0nr3h765fqg4hy1x-source/nix/stack/infra/codegen.nix': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: infra module dependency 'alchemy' uses catalog: but has no version in infraCatalogVersions — add it to infra/codegen.nix - ---- - -## Assistant (Build · gpt-5.3-codex · 5.3s) - -_Thinking:_ - -**Inspecting version map and dependencies** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "infraCatalogVersions", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 3 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 45: infraCatalogVersions = { - Line 378: infraCatalogVersions.${name} - Line 379: or (builtins.throw "infra module dependency '${name}' uses catalog: but has no version in infraCatalogVersions — add it to infra/codegen.nix") -``` - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "catalog:", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 39 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/feature-flags/module.nix: - Line 26: # These must stay in sync with the "catalog:" references in - Line 114: # Catalog entries — register actual versions for "catalog:" references - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix: - Line 8: # Modules declare dependencies as `"@aws-sdk/client-ecr" = "catalog:";` in - Line 81: real version, then reference it as `"catalog:"` in their workspace - -/Users/cm/git/darkmatter/stack/nix/stack/sst/sst.nix: - Line 468: sst = "catalog:"; - Line 469: "@pulumi/aws" = "catalog:"; - Line 641: # Bun catalog — register actual versions for "catalog:" references - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 283: alchemy = "catalog:"; - Line 287: "@aws-sdk/client-ssm" = "catalog:"; - Line 302: # Bun catalog — register actual versions for "catalog:" references - -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 36: # Catalog versions for NPM packages used by infra modules via "catalog:". - Line 39: # 1. Module dependencies reference "catalog:" (workspace dedup) - Line 372: # Collect catalog versions for every "catalog:" dependency across all modules. - Line 373: # Only includes deps whose value is literally "catalog:" — direct version - Line 375: allCatalogDeps = lib.filterAttrs (_: v: v == "catalog:") allDeps; - Line 379: or (builtins.throw "infra module dependency '${name}' uses catalog: but has no version in infraCatalogVersions — add it to infra/codegen.nix") - Line 671: # Bun catalog — register actual versions for all "catalog:" references - Line 682: alchemy = "catalog:"; - Line 795: # "@aws-sdk/client-s3" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix: - Line 291: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix: - Line 129: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix: - Line 262: "@aws-sdk/client-sts" = "catalog:"; # AccountId from alchemy/aws - Line 263: "@aws-sdk/client-iam" = "catalog:"; # Role, GitHubOIDCProvider - Line 264: "@aws-sdk/client-kms" = "catalog:"; # KmsKey, KmsAlias - Line 265: "@aws-sdk/client-ssm" = "catalog:"; # SSM for group key storage - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix: - Line 67: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix: - Line 85: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix: - Line 109: "@aws-sdk/client-iam" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix: - Line 187: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix: - Line 890: "@aws-sdk/client-ec2" = "catalog:"; - Line 891: "@aws-sdk/client-iam" = "catalog:"; - Line 892: "@aws-sdk/client-elastic-load-balancing-v2" = "catalog:"; - Line 893: "@aws-sdk/client-ecr" = "catalog:"; - Line 894: "@aws-sdk/client-ssm" = "catalog:"; - Line 895: "@aws-sdk/client-sts" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix: - Line 241: "alchemy" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix: - Line 177: "alchemy" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix: - Line 11: # dependencies = { alchemy = "catalog:"; }; - Line 257: dependencies = { alchemy = "catalog:"; }; -``` - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "alchemy", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 164 matches (showing first 100) -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix: - Line 91: "alchemy" = "^0.81.2"; - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 2: # alchemy/codegen.nix - Line 4: # Code generation engine for the @gen/alchemy shared package. - Line 15: # - Registers .alchemy in .gitignore - Line 17: # - Registers alchemy:setup and deploy scripts (if deploy enabled) - Line 27: cfg = config.stack.alchemy; - Line 68: * during alchemy provisioning. - Line 100: * Resolve environment variable bindings, wrapping secrets with alchemy.secret(). - Line 103: * with alchemy.secret() for safe handling in Cloudflare Workers bindings. - Line 127: ? alchemy.secret(value ?? "") - Line 283: alchemy = "catalog:"; - Line 305: alchemy = cfg.version; - Line 318: description = "Alchemy app factory and re-exports (@gen/alchemy)"; - Line 319: source = "alchemy"; - Line 327: source = "alchemy"; - Line 334: description = "Shared alchemy helpers (SSM, bindings, port)"; - Line 335: source = "alchemy"; - Line 343: description = "TypeScript configuration for @gen/alchemy"; - Line 344: source = "alchemy"; - Line 349: ".alchemy" - Line 358: source = "alchemy"; - Line 365: stack.turbo.packages.alchemy = { - Line 397: "alchemy:setup" = { - Line 414: description = "Deploy via alchemy (auto-runs setup if Cloudflare is not configured)"; - Line 422: description = "Additional alchemy deploy arguments (after --)"; - Line 437: name = "alchemy:setup"; - Line 447: stack.modules.alchemy = { - Line 451: description = "Shared Alchemy IaC configuration, deploy scripts, and helpers (@gen/alchemy)"; - Line 470: "alchemy" - Line 476: priority = 10; # Load very early -- other modules depend on alchemy config - Line 482: stack.serializable.alchemy = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 9: # - src/resources/*.ts (custom alchemy resources: KMS Key, KMS Alias) - Line 11: # - alchemy.run.ts (orchestrator) - Line 278: # Generated: alchemy.run.ts (orchestrator) - Line 280: # Uses @gen/alchemy for app creation (shared state store config) instead - Line 281: # of inline alchemy boilerplate. - Line 283: alchemyRunTs = - Line 314: # Use @gen/alchemy if the alchemy module is enabled, otherwise fall back - Line 315: # to inline alchemy initialization for backward compatibility. - Line 316: alchemyCfg = config.stack.alchemy; - Line 317: useGenAlchemy = alchemyCfg.enable; - Line 322: import { createApp } from "@gen/alchemy"; - Line 340: import alchemy from "alchemy"; - Line 341: import { CloudflareStateStore } from "alchemy/state"; - Line 344: const app = await alchemy("${projectName}-infra", { - Line 348: apiToken: alchemy.secret(process.env.CLOUDFLARE_API_TOKEN!), - Line 427: Infrastructure-as-code package for **${projectName}**, powered by [Alchemy](https://github.com/sam-goodwin/alchemy). - Line 437: cd ${outputDir} && bun run alchemy.run.ts - Line 440: cd ${outputDir} && bun run alchemy.run.ts --destroy - Line 451: ├── alchemy.run.ts # Orchestrator (imports all modules, syncs outputs) - Line 482: "alchemy.run.ts" - Line 487: ".alchemy" - Line 554: # Custom alchemy resources - Line 558: description = "Custom KMS Key alchemy resource"; - Line 565: description = "Custom KMS Alias alchemy resource"; - Line 572: description = "Custom IAM Role alchemy resource"; - Line 578: description = "Custom Security Group alchemy resource"; - Line 584: description = "Custom Key Pair alchemy resource"; - Line 590: description = "Custom IAM Instance Profile alchemy resource"; - Line 596: description = "Custom EC2 Instance alchemy resource"; - Line 602: description = "Custom ALB alchemy resource"; - Line 608: description = "Custom Target Group alchemy resource"; - Line 614: description = "Custom ALB Listener alchemy resource"; - Line 620: description = "Custom ALB Listener Rule alchemy resource"; - Line 626: description = "Custom Target Group Attachment alchemy resource"; - Line 632: description = "Custom ECR Repository alchemy resource"; - Line 637: "${outputDir}/alchemy.run.ts" = { - Line 638: text = alchemyRunTs; - Line 682: alchemy = "catalog:"; - Line 684: // lib.optionalAttrs config.stack.alchemy.enable { - Line 685: ${config.stack.alchemy.package.name} = "workspace:*"; - Line 701: "alchemy:deploy" = { - Line 702: exec = "alchemy deploy"; - Line 708: "alchemy:destroy" = { - Line 709: exec = "alchemy destroy"; - Line 715: "alchemy:dev" = { - Line 716: exec = "alchemy dev"; - Line 846: // Or use alchemy built-in resources: - Line 848: // import { Role } from "alchemy/aws"; - Line 898: cd "${outputDir}" && bunx alchemy deploy "$@" - Line 900: description = "Deploy infrastructure via alchemy (aws-vault integration if enabled)"; - Line 908: description = "Additional alchemy deploy arguments"; - Line 922: cd "${outputDir}" && bunx alchemy destroy "$@" - Line 924: description = "Destroy infrastructure via alchemy (aws-vault integration if enabled)"; - Line 932: description = "Additional alchemy destroy arguments"; - Line 939: cd "${outputDir}" && bunx alchemy dev "$@" - Line 945: description = "Additional alchemy dev arguments"; - Line 1126: "alchemy" - -/Users/cm/git/darkmatter/stack/.stack/config.nix: - Line 726: "alchemy:ensure" = { - Line 727: description = "Ensure alchemy is initialized - builds will error without this if using alchemy for deployments. creates wrangler.jsonc when using @cloudflare/vite-plugin"; - Line 729: exec = "test -f .alchemy/local/wrangler.jsonc || (mkdir -p .alchemy/local && echo '{\"name\":\"web\",\"main\":\".alchemy/local/worker.js\",\"compatibility_date\":\"2025-01-01\",\"assets\":{\"directory\":\"dist\"}}' > .alchemy/local/wrangler.jsonc)"; - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/default.nix: - Line 2: # alchemy/default.nix - Line 6: # Provides centralized alchemy SDK configuration consumed by other modules - Line 10: # - A generated shared TypeScript package (@gen/alchemy) with: - Line 15: # - .gitignore for .alchemy directories - Line 18: # stack.alchemy = { - Line 24: # config.stack.alchemy.version (npm version) - Line 25: # config.stack.alchemy.package.* (generated package config) - Line 26: # config.stack.alchemy.deploy.* (setup + deploy scripts) - Line 27: # @gen/alchemy (TypeScript import) - -(Results truncated: showing 100 of 164 matches (64 hidden). Consider using a more specific path or pattern.) -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - -**Inspecting infrastructure codegen map** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 1, - "limit": 140 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -1: # ============================================================================== -2: # infra/codegen.nix -3: # -4: # Code generation engine for the infrastructure module system. -5: # -6: # Generates via stack.files.entries: -7: # - src/index.ts (Infra class with embedded project config) -8: # - src/types.ts (per-module input TypeScript interfaces) -9: # - src/resources/*.ts (custom alchemy resources: KMS Key, KMS Alias) -10: # - modules/.ts (copied from module path attributes) -11: # - alchemy.run.ts (orchestrator) -12: # - package.json (union of all module dependencies) -13: # - tsconfig.json (TypeScript config) -14: # -15: # Also: -16: # - Writes infra-inputs.json to state dir -17: # - Sets STACKPANEL_INFRA_INPUTS env var -18: # - Registers scripts (infra:deploy, infra:destroy, etc.) -19: # - Registers as a stack module for UI -20: # - Serializes config for the agent -21: # ============================================================================== -22: { -23: lib, -24: config, -25: pkgs, -26: ... -27: }: -28: let -29: cfg = config.stack.infra; -30: projectName = config.stack.name or "my-project"; -31: outputDir = cfg.output-dir; -32: stateDir = config.stack.dirs.state; -33: moduleIds = builtins.attrNames cfg.modules; -34: -35: # ============================================================================ -36: # Catalog versions for NPM packages used by infra modules via "catalog:". -37: # -38: # Defined once here so that: -39: # 1. Module dependencies reference "catalog:" (workspace dedup) -40: # 2. stack.bun.catalog gets the real version (root package.json) -41: # -42: # When adding a new AWS SDK or other catalog dependency to an infra module, -43: # add the version here too. -44: # ============================================================================ -45: infraCatalogVersions = { -46: "@aws-sdk/client-ec2" = "^3.953.0"; -47: "@aws-sdk/client-ecr" = "^3.953.0"; -48: "@aws-sdk/client-elastic-load-balancing-v2" = "^3.953.0"; -49: "@aws-sdk/client-iam" = "^3.953.0"; -50: "@aws-sdk/client-kms" = "^3.953.0"; -51: "@aws-sdk/client-ssm" = "^3.953.0"; -52: "@aws-sdk/client-sts" = "^3.953.0"; -53: "@pulumi/aws" = "^7.15.0"; -54: "sst" = "^3.17.25"; -55: }; -56: sortedModuleIds = lib.sort (a: b: a < b) moduleIds; -57: -58: modulePathIsDirectory = -59: id: -60: let -61: modPath = cfg.modules.${id}.path; -62: in -63: builtins.pathExists (modPath + "/index.ts"); -64: -65: moduleImportPath = -66: id: if modulePathIsDirectory id then "./modules/${id}/index.ts" else "./modules/${id}.ts"; -67: -68: # ============================================================================ -69: # Module bunDeps handling -70: # -71: # External flakes can provide pre-validated bunDeps. When provided, -72: # dependencies are already validated at the flake's eval time. -73: # ============================================================================ -74: -75: # Modules that provide their own bunDeps -76: modulesWithBunDeps = lib.filterAttrs (_: mod: mod.bunDeps != null) cfg.modules; -77: -78: # Modules without bunDeps (deps need runtime or package-level validation) -79: modulesWithoutBunDeps = lib.filterAttrs (_: mod: mod.bunDeps == null) cfg.modules; -80: -81: # All module bunDeps as a list -82: allModuleBunDeps = lib.mapAttrsToList (_: mod: mod.bunDeps) modulesWithBunDeps; -83: -84: # Check if all modules provide bunDeps (fully validated at flake level) -85: allModulesHaveBunDeps = -86: builtins.length (builtins.attrNames modulesWithoutBunDeps) == 0 && builtins.length moduleIds > 0; -87: -88: # Check if any module provides bunDeps -89: anyModuleHasBunDeps = builtins.length (builtins.attrNames modulesWithBunDeps) > 0; -90: -91: # ============================================================================ -92: # Inputs JSON (written to state dir, read by Infra class at runtime) -93: # ============================================================================ -94: storageBackendConfig = -95: if cfg.storage-backend.type == "chamber" then -96: { -97: type = "chamber"; -98: service = cfg.storage-backend.chamber.service; -99: } -100: else if cfg.storage-backend.type == "sops" then -101: let -102: secretsDir = config.stack.secrets.secrets-dir or ".stack/secrets"; -103: group = cfg.storage-backend.sops.group; -104: resolvedPath = "${secretsDir}/vars/${group}.sops.yaml"; -105: in -106: { -107: type = "sops"; -108: filePath = resolvedPath; -109: inherit group; -110: } -111: else if cfg.storage-backend.type == "ssm" then -112: { -113: type = "ssm"; -114: prefix = cfg.storage-backend.ssm.prefix; -115: } -116: else -117: { -118: type = "none"; -119: }; -120: -121: inputsJson = { -122: __config__ = { -123: storageBackend = storageBackendConfig; -124: keyFormat = cfg.key-format; -125: inherit projectName; -126: }; -127: } -128: // lib.mapAttrs (_id: mod: mod.inputs) cfg.modules; -129: -130: inputsJsonStr = builtins.toJSON inputsJson; -131: -132: # ============================================================================ -133: # Best-effort TypeScript type inference from Nix values -134: # ============================================================================ -135: nixTypeToTs = -136: value: -137: if builtins.isBool value then -138: "boolean" -139: else if builtins.isInt value || builtins.isFloat value then -140: "number" - -(Showing lines 1-140 of 1179. Use offset=141 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.2s) - -_Thinking:_ - -**Verifying alchemy dependency presence** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix", - "offset": 140, - "limit": 80 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix -file -140: -141: path-prefix = lib.mkOption { -142: type = lib.types.str; -143: default = "/${projectName}"; -144: description = "SSM path prefix (stage is appended)"; -145: }; -146: }; -147: }; -148: -149: # ============================================================================ -150: # Config: register in infra module system -151: # ============================================================================ -152: config = lib.mkIf (infraCfg.enable && cfg.enable) { -153: stack.infra.modules.cache = { -154: name = "Cache"; -155: description = "Redis/Valkey cache provisioning (Upstash / devenv / Docker)"; -156: path = ./index.ts; -157: inputs = { -158: inherit projectName; -159: inherit (cfg) provider; -160: upstash = { -161: inherit (cfg.upstash) region; -162: apiKeySsmPath = cfg.upstash.api-key-ssm-path; -163: emailSsmPath = cfg.upstash.email-ssm-path; -164: }; -165: devenv = { -166: inherit (cfg.devenv) host port; -167: }; -168: docker = { -169: inherit (cfg.docker) image tag port network; -170: }; -171: ssm = { -172: inherit (cfg.ssm) enable; -173: pathPrefix = cfg.ssm.path-prefix; -174: }; -175: }; -176: dependencies = { -177: "alchemy" = "catalog:"; -178: } -179: // lib.optionalAttrs (config.stack.alchemy.enable) { -180: ${config.stack.alchemy.package.name} = "workspace:*"; -181: }; -182: outputs = { -183: redisUrl = { -184: description = "Redis connection URL (endpoint)"; -185: sync = true; -186: }; -187: redisToken = { -188: description = "Redis REST token (Upstash only)"; -189: sensitive = true; -190: sync = true; -191: }; -192: provider = { -193: description = "Active cache provider (upstash, devenv, docker)"; -194: sync = true; -195: }; -196: }; -197: }; -198: }; -199: } - -(End of file - total 199 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix", - "offset": 210, - "limit": 70 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix -file -210: }; -211: }; -212: -213: # ============================================================================ -214: # Config: register in infra module system -215: # ============================================================================ -216: config = lib.mkIf (infraCfg.enable && cfg.enable) { -217: stack.infra.modules.database = { -218: name = "Database"; -219: description = "Postgres database provisioning (Neon / devenv / Docker)"; -220: path = ./index.ts; -221: inputs = { -222: inherit projectName; -223: inherit (cfg) name provider; -224: neon = { -225: inherit (cfg.neon) region enable-branching; -226: pgVersion = cfg.neon.pg-version; -227: apiKeySsmPath = cfg.neon.api-key-ssm-path; -228: }; -229: devenv = { -230: inherit (cfg.devenv) database user password host port; -231: }; -232: docker = { -233: inherit (cfg.docker) image tag user password port network; -234: }; -235: ssm = { -236: inherit (cfg.ssm) enable; -237: pathPrefix = cfg.ssm.path-prefix; -238: }; -239: }; -240: dependencies = { -241: "alchemy" = "catalog:"; -242: } -243: // lib.optionalAttrs (config.stack.alchemy.enable) { -244: ${config.stack.alchemy.package.name} = "workspace:*"; -245: }; -246: outputs = { -247: databaseUrl = { -248: description = "PostgreSQL connection URL"; -249: sensitive = true; -250: sync = true; -251: }; -252: provider = { -253: description = "Active database provider (neon, devenv, docker)"; -254: sync = true; -255: }; -256: }; -257: }; -258: }; -259: } - -(End of file - total 259 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.8s) - -_Thinking:_ - -**Analyzing version source and error context** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/flake.nix", - "offset": 70, - "limit": 40 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/flake.nix -file -70: pkgs = import nixpkgs { -71: inherit system overlays; -72: }; -73: -74: # Stack packages (CLI, etc.) -75: packages = import ./nix/flake/packages.nix { inherit pkgs inputs; }; -76: -77: # Stack outputs (devShells, checks, apps, legacyPackages) -78: spOutputs = import ./nix/flake/default.nix { -79: inherit -80: pkgs -81: inputs -82: self -83: system -84: projectRoot -85: ; -86: stackpanelImports = [ ./.stack/modules ]; -87: }; -88: in -89: { -90: # Merge stack packages with outputs packages -91: packages = packages // (spOutputs.packages or { }); -92: -93: # DevShells from stack -94: devShells = spOutputs.devShells; -95: -96: # Checks - include package checks plus stack checks -97: checks = { -98: stack = packages.stack; -99: default-package = packages.default; -100: } -101: // spOutputs.checks; -102: -103: # Apps from stack -104: apps = spOutputs.apps; -105: -106: # Legacy packages for introspection -107: legacyPackages = spOutputs.legacyPackages; -108: } -109: ) - -(Showing lines 70-109 of 119. Use offset=110 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.6s) - -_Thinking:_ - -**Explaining dependency error cause** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 360, - "limit": 30 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -360: -361: await app.finalize(); -362: ''; -363: -364: # ============================================================================ -365: # Module dependency aggregation (used by turbo.packages) -366: # ============================================================================ -367: allDeps = lib.foldlAttrs ( -368: acc: _id: mod: -369: acc // mod.dependencies -370: ) { } cfg.modules; -371: -372: # Collect catalog versions for every "catalog:" dependency across all modules. -373: # Only includes deps whose value is literally "catalog:" — direct version -374: # strings are left alone (they don't need a catalog entry). -375: allCatalogDeps = lib.filterAttrs (_: v: v == "catalog:") allDeps; -376: catalogEntries = lib.mapAttrs ( -377: name: _: -378: infraCatalogVersions.${name} -379: or (builtins.throw "infra module dependency '${name}' uses catalog: but has no version in infraCatalogVersions — add it to infra/codegen.nix") -380: ) allCatalogDeps; -381: -382: # ============================================================================ -383: # README.md — tailored to registered modules -384: # ============================================================================ -385: readmeMd = -386: let -387: moduleSections = lib.concatMapStringsSep "\n" ( -388: id: -389: let - -(Showing lines 360-389 of 1179. Use offset=390 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.1s) - -_Thinking:_ - -**Inspecting map for mismatch** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix", - "offset": 80, - "limit": 20 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix -file -80: Modules that need an NPM dependency should declare it here with the -81: real version, then reference it as `"catalog:"` in their workspace -82: package.json dependencies. -83: -84: Multiple modules can declare the same package. If versions conflict, -85: the Nix module system's standard merge/priority rules apply — use -86: `lib.mkForce` or `lib.mkOverride` to resolve conflicts explicitly. -87: -88: Example: -89: stack.bun.catalog = { -90: "@aws-sdk/client-ecr" = "^3.953.0"; -91: "alchemy" = "^0.81.2"; -92: "react" = "19.2.4"; -93: }; -94: ''; -95: example = lib.literalExpression '' -96: { -97: "@aws-sdk/client-ecr" = "^3.953.0"; -98: "react" = "19.2.4"; -99: "zod" = "^4.1.13"; - -(Showing lines 80-99 of 205. Use offset=100 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.5s) - -_Thinking:_ - -**Investigating alchemy setting source** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix", - "offset": 270, - "limit": 50 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix -file -270: rootDir = "."; -271: }; -272: include = [ "src/**/*.ts" ]; -273: exclude = [ -274: "node_modules" -275: "dist" -276: ]; -277: }; -278: -279: # ============================================================================ -280: # Determine dependencies based on enabled helpers -281: # ============================================================================ -282: baseDeps = { -283: alchemy = "catalog:"; -284: }; -285: -286: helperDeps = lib.optionalAttrs cfg.helpers.ssm { -287: "@aws-sdk/client-ssm" = "catalog:"; -288: }; -289: -290: allDeps = baseDeps // helperDeps // cfg.package.extra-dependencies; -291: -292: # ============================================================================ -293: # Flags for conditional features -294: # ============================================================================ -295: hasSecrets = -296: cfg.secrets.state-token-sops-path != null || cfg.secrets.cloudflare-token-sops-path != null; -297: -298: in -299: { -300: config = lib.mkIf cfg.enable { -301: # ========================================================================== -302: # Bun catalog — register actual versions for "catalog:" references -303: # ========================================================================== -304: stack.bun.catalog = { -305: alchemy = cfg.version; -306: } // lib.optionalAttrs cfg.helpers.ssm { -307: "@aws-sdk/client-ssm" = "^3.953.0"; -308: }; -309: -310: # ========================================================================== -311: # File generation -312: # ========================================================================== -313: stack.files.entries = { -314: # Main entry: createApp factory + re-exports -315: "${outputDir}/src/index.ts" = { -316: text = indexTs; -317: mode = "0644"; -318: description = "Alchemy app factory and re-exports (@gen/alchemy)"; -319: source = "alchemy"; - -(Showing lines 270-319 of 513. Use offset=320 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 1.9s) - -_Thinking:_ - -**Inspecting stack catalog setting** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 650, - "limit": 50 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -650: source = "infra"; -651: }; -652: -653: # Inputs JSON (state file) -654: "${stateDir}/infra-inputs.json" = { -655: text = inputsJsonStr; -656: mode = "0600"; # restricted — may contain sensitive config -657: description = "Serialized infra module inputs"; -658: source = "infra"; -659: }; -660: } -661: // moduleFileEntries; -662: -663: # ========================================================================== -664: # Devshell environment -665: # ========================================================================== -666: stack.devshell.env = { -667: STACKPANEL_INFRA_INPUTS = "${stateDir}/infra-inputs.json"; -668: }; -669: -670: # ========================================================================== -671: # Bun catalog — register actual versions for all "catalog:" references -672: # ========================================================================== -673: stack.bun.catalog = catalogEntries; -674: -675: # ========================================================================== -676: # Turbo workspace package (generates package.json + turbo.json tasks) -677: # ========================================================================== -678: stack.turbo.packages.infra = { -679: name = cfg.package.name; -680: path = outputDir; -681: dependencies = { -682: alchemy = "catalog:"; -683: } -684: // lib.optionalAttrs config.stack.alchemy.enable { -685: ${config.stack.alchemy.package.name} = "workspace:*"; -686: } -687: // allDeps -688: // cfg.package.dependencies; -689: devDependencies = { -690: bun2nix = "latest"; -691: }; -692: exports = { -693: "." = { -694: default = "./src/index.ts"; -695: }; -696: "./*" = { -697: default = "./src/*.ts"; -698: }; -699: }; - -(Showing lines 650-699 of 1179. Use offset=700 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 18.3s) - -_Thinking:_ - -**Diagnosing missing alchemy version in infra catalog** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/default.nix", - "offset": 1, - "limit": 120 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/default.nix -file -1: # ============================================================================== -2: # alchemy/default.nix -3: # -4: # Lower-level primitive module for Alchemy IaC configuration. -5: # -6: # Provides centralized alchemy SDK configuration consumed by other modules -7: # (infra/, deployment/, etc.): -8: # - Alchemy version management -9: # - State store configuration (Cloudflare, filesystem, auto) -10: # - A generated shared TypeScript package (@gen/alchemy) with: -11: # - createApp() factory function -12: # - State store provider -13: # - Shared helpers (SSM reads, secret wrapping, binding resolution) -14: # - ALCHEMY_STATE_TOKEN secret management -15: # - .gitignore for .alchemy directories -16: # -17: # Usage: -18: # stack.alchemy = { -19: # enable = true; -20: # stateStore.provider = "auto"; -21: # }; -22: # -23: # Other modules consume this via: -24: # config.stack.alchemy.version (npm version) -25: # config.stack.alchemy.package.* (generated package config) -26: # config.stack.alchemy.deploy.* (setup + deploy scripts) -27: # @gen/alchemy (TypeScript import) -28: # ============================================================================== -29: { ... }: -30: { -31: imports = [ -32: ./options.nix -33: ./codegen.nix -34: ]; -35: } - -(End of file - total 35 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 1.7s) - -_Thinking:_ - -**Considering default options file version** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "version", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 10 matches -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 302: # Bun catalog — register actual versions for "catalog:" references - Line 305: alchemy = cfg.version; - Line 455: version = "1.0.0"; - Line 485: version - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/default.nix: - Line 8: # - Alchemy version management - Line 24: # config.stack.alchemy.version (npm version) - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/options.nix: - Line 8: # - stack.alchemy.version (npm version constraint) - Line 36: version = lib.mkOption { - Line 40: Alchemy npm version constraint. - Line 42: Other modules should reference this instead of hardcoding a version. -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.3s) - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/options.nix", - "offset": 30, - "limit": 30 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/options.nix -file -30: enable = lib.mkOption { -31: type = lib.types.bool; -32: default = false; -33: description = "Enable the Alchemy IaC module (generates @gen/alchemy shared package)"; -34: }; -35: -36: version = lib.mkOption { -37: type = lib.types.str; -38: default = "^0.81.2"; -39: description = '' -40: Alchemy npm version constraint. -41: Used in the generated package.json and catalog. -42: Other modules should reference this instead of hardcoding a version. -43: ''; -44: }; -45: -46: # ========================================================================== -47: # State Store -48: # ========================================================================== -49: state-store = { -50: provider = lib.mkOption { -51: type = lib.types.enum [ -52: "cloudflare" -53: "filesystem" -54: "auto" -55: ]; -56: default = "auto"; -57: description = '' -58: State store provider for alchemy. -59: - -(Showing lines 30-59 of 273. Use offset=60 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 12.1s) - -_Thinking:_ - -**Summarizing module dependency issue** - -You’ve got two separate things there: - -- `warning: Git tree ... has uncommitted changes` is just a warning from flakes (dirty repo), not the failure. -- The real failure is a catalog version validation error in `nix/stack/infra/codegen.nix:379`. - -What’s happening: - -- Infra modules declare dependencies as `"catalog:"` (for dedup), e.g. `nix/stack/infra/modules/cache/module.nix:177` and `nix/stack/infra/modules/database/module.nix:241` both declare `alchemy = "catalog:"`. -- `nix/stack/infra/codegen.nix` then resolves every `"catalog:"` dep via `infraCatalogVersions` (`nix/stack/infra/codegen.nix:45`). -- That map is missing an `alchemy` entry, so it throws intentionally at `nix/stack/infra/codegen.nix:379`: - `infra module dependency 'alchemy' uses catalog: but has no version...` - -So the root cause is: **`alchemy` is referenced as catalog in infra module deps, but not registered in `infraCatalogVersions`.** -That bubbles up and shows near `flake.nix:91` while merging outputs. - -Quick fix: add `alchemy` to `infraCatalogVersions` (preferably using `config.stack.alchemy.version`). - ---- - -## User - -nix develop -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -error: - … while evaluating the attribute 'packages' - at /Users/cm/git/darkmatter/stack/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /Users/cm/git/darkmatter/stack/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the option `stack.outputs': - - … while evaluating definitions from `/nix/store/67x4qil6i9skf56fw2q9py3qgg02xbsi-source/nix/stack/devshell/scripts.nix': - - … while evaluating the option `stack.scripts': - - … while evaluating definitions from `/nix/store/67x4qil6i9skf56fw2q9py3qgg02xbsi-source/nix/stack/files': - - … while evaluating the option `stack.files.entries': - - … while evaluating definitions from `/nix/store/67x4qil6i9skf56fw2q9py3qgg02xbsi-source/nix/stack/modules/turbo/packages.nix': - - … while evaluating the option `stack.turbo.packages': - - … while evaluating definitions from `/nix/store/67x4qil6i9skf56fw2q9py3qgg02xbsi-source/nix/stack/devshell/scripts.nix': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: infinite recursion encountered - ---- - -## Assistant (Build · gpt-5.3-codex · 13.7s) - -_Thinking:_ - -**Diagnosing recursion cause** - -**Tool: bash** - -**Input:** -```json -{ - "command": "git status --short", - "description": "Shows concise git working tree status" -} -``` - -**Output:** -``` - M .envrc - M .stack/config.nix -A apps/docs/content/docs/deployment/infra-modules.mdx -A apps/docs/content/docs/features.mdx -A apps/docs/content/docs/internal/dependency-graph.mdx -A apps/docs/content/docs/modules/feature-flags.mdx -M apps/docs/content/docs/reference/gitignore.mdx -A docs/nix.functions.md -M nix/stack/infra/codegen.nix -M packages/gen/env/src/docker-entrypoint.ts - M packages/infra/package.json - M packages/infra/src/types.ts - -``` - -**Tool: bash** - -**Input:** -```json -{ - "command": "git diff -- nix/stack/infra/codegen.nix nix/stack/alchemy/codegen.nix", - "description": "Shows diffs for infra and alchemy codegen files" -} -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.7s) - -_Thinking:_ - -**Checking staged changes** - -**Tool: bash** - -**Input:** -```json -{ - "command": "git diff --cached -- nix/stack/infra/codegen.nix", - "description": "Shows staged diff for infra codegen file" -} -``` - -**Output:** -``` -diff --git a/nix/stack/infra/codegen.nix b/nix/stack/infra/codegen.nix -index cbca51d..4ecff56 100644 ---- a/nix/stack/infra/codegen.nix -+++ b/nix/stack/infra/codegen.nix -@@ -144,12 +144,14 @@ let - "string[]" - else if builtins.isAttrs value then - let -- fields = lib.mapAttrsToList (k: v: " ${k}: ${nixTypeToTs v};") value; -+ fields = lib.mapAttrsToList (k: v: " ${lib.strings.toCamelCase k}: ${nixTypeToTs v};") value; - in - "{\n${lib.concatStringsSep "\n" fields}\n}" - else - "any"; - -+ -+ - toPascalCase = - s: - let - -``` - -**Tool: bash** - -**Input:** -```json -{ - "command": "git diff -- nix/stack/infra/codegen.nix", - "description": "Shows unstaged diff for infra codegen file" -} -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - -**Reviewing staging and infra code changes** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 35, - "limit": 40 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -35: # ============================================================================ -36: # Catalog versions for NPM packages used by infra modules via "catalog:". -37: # -38: # Defined once here so that: -39: # 1. Module dependencies reference "catalog:" (workspace dedup) -40: # 2. stack.bun.catalog gets the real version (root package.json) -41: # -42: # When adding a new AWS SDK or other catalog dependency to an infra module, -43: # add the version here too. -44: # ============================================================================ -45: infraCatalogVersions = { -46: "@aws-sdk/client-ec2" = "^3.953.0"; -47: "@aws-sdk/client-ecr" = "^3.953.0"; -48: "@aws-sdk/client-elastic-load-balancing-v2" = "^3.953.0"; -49: "@aws-sdk/client-iam" = "^3.953.0"; -50: "@aws-sdk/client-kms" = "^3.953.0"; -51: "@aws-sdk/client-ssm" = "^3.953.0"; -52: "@aws-sdk/client-sts" = "^3.953.0"; -53: "@pulumi/aws" = "^7.15.0"; -54: "sst" = "^3.17.25"; -55: }; -56: sortedModuleIds = lib.sort (a: b: a < b) moduleIds; -57: -58: modulePathIsDirectory = -59: id: -60: let -61: modPath = cfg.modules.${id}.path; -62: in -63: builtins.pathExists (modPath + "/index.ts"); -64: -65: moduleImportPath = -66: id: if modulePathIsDirectory id then "./modules/${id}/index.ts" else "./modules/${id}.ts"; -67: -68: # ============================================================================ -69: # Module bunDeps handling -70: # -71: # External flakes can provide pre-validated bunDeps. When provided, -72: # dependencies are already validated at the flake's eval time. -73: # ============================================================================ -74: - -(Showing lines 35-74 of 1181. Use offset=75 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.7s) - -_Thinking:_ - -**Investigating recursion error source** - -**Tool: bash** - -**Input:** -```json -{ - "command": "nix develop --impure --show-trace", - "timeout": 120000, - "description": "Runs nix develop with full stack trace" -} -``` - -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: - … while evaluating the attribute 'packages' - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the attribute 'packages' - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/flake/default.nix:437:3: - 436| - 437| packages = if enabled then directPkgs // containerPackages else { }; - | ^ - 438| - - … in the left operand of the update (//) operator - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/flake/default.nix:437:41: - 436| - 437| packages = if enabled then directPkgs // containerPackages else { }; - | ^ - 438| - - … from call site - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/flake/default.nix:368:16: - 367| spOutputs = spConfig.outputs or { }; - 368| directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) spOutputs; - | ^ - 369| nestedPkgs = lib.filterAttrs (_: v: builtins.isAttrs v && !(lib.isDerivation v)) spOutputs; - - … while calling 'filterAttrs' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:663:23: - 662| */ - 663| filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set)); - | ^ - 664| - - … while calling the 'removeAttrs' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:663:28: - 662| */ - 663| filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set)); - | ^ - 664| - - … from call site - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/flake/default.nix:367:15: - 366| # Stack outputs (packages from outputs option) - 367| spOutputs = spConfig.outputs or { }; - | ^ - 368| directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) spOutputs; - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.outputs': - - … while evaluating the attribute 'mergedValue' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1192:5: - 1191| # Type-check the remaining definitions, and merge them. Or throw if no definitions. - 1192| mergedValue = - | ^ - 1193| if isDefined then - - … while evaluating a branch condition - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1193:7: - 1192| mergedValue = - 1193| if isDefined then - | ^ - 1194| if type.merge ? v2 then - - … while evaluating the attribute 'values' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1186:9: - 1185| { - 1186| values = defsSorted; - | ^ - 1187| inherit (defsFiltered) highestPrio; - - … while evaluating a branch condition - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1180:11: - 1179| # Avoid sorting if we don't have to. - 1180| if any (def: def.value._type or "" == "order") defsFiltered.values then - | ^ - 1181| sortProperties defsFiltered.values - - … while calling the 'any' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1180:14: - 1179| # Avoid sorting if we don't have to. - 1180| if any (def: def.value._type or "" == "order") defsFiltered.values then - | ^ - 1181| sortProperties defsFiltered.values - - … while evaluating the attribute 'values' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1360:7: - 1359| { - 1360| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; - | ^ - 1361| inherit highestPrio; - - … while calling the 'concatMap' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1360:16: - 1359| { - 1360| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; - | ^ - 1361| inherit highestPrio; - - … while calling the 'concatMap' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1160:26: - 1159| # Process mkMerge and mkIf properties. - 1160| defsNormalized = concatMap ( - | ^ - 1161| m: - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1161:11: - 1160| defsNormalized = concatMap ( - 1161| m: - | ^ - 1162| map ( - - … while calling the 'map' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1162:11: - 1161| m: - 1162| map ( - | ^ - 1163| value: - - … while evaluating definitions from `/nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/devshell/scripts.nix': - - … from call site - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1171:80: - 1170| } - 1171| ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) - | ^ - 1172| ) defs; - - … while calling 'dischargeProperties' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1311:5: - 1310| dischargeProperties = - 1311| def: - | ^ - 1312| if def._type or "" == "merge" then - - … while evaluating a branch condition - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1315:7: - 1314| else if def._type or "" == "if" then - 1315| if isBool def.condition then - | ^ - 1316| if def.condition then dischargeProperties def.content else [ ] - - … while calling the 'isBool' builtin - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1315:10: - 1314| else if def._type or "" == "if" then - 1315| if isBool def.condition then - | ^ - 1316| if def.condition then dischargeProperties def.content else [ ] - - … while evaluating the attribute 'condition' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … while evaluating the attribute 'condition' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … while evaluating the attribute 'condition' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … in the left operand of the AND (&&) operator - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/devshell/scripts.nix:405:33: - 404| - 405| config = lib.mkIf (hasScripts && scriptsCfg.enable) (lib.mkMerge [ - | ^ - 406| { - - … from call site - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/devshell/scripts.nix:48:9: - 47| - 48| cfg = config.stack.scripts; - | ^ - 49| scriptsCfg = config.stack.scriptsConfig; - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.scripts': - - (10 duplicate frames omitted) - - … while evaluating definitions from `/nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/files': - - (7 duplicate frames omitted) - - … from call site - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/files/default.nix:84:18: - 83| # Filter to only enabled files - 84| enabledFiles = cfg.entries; - | ^ - 85| - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.files.entries': - - (10 duplicate frames omitted) - - … while evaluating definitions from `/nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/modules/turbo/packages.nix': - - (8 duplicate frames omitted) - - … from call site - at /nix/store/y33pndg9902k0h2jnvh11b5lrz9k73px-source/nix/stack/modules/turbo/packages.nix:29:13: - 28| cfg = config.stack.turbo; - 29| pkgsCfg = cfg.packages; - | ^ - 30| hasPkgs = pkgsCfg != { }; - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/ck9q590fr700sr7jcigygbx3fpisik8m-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.turbo.packages': - - (19 duplicate frames omitted) - - error: infinite recursion encountered - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.8s) - -_Thinking:_ - -**Investigating recursion cause in packages** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix", - "offset": 1, - "limit": 260 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix -file -1: # ============================================================================== -2: # packages.nix - Turbo Workspace Package Declarations -3: # -4: # Declares workspace packages whose package.json scripts and turbo.json tasks -5: # are managed by Nix. Single source of truth for both files. -6: # -7: # Usage: -8: # stack.turbo.packages.infra = { -9: # name = "@stack/infra"; -10: # path = "packages/infra"; -11: # dependencies = { alchemy = "catalog:"; }; -12: # scripts.deploy = { -13: # exec = "alchemy deploy"; -14: # turbo = { enable = true; cache = false; }; -15: # }; -16: # }; -17: # -18: # This generates: -19: # - packages/infra/package.json (with name, scripts, deps, exports) -20: # - turbo.json tasks entry for scripts with turbo.enable = true -21: # ============================================================================== -22: { -23: lib, -24: config, -25: ... -26: }: -27: let -28: cfg = config.stack.turbo; -29: pkgsCfg = cfg.packages; -30: hasPkgs = pkgsCfg != { }; -31: -32: # ============================================================================ -33: # Submodule: turbo task config for a script -34: # ============================================================================ -35: turboScriptOpts = { -36: options = { -37: enable = lib.mkOption { -38: type = lib.types.bool; -39: default = false; -40: description = "Register this script as a turbo task in turbo.json"; -41: }; -42: -43: cache = lib.mkOption { -44: type = lib.types.nullOr lib.types.bool; -45: default = null; -46: description = "Enable Turborepo caching (null = turbo default)"; -47: }; -48: -49: dependsOn = lib.mkOption { -50: type = lib.types.listOf lib.types.str; -51: default = [ ]; -52: description = "Tasks that must complete first (use ^ for workspace deps)"; -53: }; -54: -55: outputs = lib.mkOption { -56: type = lib.types.listOf lib.types.str; -57: default = [ ]; -58: description = "Output file globs for caching"; -59: }; -60: -61: inputs = lib.mkOption { -62: type = lib.types.listOf lib.types.str; -63: default = [ ]; -64: description = "Input file globs for cache key"; -65: }; -66: -67: persistent = lib.mkOption { -68: type = lib.types.nullOr lib.types.bool; -69: default = null; -70: description = "Long-running process (e.g. dev server)"; -71: }; -72: -73: interactive = lib.mkOption { -74: type = lib.types.nullOr lib.types.bool; -75: default = null; -76: description = "Task accepts stdin input"; -77: }; -78: }; -79: }; -80: -81: # ============================================================================ -82: # Submodule: package script entry -83: # ============================================================================ -84: scriptOpts = { -85: options = { -86: exec = lib.mkOption { -87: type = lib.types.str; -88: description = '' -89: The command string for this script. -90: Written to package.json scripts as-is. -91: ''; -92: }; -93: -94: turbo = lib.mkOption { -95: type = lib.types.submodule turboScriptOpts; -96: default = { }; -97: description = "Turbo task configuration for this script"; -98: }; -99: }; -100: }; -101: -102: # ============================================================================ -103: # Submodule: workspace package declaration -104: # ============================================================================ -105: packageOpts = { -106: options = { -107: name = lib.mkOption { -108: type = lib.types.str; -109: description = "NPM package name (e.g. @stack/infra)"; -110: }; -111: -112: path = lib.mkOption { -113: type = lib.types.str; -114: description = "Workspace path relative to repo root (e.g. packages/infra)"; -115: }; -116: -117: private = lib.mkOption { -118: type = lib.types.bool; -119: default = true; -120: description = "Set private: true in package.json"; -121: }; -122: -123: type = lib.mkOption { -124: type = lib.types.enum [ -125: "module" -126: "commonjs" -127: ]; -128: default = "module"; -129: description = "Package type (module = ESM, commonjs = CJS)"; -130: }; -131: -132: dependencies = lib.mkOption { -133: type = lib.types.attrsOf lib.types.str; -134: default = { }; -135: description = "package.json dependencies"; -136: }; -137: -138: devDependencies = lib.mkOption { -139: type = lib.types.attrsOf lib.types.str; -140: default = { }; -141: description = "package.json devDependencies"; -142: }; -143: -144: exports = lib.mkOption { -145: type = lib.types.attrsOf lib.types.anything; -146: default = { }; -147: description = '' -148: package.json exports field. -149: Example: { "." = { default = "./src/index.ts"; }; } -150: ''; -151: }; -152: -153: scripts = lib.mkOption { -154: type = lib.types.attrsOf (lib.types.submodule scriptOpts); -155: default = { }; -156: description = "package.json scripts (and optional turbo task config)"; -157: }; -158: -159: # Escape hatch for additional package.json fields -160: extraFields = lib.mkOption { -161: type = lib.types.attrsOf lib.types.anything; -162: default = { }; -163: description = '' -164: Additional fields to merge into package.json. -165: Use for fields not covered by other options (e.g. bin, main, files). -166: ''; -167: }; -168: }; -169: }; -170: -171: # ============================================================================ -172: # Build package.json value for a package (used with type = "json" for merging) -173: # ============================================================================ -174: mkPackageJsonValue = -175: _pkgId: pkg: -176: { -177: name = lib.mkDefault pkg.name; -178: private = lib.mkDefault pkg.private; -179: type = lib.mkDefault pkg.type; -180: } -181: // lib.optionalAttrs (pkg.scripts != { }) { -182: scripts = lib.mapAttrs (_: s: lib.mkDefault s.exec) pkg.scripts; -183: } -184: // lib.optionalAttrs (pkg.dependencies != { }) { -185: dependencies = lib.mapAttrs (_: lib.mkDefault) pkg.dependencies; -186: } -187: // lib.optionalAttrs (pkg.devDependencies != { }) { -188: devDependencies = lib.mapAttrs (_: lib.mkDefault) pkg.devDependencies; -189: } -190: // lib.optionalAttrs (pkg.exports != { }) { -191: exports = lib.mapAttrs (_: lib.mkDefault) pkg.exports; -192: } -193: // pkg.extraFields; -194: -195: # ============================================================================ -196: # Collect turbo-enabled scripts as stack.tasks entries -197: # ============================================================================ -198: collectTurboTasks = -199: let -200: # For each package, extract scripts with turbo.enable = true -201: # and build a task entry compatible with stack.tasks schema -202: pkgTasks = lib.mapAttrsToList ( -203: _pkgId: pkg: -204: lib.filterAttrs (_: v: v != null) ( -205: lib.mapAttrs ( -206: scriptName: scriptCfg: -207: if scriptCfg.turbo.enable then -208: { } -209: // lib.optionalAttrs (scriptCfg.turbo.cache != null) { -210: cache = scriptCfg.turbo.cache; -211: } -212: // lib.optionalAttrs (scriptCfg.turbo.dependsOn != [ ]) { -213: dependsOn = scriptCfg.turbo.dependsOn; -214: } -215: // lib.optionalAttrs (scriptCfg.turbo.outputs != [ ]) { -216: outputs = scriptCfg.turbo.outputs; -217: } -218: // lib.optionalAttrs (scriptCfg.turbo.inputs != [ ]) { -219: inputs = scriptCfg.turbo.inputs; -220: } -221: // lib.optionalAttrs (scriptCfg.turbo.persistent != null) { -222: persistent = scriptCfg.turbo.persistent; -223: } -224: // lib.optionalAttrs (scriptCfg.turbo.interactive != null) { -225: interactive = scriptCfg.turbo.interactive; -226: } -227: else -228: null -229: ) pkg.scripts -230: ) -231: ) pkgsCfg; -232: in -233: lib.foldl' (acc: tasks: acc // tasks) { } pkgTasks; -234: -235: in -236: { -237: # ============================================================================ -238: # Options -239: # ============================================================================ -240: options.stack.turbo.packages = lib.mkOption { -241: type = lib.types.attrsOf (lib.types.submodule packageOpts); -242: default = { }; -243: description = '' -244: Workspace package declarations. -245: -246: Each entry generates a package.json at the specified path and -247: optionally registers scripts as turbo.json tasks. -248: -249: This is the single source of truth for package scripts — changes here -250: update both package.json and turbo.json atomically. -251: ''; -252: example = lib.literalExpression '' -253: { -254: infra = { -255: name = "@stack/infra"; -256: path = "packages/infra"; -257: dependencies = { alchemy = "catalog:"; }; -258: scripts.deploy = { -259: exec = "alchemy deploy"; -260: turbo = { enable = true; cache = false; }; - -(Showing lines 1-260 of 290. Use offset=261 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 1.5s) - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix", - "offset": 261, - "limit": 80 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix -file -261: }; -262: }; -263: } -264: ''; -265: }; -266: -267: # ============================================================================ -268: # Config -269: # ============================================================================ -270: config = lib.mkIf hasPkgs { -271: -272: # -------------------------------------------------------------------------- -273: # Generate package.json for each declared package -274: # -------------------------------------------------------------------------- -275: stack.files.entries = lib.mapAttrs' ( -276: pkgId: pkg: -277: lib.nameValuePair "${pkg.path}/package.json" { -278: type = "json"; -279: jsonValue = mkPackageJsonValue pkgId pkg; -280: source = "turbo"; -281: description = "Package manifest for ${pkg.name}"; -282: } -283: ) pkgsCfg; -284: -285: # -------------------------------------------------------------------------- -286: # Register turbo-enabled scripts as tasks (flows into turbo.json) -287: # -------------------------------------------------------------------------- -288: stack.tasks = collectTurboTasks; -289: }; -290: } - -(End of file - total 290 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - -**Mapping recursive config dependencies** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix", - "offset": 1, - "limit": 460 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix -file -1: # ============================================================================== -2: # scripts.nix -3: # -4: # Unified script management for stack devshells. -5: # -6: # This module consolidates script/command definitions into a single -7: # `stack.scripts` option. All scripts are bundled into a single package -8: # with executables in `bin/`, avoiding conflicts and providing clean namespace. -9: # -10: # Schema defined in: nix/stack/db/schemas/scripts.proto.nix -11: # -12: # Features: -13: # - Attribute set keyed by command name (ensures no conflicts) -14: # - Single package with all scripts in `bin/` -15: # - Optional devshell integration (enabled by default) -16: # - Support for runtimeInputs, env, and description -17: # - Load script content from files via `path` option -18: # -19: # Usage (inline exec): -20: # stack.scripts.db-seed = { -21: # exec = "npm run seed"; -22: # description = "Seed the database with test data"; -23: # }; -24: # -25: # Usage (path to file): -26: # stack.scripts.db-seed = { -27: # path = ./.stack/src/scripts/db-seed.sh; -28: # description = "Seed the database with test data"; -29: # runtimeInputs = [ pkgs.nodejs ]; -30: # }; -31: # -32: # Extension scripts use namespace prefix: -33: # stack.scripts."sst:deploy" = { -34: # path = ./src/scripts/deploy.sh; -35: # description = "Deploy SST infrastructure"; -36: # }; -37: # ============================================================================== -38: { -39: config, -40: lib, -41: pkgs, -42: ... -43: }: -44: let -45: # Import proto-derived options from db -46: db = import ../db { inherit lib; }; -47: -48: cfg = config.stack.scripts; -49: scriptsCfg = config.stack.scriptsConfig; -50: -51: # Timeout presets for common script types -52: # These provide sensible defaults for different use cases -53: timeouts = { -54: quick = 30; # 30 seconds - quick checks, simple commands -55: default = 300; # 5 minutes - most scripts (network calls, builds) -56: build = 900; # 15 minutes - complex builds, compilations -57: deploy = 1800; # 30 minutes - deployments, migrations -58: long = 3600; # 1 hour - long-running data processing -59: none = 0; # No timeout - use with caution -60: }; -61: -62: # Resolve script content from either exec or path -63: resolveScriptContent = -64: name: script: -65: let -66: hasExec = script.exec or null != null && script.exec != ""; -67: hasPath = script.path or null != null; -68: in -69: if hasExec && hasPath then -70: throw "Script '${name}': cannot specify both 'exec' and 'path' - use one or the other" -71: else if hasPath then -72: builtins.readFile script.path -73: else if hasExec then -74: script.exec -75: else -76: throw "Script '${name}': must specify either 'exec' or 'path'"; -77: -78: # Build a single script executable -79: mkScript = -80: name: script: -81: let -82: envExports = lib.concatStringsSep "\n" ( -83: lib.mapAttrsToList (k: v: "export ${k}=${lib.escapeShellArg v}") (script.env or { }) -84: ); -85: scriptContent = resolveScriptContent name script; -86: -87: # Timeout configuration (default: 300 seconds = 5 minutes) -88: # Set to 0 to disable timeout -89: # Common presets available: timeouts.quick (30s), timeouts.build (15m), etc. -90: timeoutSeconds = script.timeout or timeouts.default; -91: hasTimeout = timeoutSeconds > 0; -92: -93: # Wrap script with timeout if configured -94: # The timeout command from coreutils will SIGTERM after the specified duration -95: # and SIGKILL after an additional 10 seconds if the process doesn't terminate -96: wrappedContent = if hasTimeout then '' -97: # Script timeout: ${toString timeoutSeconds} seconds (${toString (timeoutSeconds / 60.0)} minutes) -98: # This prevents the script from hanging indefinitely on network issues, waiting for input, etc. -99: # Note: We use a temp file instead of a heredoc (bash -s < "$_sp_script_tmp" <<'SCRIPT_TIMEOUT_EOF' -105: set -euo pipefail -106: ${scriptContent} -107: SCRIPT_TIMEOUT_EOF -108: timeout ${toString timeoutSeconds} bash "$_sp_script_tmp" "$@" -109: '' else scriptContent; -110: in -111: pkgs.writeShellApplication { -112: inherit name; -113: # Always include coreutils for timeout command support -114: runtimeInputs = (script.runtimeInputs or [ ]) ++ [ pkgs.coreutils ]; -115: text = '' -116: set -euo pipefail -117: ${envExports} -118: ${wrappedContent} -119: ''; -120: }; -121: -122: # Build all scripts as individual packages -123: scriptPackages = lib.mapAttrs mkScript cfg; -124: -125: # Create a combined package with bin/ directory containing all scripts -126: scriptsPackage = pkgs.symlinkJoin { -127: name = "stack-scripts"; -128: paths = lib.attrValues scriptPackages; -129: meta = { -130: description = "Stack project scripts"; -131: }; -132: }; -133: -134: # Generate serializable script definitions for CLI/agent access -135: # Uses derivation paths instead of inline content for security -136: serializableScripts = lib.mapAttrs ( -137: name: script: -138: let -139: pkg = scriptPackages.${name}; -140: in -141: { -142: inherit name; -143: description = script.description or null; -144: env = script.env or { }; -145: # Documented arguments for help text -146: args = script.args or [ ]; -147: # Timeout in seconds (0 = no timeout, default: 300) -148: timeout = script.timeout or timeouts.default; -149: # Derivation path - agent executes this directly (no sh -c with inline content) -150: binPath = "${pkg}/bin/${name}"; -151: # Source info for debugging -152: source = if script.path or null != null then "path" else "inline"; -153: } -154: ) cfg; -155: -156: hasScripts = cfg != { }; -157: -158: # Generate package.json scripts map for optional Turbo workspace package -159: generatedPackageScripts = lib.mapAttrs (_name: _scriptCfg: _name) cfg; -160: -161: # Collect scripts that declare turbo metadata -162: turboEnabledScripts = lib.filterAttrs (_name: script: (script.turbo.enable or false)) cfg; -163: -164: # Build stack.tasks entries from per-script turbo metadata -165: generatedTurboTasks = lib.mapAttrs ( -166: scriptName: scriptCfg: -167: { } -168: // lib.optionalAttrs ((scriptCfg.turbo.dependsOn or [ ]) != [ ]) { -169: dependsOn = scriptCfg.turbo.dependsOn; -170: } -171: // lib.optionalAttrs ((scriptCfg.turbo.outputs or [ ]) != [ ]) { -172: outputs = scriptCfg.turbo.outputs; -173: } -174: // lib.optionalAttrs ((scriptCfg.turbo.inputs or [ ]) != [ ]) { -175: inputs = scriptCfg.turbo.inputs; -176: } -177: // lib.optionalAttrs ((scriptCfg.turbo.cache or null) != null) { -178: cache = scriptCfg.turbo.cache; -179: } -180: // lib.optionalAttrs ((scriptCfg.turbo.persistent or null) != null) { -181: persistent = scriptCfg.turbo.persistent; -182: } -183: // lib.optionalAttrs ((scriptCfg.turbo.interactive or null) != null) { -184: interactive = scriptCfg.turbo.interactive; -185: } -186: ) turboEnabledScripts; -187: -188: # Nix-only script options (not serializable to proto - contains packages/paths) -189: nixScriptOptionsModule = -190: { lib, ... }: -191: { -192: options = { -193: # Path is Nix-only because it uses types.path (not serializable as string) -194: path = lib.mkOption { -195: type = lib.types.nullOr lib.types.path; -196: default = null; -197: description = '' -198: Path to script file. Content is read and used as the script body. -199: Mutually exclusive with `exec` - use one or the other. -200: ''; -201: example = lib.literalExpression "./.stack/src/scripts/my-script.sh"; -202: }; -203: -204: -205: -206: runtimeInputs = lib.mkOption { -207: type = lib.types.listOf lib.types.package; -208: default = [ ]; -209: description = '' -210: List of packages to add to PATH when running the script. -211: Packages to include in PATH when running the script. -212: These are pinned to specific Nix store paths, ensuring reproducible execution. -213: ''; -214: example = lib.literalExpression "[ pkgs.nodejs pkgs.jq ]"; -215: }; -216: -217: turbo = lib.mkOption { -218: type = lib.types.submodule { -219: options = { -220: enable = lib.mkOption { -221: type = lib.types.bool; -222: default = false; -223: description = "Whether to register this script as a Turborepo task."; -224: }; -225: -226: dependsOn = lib.mkOption { -227: type = lib.types.listOf lib.types.str; -228: default = [ ]; -229: description = '' -230: Turborepo task dependencies for this script. -231: Example: [ "^build" "lint" ]. -232: ''; -233: }; -234: -235: outputs = lib.mkOption { -236: type = lib.types.listOf lib.types.str; -237: default = [ ]; -238: description = "Turborepo outputs globs for caching."; -239: }; -240: -241: inputs = lib.mkOption { -242: type = lib.types.listOf lib.types.str; -243: default = [ ]; -244: description = "Turborepo inputs globs for cache keys."; -245: }; -246: -247: cache = lib.mkOption { -248: type = lib.types.nullOr lib.types.bool; -249: default = null; -250: description = "Override Turborepo cache setting (null = turbo default)."; -251: }; -252: -253: persistent = lib.mkOption { -254: type = lib.types.nullOr lib.types.bool; -255: default = null; -256: description = "Mark task as persistent (long-running process)."; -257: }; -258: -259: interactive = lib.mkOption { -260: type = lib.types.nullOr lib.types.bool; -261: default = null; -262: description = "Mark task as interactive (accepts stdin)."; -263: }; -264: }; -265: }; -266: default = { }; -267: description = '' -268: Optional Turborepo metadata for this script. -269: When turbo.enable = true and scriptsConfig.generateTurboPackage is enabled, -270: script metadata is exported to stack.tasks and included in turbo.json. -271: ''; -272: }; -273: }; -274: }; -275: in -276: { -277: options.stack.scriptModules = lib.mkOption { -278: type = lib.types.listOf lib.types.deferredModule; -279: default = [ ]; -280: description = '' -281: Additional modules to extend script configuration options. -282: ''; -283: }; -284: -285: options.stack.scripts = lib.mkOption { -286: type = lib.types.attrsOf ( -287: lib.types.submoduleWith { -288: modules = [ -289: # Proto-derived options (exec, description, env, timeout) -290: { options = db.asOptions db.extend.script; } -291: # Set timeout default (proto defines the option, we set the default) -292: { config.timeout = lib.mkDefault timeouts.default; } -293: # Nix-only runtime options (runtimeInputs, path) -294: nixScriptOptionsModule -295: ] -296: ++ config.stack.scriptModules; -297: specialArgs = { inherit lib; }; -298: } -299: ); -300: default = { }; -301: description = '' -302: Scripts exposed in the development shell. -303: -304: Each script becomes an executable command available in PATH. -305: The attribute name determines the command name. -306: -307: Script content can be provided via: -308: - exec: Inline shell command -309: - path: Path to script file (content is read at eval time) -310: -311: These are mutually exclusive - use one or the other. -312: -313: Proto-derived options (from scripts.proto.nix): -314: - exec: Shell command to execute -315: - description: Human-readable description -316: - env: Environment variables -317: -318: Nix extensions: -319: - path: Path to script file (alternative to inline exec) -320: - runtimeInputs: Nix packages for PATH -321: -322: Extension scripts should use namespace prefix (e.g., "sst:deploy"). -323: ''; -324: example = lib.literalExpression '' -325: { -326: # Inline exec -327: db-seed = { -328: exec = "npm run seed"; -329: description = "Seed the database with test data"; -330: }; -331: -332: # Path to script file -333: deploy = { -334: path = ./.stack/src/scripts/deploy.sh; -335: description = "Deploy the application"; -336: runtimeInputs = [ pkgs.awscli2 ]; -337: }; -338: -339: # Extension-namespaced script -340: "sst:dev" = { -341: exec = "sst dev"; -342: description = "Start SST dev mode"; -343: runtimeInputs = [ pkgs.nodejs ]; -344: }; -345: } -346: ''; -347: }; -348: -349: options.stack.scriptsConfig = { -350: enable = lib.mkOption { -351: type = lib.types.bool; -352: default = true; -353: description = "Whether to add the scripts package to the devshell."; -354: }; -355: -356: generateTurboPackage = lib.mkOption { -357: type = lib.types.bool; -358: default = false; -359: description = '' -360: Whether to generate a Turbo-managed workspace package for scripts at -361: scriptsConfig.turboPackagePath (default: packages/gen/scripts). -362: -363: When enabled, this creates/merges package.json scripts via stack.turbo.packages -364: and registers script-level turbo metadata (when script.turbo.enable = true) -365: into stack.tasks for turbo.json generation. -366: ''; -367: }; -368: -369: turboPackageId = lib.mkOption { -370: type = lib.types.str; -371: default = "scripts"; -372: description = "Identifier key under stack.turbo.packages for the generated scripts package."; -373: }; -374: -375: turboPackageName = lib.mkOption { -376: type = lib.types.str; -377: default = "@gen/scripts"; -378: description = "NPM package name for the generated scripts workspace package."; -379: }; -380: -381: turboPackagePath = lib.mkOption { -382: type = lib.types.str; -383: default = "packages/gen/scripts"; -384: description = "Workspace-relative path where the generated scripts package.json is managed."; -385: }; -386: -387: package = lib.mkOption { -388: type = lib.types.package; -389: readOnly = true; -390: default = scriptsPackage; -391: description = "The generated combined scripts package (read-only)."; -392: }; -393: -394: packages = lib.mkOption { -395: type = lib.types.attrsOf lib.types.package; -396: readOnly = true; -397: default = scriptPackages; -398: description = '' -399: Individual script packages (read-only). -400: Use to reference specific scripts: config.stack.scriptsConfig.packages.my-script -401: ''; -402: }; -403: }; -404: -405: config = lib.mkIf (hasScripts && scriptsCfg.enable) (lib.mkMerge [ -406: { -407: # Add the scripts package to devshell -408: stack.devshell.packages = [ scriptsPackage ]; -409: -410: # Store serializable definitions for CLI/TUI access -411: stack.devshell._commandsSerializable = serializableScripts; -412: -413: # Expose individual script packages as flake outputs -414: # Available via: nix run .#scripts. -415: stack.outputs.scripts = scriptPackages; -416: -417: # Also expose the combined package -418: stack.outputs.stack-scripts = scriptsPackage; -419: -420: # Print available scripts on shell entry -421: stack.devshell.hooks.main = [ -422: '' -423: echo "📜 stack scripts loaded" -424: '' -425: ]; -426: } -427: -428: (lib.mkIf scriptsCfg.generateTurboPackage { -429: stack.turbo.packages.${scriptsCfg.turboPackageId} = { -430: name = scriptsCfg.turboPackageName; -431: path = scriptsCfg.turboPackagePath; -432: scripts = lib.mapAttrs (_scriptName: command: { exec = command; }) generatedPackageScripts; -433: }; -434: -435: stack.tasks = generatedTurboTasks; -436: }) -437: ]); -438: } - -(End of file - total 438 lines) - -``` - ---- - -## User - -every module needs to declare a proper version (or version range), not catalog: - ---- - -## Assistant (Build · gpt-5.3-codex · 15.8s) - -_Thinking:_ - -**Migrating infra modules to explicit versions** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "stack\\.turbo\\.packages", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 7 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 680: stack.turbo.packages.infra = { - -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix: - Line 363: When enabled, this creates/merges package.json scripts via stack.turbo.packages - Line 372: description = "Identifier key under stack.turbo.packages for the generated scripts package."; - Line 429: stack.turbo.packages.${scriptsCfg.turboPackageId} = { - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 365: stack.turbo.packages.alchemy = { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix: - Line 8: # stack.turbo.packages.infra = { - Line 240: options.stack.turbo.packages = lib.mkOption { -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - -**Inspecting dependency option types** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "dependencies", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/infra", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 33 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 12: # - package.json (union of all module dependencies) - Line 39: # 1. Module dependencies reference "catalog:" (workspace dedup) - Line 72: # dependencies are already validated at the flake's eval time. - Line 371: acc // mod.dependencies - Line 395: depsStr = lib.concatStringsSep ", " (map (d: "`${d}`") (lib.attrNames mod.dependencies)); - Line 410: ${lib.optionalString (mod.dependencies != { }) "Dependencies: ${depsStr}\n"} - Line 683: dependencies = { - Line 690: // cfg.package.dependencies; - Line 796: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix: - Line 84: # Determine which hosts are in use (for npm dependencies) - Line 119: # Aggregate npm dependencies based on hosts in use - Line 158: dependencies = allDeps; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix: - Line 25: # Maps alchemy resource imports to their required npm peer dependencies. - Line 26: # Used for validation to ensure all required dependencies are declared. - Line 89: dependencies = lib.mkOption { - Line 93: NPM dependencies this module requires. - Line 94: Merged into the infra package.json dependencies. - Line 97: for pre-validated, reproducible dependencies. - Line 242: dependencies, and output declarations. - Line 257: dependencies = lib.mkOption { - Line 260: description = "Additional package.json dependencies beyond what modules declare"; - Line 269: When provided, dependencies are validated at Nix evaluation time - Line 271: peer dependencies before runtime. - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix: - Line 290: dependencies = lib.optionalAttrs (cfg.source == "aws-ec2") { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix: - Line 128: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix: - Line 261: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix: - Line 66: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix: - Line 84: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix: - Line 108: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix: - Line 186: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix: - Line 889: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix: - Line 240: dependencies = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix: - Line 176: dependencies = { -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.5s) - -_Thinking:_ - -**Reviewing dependency semantics** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix", - "offset": 70, - "limit": 240 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/options.nix -file -70: path = lib.mkOption { -71: type = lib.types.path; -72: description = '' -73: Path to the TypeScript module file. -74: Must default-export a Record of outputs. -75: Use `import Infra from "@stack/infra"` for the library. -76: ''; -77: }; -78: -79: inputs = lib.mkOption { -80: type = lib.types.attrsOf lib.types.anything; -81: default = { }; -82: description = '' -83: Configuration values passed to the module at runtime. -84: Serialized to JSON in .stack/state/infra-inputs.json. -85: Values matching ENC[age,...] are decrypted at runtime. -86: ''; -87: }; -88: -89: dependencies = lib.mkOption { -90: type = lib.types.attrsOf lib.types.str; -91: default = { }; -92: description = '' -93: NPM dependencies this module requires. -94: Merged into the infra package.json dependencies. -95: -96: For external flake modules, prefer providing bunDeps instead -97: for pre-validated, reproducible dependencies. -98: ''; -99: }; -100: -101: bunDeps = lib.mkOption { -102: type = lib.types.nullOr lib.types.package; -103: default = null; -104: description = '' -105: Pre-fetched Bun dependency cache from bun2nix.fetchBunDeps. -106: -107: External flakes should provide this for reproducible builds. -108: When provided, these deps are validated at Nix eval time and -109: merged with other modules' deps. -110: -111: Example (in a flake providing an infra module): -112: bunDeps = bun2nix.fetchBunDeps { -113: bunNix = ./bun.nix; -114: }; -115: -116: The flake should also ship package.json, bun.lock, and bun.nix -117: alongside the module. -118: ''; -119: }; -120: -121: outputs = lib.mkOption { -122: type = lib.types.attrsOf outputDeclType; -123: default = { }; -124: description = '' -125: Output declarations for this module. -126: Keys must match the keys of the default export from the TypeScript file. -127: Only outputs with sync=true are written to the storage backend. -128: ''; -129: }; -130: }; -131: }; -132: -133: in -134: { -135: # ============================================================================ -136: # Options -137: # ============================================================================ -138: options.stack.infra = { -139: enable = lib.mkOption { -140: type = lib.types.bool; -141: default = false; -142: description = "Enable the infrastructure module system"; -143: }; -144: -145: framework = lib.mkOption { -146: type = lib.types.enum [ "alchemy" ]; -147: default = "alchemy"; -148: description = '' -149: IaC framework to use for infrastructure provisioning. -150: Currently only "alchemy" is supported. The alchemy module at -151: config.stack.alchemy provides the shared SDK configuration -152: (version, state store, helpers) that this module consumes. -153: ''; -154: }; -155: -156: output-dir = lib.mkOption { -157: type = lib.types.str; -158: default = "packages/infra"; -159: description = "Directory for generated infrastructure files (relative to project root)"; -160: }; -161: -162: key-format = lib.mkOption { -163: type = lib.types.str; -164: default = "$module-$key"; -165: description = '' -166: Template for output storage keys. -167: Variables: $module (module ID), $key (output key name). -168: Example: "$module-$key" -> "aws-secrets-roleArn" -169: ''; -170: }; -171: -172: # ========================================================================== -173: # Storage backend for persisting outputs -174: # ========================================================================== -175: storage-backend = { -176: type = lib.mkOption { -177: type = lib.types.enum [ -178: "chamber" -179: "sops" -180: "ssm" -181: "none" -182: ]; -183: default = "none"; -184: description = "Storage backend for persisting infrastructure outputs"; -185: }; -186: -187: chamber = { -188: service = lib.mkOption { -189: type = lib.types.str; -190: default = ""; -191: description = '' -192: Chamber service name for output storage. -193: Outputs are written as: chamber write -- -194: ''; -195: }; -196: }; -197: -198: sops = { -199: file-path = lib.mkOption { -200: type = lib.types.str; -201: default = ".stack/secrets/vars/dev.sops.yaml"; -202: description = '' -203: Path to SOPS-encrypted YAML file for infra outputs. -204: Defaults to the dev group file. Uses `sops set` for non-destructive -205: per-key updates, preserving existing secrets in the file. -206: ''; -207: }; -208: -209: group = lib.mkOption { -210: type = lib.types.str; -211: default = "dev"; -212: description = '' -213: Secrets group to write outputs to (e.g., "dev", "prod", "common"). -214: Used to resolve the SOPS file path from the secrets directory: -215: /vars/.sops.yaml -216: When set, overrides file-path. -217: ''; -218: }; -219: }; -220: -221: ssm = { -222: prefix = lib.mkOption { -223: type = lib.types.str; -224: default = ""; -225: description = '' -226: SSM Parameter Store path prefix for infra outputs. -227: Outputs are written to: / -228: ''; -229: }; -230: }; -231: }; -232: -233: # ========================================================================== -234: # Module registry (populated by infra modules) -235: # ========================================================================== -236: modules = lib.mkOption { -237: type = lib.types.attrsOf infraModuleType; -238: default = { }; -239: description = '' -240: Registry of infrastructure modules. -241: Each infra module registers itself here with its path, inputs, -242: dependencies, and output declarations. -243: Do not set this directly — infra modules populate it via config. -244: ''; -245: }; -246: -247: # ========================================================================== -248: # Generated package configuration -249: # ========================================================================== -250: package = { -251: name = lib.mkOption { -252: type = lib.types.str; -253: default = "@${projectName}/infra"; -254: description = "NPM package name for the generated infrastructure package"; -255: }; -256: -257: dependencies = lib.mkOption { -258: type = lib.types.attrsOf lib.types.str; -259: default = { }; -260: description = "Additional package.json dependencies beyond what modules declare"; -261: }; -262: -263: bun-nix = lib.mkOption { -264: type = lib.types.nullOr lib.types.path; -265: default = null; -266: description = '' -267: Path to a bun.nix file for the infra package. -268: -269: When provided, dependencies are validated at Nix evaluation time -270: via bun2nix.fetchBunDeps, catching invalid versions and missing -271: peer dependencies before runtime. -272: -273: Bootstrapping workflow: -274: 1. Enter devshell: nix develop --impure -275: 2. Generate lock file: cd ${cfg.output-dir} && bun2nix -276: 3. Set this option: bun-nix = ./${cfg.output-dir}/bun.nix; -277: 4. Re-enter devshell to enable validation -278: -279: See: https://nix-community.github.io/bun2nix/building-packages/fetchBunDeps.html -280: ''; -281: }; -282: }; -283: -284: # ========================================================================== -285: # Outputs stub (cross-resource references) -286: # ========================================================================== -287: outputs = lib.mkOption { -288: type = lib.types.attrsOf (lib.types.attrsOf lib.types.anything); -289: default = { }; -290: description = '' -291: Infrastructure outputs from the last deployment. -292: Keyed by module ID, then by output key. -293: -294: Populated by running `infra:pull-outputs` after deployment, -295: which reads from the storage backend and writes to -296: .stack/data/infra-outputs.nix. -297: -298: Outputs are typically strings, but may include structured values -299: (e.g., machine inventories) when modules emit complex outputs. -300: -301: Machine inventories are expected at: -302: config.stack.infra.outputs.machines.machines -303: -304: Suggested shape: -305: machines = { -306: web-1 = { -307: host = "web-1.example.com"; -308: ssh = { user = "root"; port = 22; }; -309: roles = [ "web" ]; - -(Showing lines 70-309 of 334. Use offset=310 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - -**Inspecting catalog placeholders** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "=\\s*\"catalog:\"", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 18 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix: - Line 291: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix: - Line 129: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix: - Line 262: "@aws-sdk/client-sts" = "catalog:"; # AccountId from alchemy/aws - Line 263: "@aws-sdk/client-iam" = "catalog:"; # Role, GitHubOIDCProvider - Line 264: "@aws-sdk/client-kms" = "catalog:"; # KmsKey, KmsAlias - Line 265: "@aws-sdk/client-ssm" = "catalog:"; # SSM for group key storage - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix: - Line 67: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix: - Line 85: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix: - Line 109: "@aws-sdk/client-iam" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix: - Line 187: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix: - Line 890: "@aws-sdk/client-ec2" = "catalog:"; - Line 891: "@aws-sdk/client-iam" = "catalog:"; - Line 892: "@aws-sdk/client-elastic-load-balancing-v2" = "catalog:"; - Line 893: "@aws-sdk/client-ecr" = "catalog:"; - Line 894: "@aws-sdk/client-ssm" = "catalog:"; - Line 895: "@aws-sdk/client-sts" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix: - Line 241: "alchemy" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix: - Line 177: "alchemy" = "catalog:"; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - -**Examining dependency version sourcing** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix", - "offset": 70, - "limit": 120 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix -file -70: # Fly deploys are handled by the Fly deployment module (flyctl + fly.toml). -71: # --------------------------------------------------------------------------- -72: supportedHosts = [ "cloudflare" ]; -73: -74: deployableApps = lib.filterAttrs ( -75: _: appCfg: -76: (appCfg.deployment.enable or false) -77: && (getFramework appCfg) != null -78: && lib.elem (getHost appCfg) supportedHosts -79: ) (cfg.apps or { }); -80: -81: hasDeployableApps = deployableApps != { }; -82: -83: # --------------------------------------------------------------------------- -84: # Determine which hosts are in use (for npm dependencies) -85: # --------------------------------------------------------------------------- -86: hosts = lib.unique (lib.mapAttrsToList (_: appCfg: getHost appCfg) deployableApps); -87: -88: hasCloudflare = lib.elem "cloudflare" hosts; -89: # --------------------------------------------------------------------------- -90: # Build pure-data inputs for the TS module -91: # --------------------------------------------------------------------------- -92: appInputs = lib.mapAttrs ( -93: appName: appCfg: -94: let -95: fw = getFramework appCfg; -96: fwCfg = appCfg.framework.${fw}; -97: in -98: { -99: framework = fw; -100: host = getHost appCfg; -101: path = appCfg.path or "apps/${appName}"; -102: bindings = appCfg.deployment.bindings; -103: secrets = appCfg.deployment.secrets; -104: } -105: # Include framework-specific options as extra keys -106: // lib.optionalAttrs (fw == "vite") { -107: ssr = fwCfg.ssr or false; -108: assetsDir = fwCfg."assets-dir" or "dist"; -109: } -110: // lib.optionalAttrs (fw == "hono") { -111: entrypoint = fwCfg.entrypoint or "src/index.ts"; -112: } -113: // lib.optionalAttrs (fw == "nextjs") { -114: output = fwCfg.output or "standalone"; -115: } -116: ) deployableApps; -117: -118: # --------------------------------------------------------------------------- -119: # Aggregate npm dependencies based on hosts in use -120: # --------------------------------------------------------------------------- -121: baseDeps = { -122: # alchemy is always needed -123: }; -124: -125: cloudflareDeps = lib.optionalAttrs hasCloudflare { -126: # alchemy/cloudflare is part of the alchemy package -127: }; -128: -129: # Future: flyDeps, vercelDeps, awsDeps -130: allDeps = baseDeps // cloudflareDeps; -131: -132: # --------------------------------------------------------------------------- -133: # Declare outputs (one URL per deployed app) -134: # --------------------------------------------------------------------------- -135: appOutputs = lib.mapAttrs' ( -136: appName: _: -137: lib.nameValuePair "${appName}Url" { -138: description = "Deployed URL for ${appName}"; -139: sensitive = false; -140: sync = true; -141: } -142: ) deployableApps; -143: -144: in -145: { -146: config = lib.mkIf hasDeployableApps { -147: # Auto-enable the infra system -148: stack.infra.enable = lib.mkDefault true; -149: -150: # Register as an infra module -151: stack.infra.modules.deployment = { -152: name = "App Deployment"; -153: description = "Deploys apps to their configured hosts (${lib.concatStringsSep ", " hosts})"; -154: path = ./index.ts; -155: inputs = { -156: apps = appInputs; -157: }; -158: dependencies = allDeps; -159: outputs = appOutputs; -160: }; -161: }; -162: } - -(End of file - total 162 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.8s) - -_Thinking:_ - -**Assessing alchemy dependency necessity** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 300, - "limit": 70 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -300: -301: # syncAll argument -302: syncAllArg = lib.concatMapStringsSep "\n" ( -303: id: -304: let -305: mod = cfg.modules.${id}; -306: syncKeys = builtins.filter (k: (mod.outputs.${k}.sync or false)) (builtins.attrNames mod.outputs); -307: varName = builtins.replaceStrings [ "-" ] [ "_" ] id; -308: in -309: '' -310: "${id}": { -311: outputs: ${varName}Outputs, -312: syncKeys: ${builtins.toJSON syncKeys}, -313: },'' -314: ) moduleIds; -315: -316: # Use @gen/alchemy if the alchemy module is enabled, otherwise fall back -317: # to inline alchemy initialization for backward compatibility. -318: alchemyCfg = config.stack.alchemy; -319: useGenAlchemy = alchemyCfg.enable; -320: in -321: if useGenAlchemy then -322: '' -323: // Generated by stack — do not edit manually. -324: import { createApp } from "@gen/alchemy"; -325: import Infra from "./src/index.ts"; -326: -327: const app = await createApp("${projectName}-infra"); -328: -329: // Import and run infra modules -330: ${moduleImports} -331: -332: // Sync declared outputs to storage backend -333: await Infra.syncAll({ -334: ${syncAllArg} -335: }); -336: -337: await app.finalize(); -338: '' -339: else -340: '' -341: // Generated by stack — do not edit manually. -342: import alchemy from "alchemy"; -343: import { CloudflareStateStore } from "alchemy/state"; -344: import Infra from "./src/index.ts"; -345: -346: const app = await alchemy("${projectName}-infra", { -347: stateStore: process.env.CLOUDFLARE_API_TOKEN -348: ? (scope) => -349: new CloudflareStateStore(scope, { -350: apiToken: alchemy.secret(process.env.CLOUDFLARE_API_TOKEN!), -351: }) -352: : undefined, -353: }); -354: -355: // Import and run infra modules -356: ${moduleImports} -357: -358: // Sync declared outputs to storage backend -359: await Infra.syncAll({ -360: ${syncAllArg} -361: }); -362: -363: await app.finalize(); -364: ''; -365: -366: # ============================================================================ -367: # Module dependency aggregation (used by turbo.packages) -368: # ============================================================================ -369: allDeps = lib.foldlAttrs ( - -(Showing lines 300-369 of 1181. Use offset=370 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 16.3s) - -_Thinking:_ - -**Analyzing dependency recursion and catalog versioning** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "turbo\\.packages|scriptsConfig|stack\\.scripts|stack\\.tasks|infra\\.package", - "path": "/Users/cm/git/darkmatter/stack/.stack/config.nix" -} -``` - -**Output:** -``` -No files found -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.5s) - -_Thinking:_ - -**Clarifying grep path usage** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "turbo\\.packages|scriptsConfig|stack\\.scripts|stack\\.tasks|infra\\.package", - "path": "/Users/cm/git/darkmatter/stack/.stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 4 matches -/Users/cm/git/darkmatter/stack/.stack/modules/generate-config-example.nix: - Line 150: config.stack.scripts.generate-config-example = lib.mkIf cfg.enable { - -/Users/cm/git/darkmatter/stack/.stack/modules/default.nix: - Line 14: # - Custom scripts with dependencies (stack.scripts) - -/Users/cm/git/darkmatter/stack/.stack/modules/prek-wrapper.nix: - Line 28: stack.scripts."stack:prek" = { - -/Users/cm/git/darkmatter/stack/.stack/modules/generate-docs.nix: - Line 75: config.stack.scripts.generate-docs = { -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 18.1s) - -_Thinking:_ - -**Tracing recursion cause hypotheses** - -**Tool: bash** - -**Input:** -```json -{ - "command": "git diff --cached -- .stack/config.nix", - "description": "Shows staged config changes for stack config" -} -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.5s) - -_Thinking:_ - -**Checking unstaged changes** - -**Tool: bash** - -**Input:** -```json -{ - "command": "git diff -- .stack/config.nix", - "description": "Shows unstaged stack config changes" -} -``` - -**Output:** -``` -diff --git a/.stack/config.nix b/.stack/config.nix -index 1294765..74c5e73 100644 ---- a/.stack/config.nix -+++ b/.stack/config.nix -@@ -345,9 +345,122 @@ - }; - }; - -+ aws-vault = { -+ enable = true; -+ # Enable wrappers so aws/terraform automatically use aws-vault -+ awscliWrapper.enable = true; -+ terraformWrapper.enable = true; -+ -+ # Show which profile is being tried (useful for debugging) -+ showProfileAttempts = true; -+ -+ # Stop after first success (default behavior) -+ stopOnFirstSuccess = true; -+ profiles = ["sso-prod" "sso-staging"]; -+ -+ awsProfiles = { -+ "sso-prod" = { -+ region = "us-east-1"; -+ output = "json"; -+ extraConfig = { -+ sso_start_url = "https://mycompany.awsapps.com/start"; -+ sso_region = "us-east-1"; -+ sso_account_id = "123456789012"; -+ sso_role_name = "ProductionAccess"; -+ }; -+ }; -+ -+ "sso-staging" = { -+ region = "us-east-1"; -+ output = "json"; -+ extraConfig = { -+ sso_start_url = "https://mycompany.awsapps.com/start"; -+ sso_region = "us-east-1"; -+ sso_account_id = "123456789012"; -+ sso_role_name = "StagingAccess"; -+ }; -+ }; -+ }; -+ }; -+ - infra = { - aws-ec2-app = { - apps = { -+ docs = { -+ ami = null; -+ associate-public-ip = true; -+ iam = { -+ enable = true; -+ role-name = "docs-ec2-role"; -+ }; -+ instance-count = 1; -+ instance-type = "t3.micro"; -+ instances = [ ]; -+ key-name = null; -+ key-pair = { -+ create = false; -+ name = null; -+ public-key = null; -+ }; -+ machine = { -+ arch = null; -+ roles = [ "docs" ]; -+ ssh = { -+ key-path = null; -+ port = 22; -+ user = "root"; -+ }; -+ tags = [ ]; -+ target-env = "staging"; -+ }; -+ os-type = "nixos"; -+ root-volume-size = null; -+ security-group = { -+ create = true; -+ description = null; -+ egress = [ -+ { -+ cidr-blocks = [ "0.0.0.0/0" ]; -+ description = "All outbound"; -+ from-port = 0; -+ protocol = "-1"; -+ to-port = 0; -+ } -+ ]; -+ ingress = [ -+ { -+ cidr-blocks = [ "0.0.0.0/0" ]; -+ description = "SSH"; -+ from-port = 22; -+ protocol = "tcp"; -+ to-port = 22; -+ } -+ { -+ cidr-blocks = [ "0.0.0.0/0" ]; -+ description = "HTTP"; -+ from-port = 80; -+ protocol = "tcp"; -+ to-port = 80; -+ } -+ { -+ cidr-blocks = [ "0.0.0.0/0" ]; -+ description = "HTTPS"; -+ from-port = 443; -+ protocol = "tcp"; -+ to-port = 443; -+ } -+ ]; -+ name = null; -+ }; -+ security-group-ids = [ ]; -+ subnet-ids = [ ]; -+ tags = { -+ ManagedBy = "stack-infra"; -+ Name = "docs"; -+ }; -+ user-data = null; -+ vpc-id = null; -+ }; - stack-staging = { - ami = null; - associate-public-ip = true; -@@ -737,4 +850,3 @@ - }; - - } -- - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 12.4s) - -_Thinking:_ - -**Planning catalog version removal** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "\"catalog:\";", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 27 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 684: alchemy = "catalog:"; - Line 797: # "@aws-sdk/client-s3" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix: - Line 8: # Modules declare dependencies as `"@aws-sdk/client-ecr" = "catalog:";` in - -/Users/cm/git/darkmatter/stack/nix/stack/sst/sst.nix: - Line 468: sst = "catalog:"; - Line 469: "@pulumi/aws" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 283: alchemy = "catalog:"; - Line 287: "@aws-sdk/client-ssm" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix: - Line 291: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix: - Line 129: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix: - Line 262: "@aws-sdk/client-sts" = "catalog:"; # AccountId from alchemy/aws - Line 263: "@aws-sdk/client-iam" = "catalog:"; # Role, GitHubOIDCProvider - Line 264: "@aws-sdk/client-kms" = "catalog:"; # KmsKey, KmsAlias - Line 265: "@aws-sdk/client-ssm" = "catalog:"; # SSM for group key storage - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix: - Line 67: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix: - Line 85: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix: - Line 109: "@aws-sdk/client-iam" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix: - Line 187: "@aws-sdk/client-ec2" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix: - Line 890: "@aws-sdk/client-ec2" = "catalog:"; - Line 891: "@aws-sdk/client-iam" = "catalog:"; - Line 892: "@aws-sdk/client-elastic-load-balancing-v2" = "catalog:"; - Line 893: "@aws-sdk/client-ecr" = "catalog:"; - Line 894: "@aws-sdk/client-ssm" = "catalog:"; - Line 895: "@aws-sdk/client-sts" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix: - Line 241: "alchemy" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix: - Line 177: "alchemy" = "catalog:"; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix: - Line 11: # dependencies = { alchemy = "catalog:"; }; - Line 257: dependencies = { alchemy = "catalog:"; }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.2s) - -_Thinking:_ - -**Inspecting module version options** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/sst/sst.nix", - "offset": 430, - "limit": 240 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/sst/sst.nix -file -430: -431: export default $config({ -432: app(input) { -433: return { -434: name: projectName, -435: removal: input?.stage === "production" ? "retain" : "remove", -436: protect: ["production"].includes(input?.stage ?? ""), -437: home: "aws", -438: providers: { -439: aws: { -440: region: awsRegion, -441: }, -442: }, -443: }; -444: }, -445: -446: async run() { -447: ${oidcSetup} -448: ${iamRoleSetup} -449: ${kmsSetup} -450: ${kmsPolicySetup} -451: ${outputs} -452: }, -453: }); -454: ''; -455: -456: # ========================================================================== -457: # Package.json generation -458: # ========================================================================== -459: packageDir = builtins.dirOf cfg.config-path; -460: -461: defaultPackageScripts = { -462: "sst:deploy" = "bunx sst deploy"; -463: "sst:dev" = "bunx sst dev"; -464: "sst:remove" = "bunx sst remove"; -465: }; -466: -467: defaultPackageDeps = { -468: sst = "catalog:"; -469: "@pulumi/aws" = "catalog:"; -470: }; -471: -472: packageJsonValue = { -473: name = cfg.package.name; -474: type = "module"; -475: private = true; -476: scripts = defaultPackageScripts // cfg.package.scripts; -477: dependencies = defaultPackageDeps // cfg.package.dependencies; -478: }; -479: in { -480: options.stack.sst = { -481: enable = lib.mkOption { -482: type = lib.types.bool; -483: default = false; -484: description = "Enable SST infrastructure provisioning"; -485: }; -486: -487: project-name = lib.mkOption { -488: type = lib.types.str; -489: default = projectName; -490: description = "SST project name (used in resource naming)"; -491: }; -492: -493: region = lib.mkOption { -494: type = lib.types.str; -495: default = defaultRegion; -496: description = "AWS region for infrastructure (inherits from stack.aws.roles-anywhere.region)"; -497: }; -498: -499: account-id = lib.mkOption { -500: type = lib.types.str; -501: default = defaultAccountId; -502: description = "AWS account ID (inherits from stack.aws.roles-anywhere.account-id)"; -503: }; -504: -505: config-path = lib.mkOption { -506: type = lib.types.str; -507: default = "packages/infra/sst.config.ts"; -508: description = "Path to generate the SST config file (relative to project root)"; -509: }; -510: -511: # Package.json generation (enables turbo integration) -512: package = { -513: enable = lib.mkOption { -514: type = lib.types.bool; -515: default = true; -516: description = "Generate a package.json in the SST directory, making it a workspace package for Turborepo"; -517: }; -518: -519: name = lib.mkOption { -520: type = lib.types.str; -521: default = "@${projectName}/infra"; -522: description = "NPM package name for the infrastructure package"; -523: }; -524: -525: dependencies = lib.mkOption { -526: type = lib.types.attrsOf lib.types.str; -527: default = {}; -528: description = "Additional dependencies to include in the generated package.json (beyond sst and @pulumi/aws)"; -529: }; -530: -531: scripts = lib.mkOption { -532: type = lib.types.attrsOf lib.types.str; -533: default = {}; -534: description = "Additional scripts to include in the generated package.json (deploy, dev, remove are included by default)"; -535: }; -536: }; -537: -538: # KMS configuration -539: kms = { -540: enable = lib.mkOption { -541: type = lib.types.bool; -542: default = true; -543: description = "Create a KMS key for secrets encryption"; -544: }; -545: -546: alias = lib.mkOption { -547: type = lib.types.str; -548: default = "${projectName}-secrets"; -549: description = "KMS key alias"; -550: }; -551: -552: deletion-window-days = lib.mkOption { -553: type = lib.types.int; -554: default = 30; -555: description = "Number of days before KMS key deletion"; -556: }; -557: }; -558: -559: # OIDC configuration -560: oidc = { -561: provider = lib.mkOption { -562: type = lib.types.enum [ -563: "github-actions" -564: "flyio" -565: "roles-anywhere" -566: "none" -567: ]; -568: default = "github-actions"; -569: description = "OIDC provider type for IAM role assumption"; -570: }; -571: -572: github-actions = { -573: org = lib.mkOption { -574: type = lib.types.str; -575: default = defaultGithubOrg; -576: description = "GitHub organization name (inherits from stack.project.owner)"; -577: }; -578: -579: repo = lib.mkOption { -580: type = lib.types.str; -581: default = defaultGithubRepo; -582: description = "GitHub repository name (inherits from stack.project.repo, or * for all repos in org)"; -583: }; -584: -585: branch = lib.mkOption { -586: type = lib.types.str; -587: default = "*"; -588: description = "Branch filter for OIDC subject"; -589: }; -590: }; -591: -592: flyio = { -593: org-id = lib.mkOption { -594: type = lib.types.str; -595: default = ""; -596: description = "Fly.io organization ID"; -597: }; -598: -599: app-name = lib.mkOption { -600: type = lib.types.str; -601: default = "*"; -602: description = "Fly.io app name (or * for all apps)"; -603: }; -604: }; -605: -606: roles-anywhere = { -607: trust-anchor-arn = lib.mkOption { -608: type = lib.types.str; -609: default = ""; -610: description = "ARN of the Roles Anywhere trust anchor"; -611: }; -612: }; -613: }; -614: -615: # IAM configuration -616: iam = { -617: role-name = lib.mkOption { -618: type = lib.types.str; -619: default = "${projectName}-secrets-role"; -620: description = "Name of the IAM role to create"; -621: }; -622: -623: additional-policies = lib.mkOption { -624: type = lib.types.listOf lib.types.str; -625: default = []; -626: description = "Additional IAM policy ARNs to attach to the role"; -627: }; -628: }; -629: }; -630: -631: config = lib.mkMerge [ -632: # ========================================================================= -633: # Force KMS on when variables backend is "chamber" -634: # ========================================================================= -635: (lib.mkIf (config.stack.secrets.backend == "chamber") { -636: stack.sst.kms.enable = lib.mkForce true; -637: }) -638: -639: (lib.mkIf cfg.enable { -640: # ========================================================================= -641: # Bun catalog — register actual versions for "catalog:" references -642: # ========================================================================= -643: stack.bun.catalog = { -644: sst = "^3.17.25"; -645: "@pulumi/aws" = "^7.15.0"; -646: }; -647: -648: # ========================================================================= -649: # Extension Registration -650: # ========================================================================= -651: # Register SST as a builtin extension so it appears in the extensions UI -652: # and can be managed like any other extension. -653: stack.extensions.sst = { -654: name = "SST Infrastructure"; -655: enabled = true; -656: builtin = true; -657: priority = 10; # Load early since other extensions may depend on AWS infra -658: -659: # Source directory for file-based scripts (reference implementation) -660: # Scripts in src/scripts/ are used via path option below -661: srcDir = ./src; -662: -663: tags = [ -664: "aws" -665: "infrastructure" -666: "secrets" -667: "oidc" -668: ]; -669: - -(Showing lines 430-669 of 863. Use offset=670 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.3s) - -_Thinking:_ - -**Evaluating catalog removal impact** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix", - "offset": 250, - "limit": 140 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix -file -250: cfg.state-store.provider -251: ] -252: deployTemplate; -253: -254: # ============================================================================ -255: # tsconfig.json -256: # ============================================================================ -257: tsconfigJsonValue = { -258: compilerOptions = { -259: target = "ES2022"; -260: module = "ES2022"; -261: moduleResolution = "bundler"; -262: strict = true; -263: esModuleInterop = true; -264: skipLibCheck = true; -265: forceConsistentCasingInFileNames = true; -266: resolveJsonModule = true; -267: declaration = true; -268: declarationMap = true; -269: outDir = "./dist"; -270: rootDir = "."; -271: }; -272: include = [ "src/**/*.ts" ]; -273: exclude = [ -274: "node_modules" -275: "dist" -276: ]; -277: }; -278: -279: # ============================================================================ -280: # Determine dependencies based on enabled helpers -281: # ============================================================================ -282: baseDeps = { -283: alchemy = "catalog:"; -284: }; -285: -286: helperDeps = lib.optionalAttrs cfg.helpers.ssm { -287: "@aws-sdk/client-ssm" = "catalog:"; -288: }; -289: -290: allDeps = baseDeps // helperDeps // cfg.package.extra-dependencies; -291: -292: # ============================================================================ -293: # Flags for conditional features -294: # ============================================================================ -295: hasSecrets = -296: cfg.secrets.state-token-sops-path != null || cfg.secrets.cloudflare-token-sops-path != null; -297: -298: in -299: { -300: config = lib.mkIf cfg.enable { -301: # ========================================================================== -302: # Bun catalog — register actual versions for "catalog:" references -303: # ========================================================================== -304: stack.bun.catalog = { -305: alchemy = cfg.version; -306: } // lib.optionalAttrs cfg.helpers.ssm { -307: "@aws-sdk/client-ssm" = "^3.953.0"; -308: }; -309: -310: # ========================================================================== -311: # File generation -312: # ========================================================================== -313: stack.files.entries = { -314: # Main entry: createApp factory + re-exports -315: "${outputDir}/src/index.ts" = { -316: text = indexTs; -317: mode = "0644"; -318: description = "Alchemy app factory and re-exports (@gen/alchemy)"; -319: source = "alchemy"; -320: }; -321: -322: # State store provider factory -323: "${outputDir}/src/state-store.ts" = { -324: text = stateStoreTs; -325: mode = "0644"; -326: description = "Alchemy state store provider factory"; -327: source = "alchemy"; -328: }; -329: -330: # Shared helpers -331: "${outputDir}/src/helpers.ts" = { -332: text = helpersTs; -333: mode = "0644"; -334: description = "Shared alchemy helpers (SSM, bindings, port)"; -335: source = "alchemy"; -336: }; -337: -338: # TSConfig -339: "${outputDir}/tsconfig.json" = { -340: type = "json"; -341: jsonValue = tsconfigJsonValue; -342: mode = "0644"; -343: description = "TypeScript configuration for @gen/alchemy"; -344: source = "alchemy"; -345: }; -346: -347: # .gitignore -348: ".gitignore".lines = [ -349: ".alchemy" -350: ]; -351: } -352: # Bootstrap file (generated when deploy is enabled with auto-provision) -353: // lib.optionalAttrs (deployCfg.enable && deployCfg.auto-provision-state-store) { -354: "${bootstrapFile}" = { -355: text = bootstrapTs; -356: mode = "0644"; -357: description = "Alchemy state store bootstrap (uses filesystem state to provision CF worker)"; -358: source = "alchemy"; -359: }; -360: }; -361: -362: # ========================================================================== -363: # Turbo workspace package (generates package.json) -364: # ========================================================================== -365: stack.turbo.packages.alchemy = { -366: name = cfg.package.name; -367: path = outputDir; -368: dependencies = allDeps; -369: exports = { -370: "." = { -371: default = "./src/index.ts"; -372: }; -373: "./*" = { -374: default = "./src/*.ts"; -375: }; -376: } -377: // cfg.package.extra-exports; -378: }; -379: -380: # ========================================================================== -381: # Devshell environment -382: # -383: # Wire SOPS-stored tokens to env vars so they're auto-loaded on shell entry. -384: # ========================================================================== -385: stack.devshell.env = -386: lib.optionalAttrs (cfg.secrets.state-token-sops-path != null) { -387: ${cfg.secrets.state-token-env-var} = cfg.secrets.state-token-sops-path; -388: } -389: // lib.optionalAttrs (cfg.secrets.cloudflare-token-sops-path != null) { - -(Showing lines 250-389 of 513. Use offset=390 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 7.9s) - -_Thinking:_ - -**Planning multi-file version patch** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix", - "offset": 1, - "limit": 330 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix -file -1: # ============================================================================== -2: # infra/modules/machines/module.nix -3: # -4: # Machine inventory infra module. -5: # -6: # Accepts a machine inventory definition in Nix and emits a JSON string -7: # output that can be stored via the infra output backend. Colmena consumes -8: # this inventory from stack.infra.outputs.machines. -9: # ============================================================================== -10: { -11: lib, -12: config, -13: ... -14: }: -15: let -16: cfg = config.stack.infra.machines; -17: -18: sshConfigType = lib.types.submodule { -19: options = { -20: user = lib.mkOption { -21: type = lib.types.str; -22: default = "root"; -23: description = "SSH user for connecting to the machine."; -24: }; -25: -26: port = lib.mkOption { -27: type = lib.types.int; -28: default = 22; -29: description = "SSH port for connecting to the machine."; -30: }; -31: -32: keyPath = lib.mkOption { -33: type = lib.types.nullOr lib.types.str; -34: default = null; -35: description = "Path to the SSH private key for this machine."; -36: }; -37: }; -38: }; -39: -40: awsFilterType = lib.types.submodule { -41: options = { -42: name = lib.mkOption { -43: type = lib.types.str; -44: description = "EC2 filter name (e.g., instance-state-name, tag:Name)."; -45: }; -46: -47: values = lib.mkOption { -48: type = lib.types.listOf lib.types.str; -49: default = [ ]; -50: description = "Values for the EC2 filter."; -51: }; -52: }; -53: }; -54: -55: machineType = lib.types.submodule { -56: options = { -57: id = lib.mkOption { -58: type = lib.types.nullOr lib.types.str; -59: default = null; -60: description = "Optional machine identifier (defaults to the attrset key)."; -61: }; -62: -63: name = lib.mkOption { -64: type = lib.types.nullOr lib.types.str; -65: default = null; -66: description = "Human-friendly machine name."; -67: }; -68: -69: host = lib.mkOption { -70: type = lib.types.nullOr lib.types.str; -71: default = null; -72: description = "SSH host or hostname for the machine."; -73: }; -74: -75: ssh = lib.mkOption { -76: type = sshConfigType; -77: default = { }; -78: description = "SSH connection settings for the machine."; -79: }; -80: -81: tags = lib.mkOption { -82: type = lib.types.listOf lib.types.str; -83: default = [ ]; -84: description = "Tags used for grouping and target selection."; -85: }; -86: -87: roles = lib.mkOption { -88: type = lib.types.listOf lib.types.str; -89: default = [ ]; -90: description = "Roles associated with this machine."; -91: }; -92: -93: provider = lib.mkOption { -94: type = lib.types.nullOr lib.types.str; -95: default = null; -96: description = "Infrastructure provider name (aws, gcp, hetzner, etc.)."; -97: }; -98: -99: arch = lib.mkOption { -100: type = lib.types.nullOr lib.types.str; -101: default = null; -102: description = "Target system architecture (e.g., x86_64-linux)."; -103: }; -104: -105: publicIp = lib.mkOption { -106: type = lib.types.nullOr lib.types.str; -107: default = null; -108: description = "Public IPv4/IPv6 address for the machine."; -109: }; -110: -111: privateIp = lib.mkOption { -112: type = lib.types.nullOr lib.types.str; -113: default = null; -114: description = "Private IPv4/IPv6 address for the machine."; -115: }; -116: -117: labels = lib.mkOption { -118: type = lib.types.attrsOf lib.types.str; -119: default = { }; -120: description = "Arbitrary labels attached to the machine."; -121: }; -122: -123: nixosProfile = lib.mkOption { -124: type = lib.types.nullOr lib.types.str; -125: default = null; -126: description = "NixOS profile name to deploy on this machine."; -127: }; -128: -129: nixosModules = lib.mkOption { -130: type = lib.types.listOf lib.types.str; -131: default = [ ]; -132: description = "Extra NixOS modules to include for this machine."; -133: }; -134: -135: targetEnv = lib.mkOption { -136: type = lib.types.nullOr lib.types.str; -137: default = null; -138: description = "Deployment environment label for this machine."; -139: }; -140: -141: env = lib.mkOption { -142: type = lib.types.attrsOf lib.types.str; -143: default = { }; -144: description = "Environment variables applied to this machine."; -145: }; -146: -147: metadata = lib.mkOption { -148: type = lib.types.attrsOf lib.types.anything; -149: default = { }; -150: description = "Extra metadata for downstream tooling."; -151: }; -152: }; -153: }; -154: in -155: { -156: options.stack.infra.machines = { -157: enable = lib.mkOption { -158: type = lib.types.bool; -159: default = false; -160: description = "Enable machine inventory provisioning via infra."; -161: }; -162: -163: source = lib.mkOption { -164: type = lib.types.enum [ -165: "static" -166: "aws-ec2" -167: ]; -168: default = "static"; -169: description = "Machine inventory source (static or AWS EC2)."; -170: }; -171: -172: aws = { -173: region = lib.mkOption { -174: type = lib.types.nullOr lib.types.str; -175: default = config.stack.aws.roles-anywhere.region or null; -176: description = "AWS region for EC2 inventory (falls back to AWS env defaults)."; -177: }; -178: -179: instance-ids = lib.mkOption { -180: type = lib.types.listOf lib.types.str; -181: default = [ ]; -182: description = "Explicit EC2 instance IDs to include in inventory."; -183: }; -184: -185: filters = lib.mkOption { -186: type = lib.types.listOf awsFilterType; -187: default = [ -188: { -189: name = "instance-state-name"; -190: values = [ "running" ]; -191: } -192: ]; -193: description = "EC2 filters for inventory discovery."; -194: }; -195: -196: name-tag-keys = lib.mkOption { -197: type = lib.types.listOf lib.types.str; -198: default = [ "Name" ]; -199: description = "Tag keys used to derive machine names."; -200: }; -201: -202: role-tag-keys = lib.mkOption { -203: type = lib.types.listOf lib.types.str; -204: default = [ -205: "stack:role" -206: "role" -207: ]; -208: description = "Tag keys used to derive machine roles."; -209: }; -210: -211: tag-keys = lib.mkOption { -212: type = lib.types.listOf lib.types.str; -213: default = [ -214: "stack:tag" -215: "tag" -216: ]; -217: description = "Tag keys used to derive machine tags."; -218: }; -219: -220: env-tag-keys = lib.mkOption { -221: type = lib.types.listOf lib.types.str; -222: default = [ -223: "stack:env" -224: "env" -225: "stage" -226: ]; -227: description = "Tag keys used to derive machine target environments."; -228: }; -229: -230: host-preference = lib.mkOption { -231: type = lib.types.listOf (lib.types.enum [ -232: "publicDns" -233: "publicIp" -234: "privateIp" -235: ]); -236: default = [ -237: "publicDns" -238: "publicIp" -239: "privateIp" -240: ]; -241: description = "Preferred host fields for connecting to EC2 machines."; -242: }; -243: -244: ssh = lib.mkOption { -245: type = sshConfigType; -246: default = { }; -247: description = "Default SSH settings for EC2 machines."; -248: }; -249: }; -250: -251: machines = lib.mkOption { -252: type = lib.types.attrsOf machineType; -253: default = { }; -254: description = "Machine inventory definitions to emit via infra outputs."; -255: }; -256: -257: sync-outputs = lib.mkOption { -258: type = lib.types.listOf lib.types.str; -259: default = [ "machines" ]; -260: description = "Which outputs to sync to the storage backend."; -261: }; -262: }; -263: -264: config = lib.mkIf cfg.enable { -265: stack.infra.enable = lib.mkDefault true; -266: -267: stack.infra.modules.machines = { -268: name = "Machine Inventory"; -269: description = "Machine inventory provider for Colmena deployments"; -270: path = ./index.ts; -271: inputs = { -272: source = cfg.source; -273: machines = cfg.machines; -274: aws = { -275: region = cfg.aws.region; -276: instanceIds = cfg.aws.instance-ids; -277: filters = cfg.aws.filters; -278: nameTagKeys = cfg.aws.name-tag-keys; -279: roleTagKeys = cfg.aws.role-tag-keys; -280: tagKeys = cfg.aws.tag-keys; -281: envTagKeys = cfg.aws.env-tag-keys; -282: hostPreference = cfg.aws.host-preference; -283: ssh = { -284: user = cfg.aws.ssh.user; -285: port = cfg.aws.ssh.port; -286: keyPath = cfg.aws.ssh.keyPath; -287: }; -288: }; -289: }; -290: dependencies = lib.optionalAttrs (cfg.source == "aws-ec2") { -291: "@aws-sdk/client-ec2" = "catalog:"; -292: }; -293: outputs = -294: let -295: mkOutput = key: desc: { -296: description = desc; -297: sensitive = false; -298: sync = builtins.elem key cfg.sync-outputs; -299: }; -300: in -301: { -302: machines = mkOutput "machines" "Machine inventory (JSON)"; -303: }; -304: }; -305: }; -306: } - -(End of file - total 306 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix", - "offset": 1, - "limit": 180 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-security-groups/module.nix -3: # -4: # AWS Security Group provisioning module. -5: # ============================================================================== -6: { -7: lib, -8: config, -9: ... -10: }: -11: let -12: cfg = config.stack.infra.aws-security-groups; -13: -14: ruleType = lib.types.submodule { -15: options = { -16: from-port = lib.mkOption { -17: type = lib.types.int; -18: description = "Start port for the rule."; -19: }; -20: -21: to-port = lib.mkOption { -22: type = lib.types.int; -23: description = "End port for the rule."; -24: }; -25: -26: protocol = lib.mkOption { -27: type = lib.types.str; -28: default = "tcp"; -29: description = "Protocol for the rule (tcp, udp, -1)."; -30: }; -31: -32: cidr-blocks = lib.mkOption { -33: type = lib.types.listOf lib.types.str; -34: default = [ ]; -35: description = "IPv4 CIDR blocks for the rule."; -36: }; -37: -38: ipv6-cidr-blocks = lib.mkOption { -39: type = lib.types.listOf lib.types.str; -40: default = [ ]; -41: description = "IPv6 CIDR blocks for the rule."; -42: }; -43: -44: security-group-ids = lib.mkOption { -45: type = lib.types.listOf lib.types.str; -46: default = [ ]; -47: description = "Source/target security group IDs for the rule."; -48: }; -49: -50: description = lib.mkOption { -51: type = lib.types.nullOr lib.types.str; -52: default = null; -53: description = "Optional rule description."; -54: }; -55: }; -56: }; -57: -58: groupType = lib.types.submodule { -59: options = { -60: name = lib.mkOption { -61: type = lib.types.str; -62: description = "Security group name."; -63: }; -64: -65: description = lib.mkOption { -66: type = lib.types.nullOr lib.types.str; -67: default = null; -68: description = "Security group description."; -69: }; -70: -71: ingress = lib.mkOption { -72: type = lib.types.listOf ruleType; -73: default = [ ]; -74: description = "Ingress rules."; -75: }; -76: -77: egress = lib.mkOption { -78: type = lib.types.listOf ruleType; -79: default = [ ]; -80: description = "Egress rules."; -81: }; -82: -83: tags = lib.mkOption { -84: type = lib.types.attrsOf lib.types.str; -85: default = { }; -86: description = "Tags applied to the security group."; -87: }; -88: }; -89: }; -90: in -91: { -92: options.stack.infra.aws-security-groups = { -93: enable = lib.mkOption { -94: type = lib.types.bool; -95: default = false; -96: description = "Enable AWS security group provisioning."; -97: }; -98: -99: vpc-id = lib.mkOption { -100: type = lib.types.str; -101: description = "VPC ID for security groups."; -102: }; -103: -104: groups = lib.mkOption { -105: type = lib.types.listOf groupType; -106: default = [ ]; -107: description = "Security group definitions."; -108: }; -109: -110: sync-outputs = lib.mkOption { -111: type = lib.types.listOf lib.types.str; -112: default = [ "groupIds" ]; -113: description = "Which outputs to sync to the storage backend."; -114: }; -115: }; -116: -117: config = lib.mkIf cfg.enable { -118: stack.infra.enable = lib.mkDefault true; -119: -120: stack.infra.modules.aws-security-groups = { -121: name = "AWS Security Groups"; -122: description = "Provision security groups in a VPC"; -123: path = ./index.ts; -124: inputs = { -125: vpcId = cfg.vpc-id; -126: groups = cfg.groups; -127: }; -128: dependencies = { -129: "@aws-sdk/client-ec2" = "catalog:"; -130: }; -131: outputs = -132: let -133: mkOutput = key: desc: { -134: description = desc; -135: sensitive = false; -136: sync = builtins.elem key cfg.sync-outputs; -137: }; -138: in -139: { -140: groupIds = mkOutput "groupIds" "Security group IDs (JSON)"; -141: }; -142: }; -143: }; -144: } - -(End of file - total 144 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix", - "offset": 1, - "limit": 320 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-secrets/module.nix -3: # -4: # AWS Secrets Infrastructure infra module. -5: # -6: # Provisions: -7: # - GitHub Actions / Fly.io / Roles Anywhere OIDC provider -8: # - IAM role with OIDC trust policy -9: # - KMS key + alias for secrets encryption -10: # - SSM Parameter Store policies for secrets group key storage -11: # -12: # This is the direct replacement for the SST-based stack.sst module. -13: # -14: # Usage: -15: # stack.infra.enable = true; -16: # stack.infra.aws.secrets = { -17: # enable = true; -18: # oidc.provider = "github-actions"; -19: # oidc.github-actions = { org = "my-org"; repo = "my-repo"; }; -20: # }; -21: # -22: # ============================================================================== -23: { -24: lib, -25: config, -26: ... -27: }: -28: let -29: cfg = config.stack.infra.aws.secrets; -30: -31: # Inherited defaults from core config -32: projectName = config.stack.name or "my-project"; -33: -34: # AWS config (inherit from roles-anywhere if available) -35: awsCfg = config.stack.aws.roles-anywhere or { }; -36: defaultRegion = awsCfg.region or "us-west-2"; -37: defaultAccountId = awsCfg.account-id or ""; -38: -39: # GitHub org/repo from project config -40: projectCfg = config.stack.project; -41: defaultGithubOrg = projectCfg.owner; -42: defaultGithubRepo = if projectCfg.repo != "" then projectCfg.repo else "*"; -43: -44: # Secrets groups config (for SSM key paths) -45: secretsGroups = config.stack.secrets.groups or { }; -46: chamberPrefix = config.stack.secrets.chamber.service-prefix or projectName; -47: -48: # Collect all SSM paths from groups -49: groupSsmPaths = lib.mapAttrsToList (_: g: g.ssm-path) secretsGroups; -50: -51: # Compute the SSM path prefix for wildcard IAM policy -52: # All group keys live under /{prefix}/keys/* -53: ssmKeyPrefix = "/${chamberPrefix}/keys"; -54: -55: in -56: { -57: # ============================================================================ -58: # Options -59: # ============================================================================ -60: options.stack.infra.aws.secrets = { -61: enable = lib.mkOption { -62: type = lib.types.bool; -63: default = true; -64: description = '' -65: Enable AWS secrets infrastructure (OIDC + IAM + KMS). -66: Enabled by default — set to false to explicitly disable. -67: ''; -68: }; -69: -70: region = lib.mkOption { -71: type = lib.types.str; -72: default = defaultRegion; -73: description = "AWS region (inherits from stack.aws.roles-anywhere.region)"; -74: }; -75: -76: account-id = lib.mkOption { -77: type = lib.types.str; -78: default = defaultAccountId; -79: description = "AWS account ID (inherits from stack.aws.roles-anywhere.account-id)"; -80: }; -81: -82: # KMS configuration -83: kms = { -84: alias = lib.mkOption { -85: type = lib.types.str; -86: default = "${projectName}-secrets"; -87: description = "KMS key alias (without 'alias/' prefix)"; -88: }; -89: -90: deletion-window-days = lib.mkOption { -91: type = lib.types.int; -92: default = 30; -93: description = "Number of days before KMS key deletion (7-30)"; -94: }; -95: }; -96: -97: # IAM configuration -98: iam = { -99: role-name = lib.mkOption { -100: type = lib.types.str; -101: default = "${projectName}-secrets-role"; -102: description = "Name of the IAM role to create"; -103: }; -104: -105: additional-policies = lib.mkOption { -106: type = lib.types.listOf lib.types.str; -107: default = [ ]; -108: description = "Additional managed policy ARNs to attach to the role"; -109: }; -110: }; -111: -112: # OIDC configuration -113: oidc = { -114: provider = lib.mkOption { -115: type = lib.types.enum [ -116: "github-actions" -117: "flyio" -118: "roles-anywhere" -119: "none" -120: ]; -121: default = "github-actions"; -122: description = "OIDC provider type for IAM role assumption"; -123: }; -124: -125: github-actions = { -126: org = lib.mkOption { -127: type = lib.types.str; -128: default = defaultGithubOrg; -129: description = "GitHub organization name"; -130: }; -131: -132: repo = lib.mkOption { -133: type = lib.types.str; -134: default = defaultGithubRepo; -135: description = "GitHub repository name (or * for all repos in org)"; -136: }; -137: -138: branch = lib.mkOption { -139: type = lib.types.str; -140: default = "*"; -141: description = "Branch filter for OIDC subject"; -142: }; -143: }; -144: -145: flyio = { -146: org-id = lib.mkOption { -147: type = lib.types.str; -148: default = ""; -149: description = "Fly.io organization ID"; -150: }; -151: -152: app-name = lib.mkOption { -153: type = lib.types.str; -154: default = "*"; -155: description = "Fly.io app name (or * for all apps)"; -156: }; -157: }; -158: -159: roles-anywhere = { -160: trust-anchor-arn = lib.mkOption { -161: type = lib.types.str; -162: default = ""; -163: description = "ARN of the Roles Anywhere trust anchor"; -164: }; -165: }; -166: }; -167: -168: # SSM configuration for secrets group keys -169: ssm = { -170: enable = lib.mkOption { -171: type = lib.types.bool; -172: default = true; -173: description = '' -174: Enable SSM Parameter Store IAM policies for secrets group key storage. -175: When enabled, the IAM role gets ssm:GetParameter and ssm:PutParameter -176: permissions for the group key paths. -177: ''; -178: }; -179: -180: key-prefix = lib.mkOption { -181: type = lib.types.str; -182: default = ssmKeyPrefix; -183: description = '' -184: SSM path prefix for group AGE keys. -185: Default: /{chamber.service-prefix}/keys -186: The IAM policy grants access to {key-prefix}/* -187: ''; -188: }; -189: -190: additional-paths = lib.mkOption { -191: type = lib.types.listOf lib.types.str; -192: default = [ ]; -193: description = '' -194: Additional SSM parameter paths to grant access to. -195: These are added alongside the group key paths. -196: Supports wildcards (e.g., "/my-app/secrets/*"). -197: ''; -198: }; -199: }; -200: -201: # Output sync configuration -202: sync-outputs = lib.mkOption { -203: type = lib.types.listOf lib.types.str; -204: default = [ -205: "roleArn" -206: "roleName" -207: "kmsKeyArn" -208: "kmsKeyId" -209: "kmsAliasName" -210: "oidcProviderArn" -211: ]; -212: description = "Which outputs to sync to the storage backend"; -213: }; -214: }; -215: -216: # ============================================================================ -217: # Config: register in infra.modules -218: # ============================================================================ -219: config = lib.mkIf cfg.enable { -220: # Auto-enable the infra system when this module is active -221: stack.infra.enable = lib.mkDefault true; -222: -223: stack.infra.modules.aws-secrets = { -224: name = "AWS Secrets Infrastructure"; -225: description = "OIDC provider, IAM role, and KMS key for secrets management"; -226: path = ./module; -227: inputs = { -228: region = cfg.region; -229: accountId = cfg.account-id; -230: projectName = projectName; -231: kms = { -232: alias = cfg.kms.alias; -233: deletionWindowDays = cfg.kms.deletion-window-days; -234: }; -235: iam = { -236: roleName = cfg.iam.role-name; -237: additionalPolicies = cfg.iam.additional-policies; -238: }; -239: oidc = { -240: provider = cfg.oidc.provider; -241: githubActions = { -242: org = cfg.oidc.github-actions.org; -243: repo = cfg.oidc.github-actions.repo; -244: branch = cfg.oidc.github-actions.branch; -245: }; -246: flyio = { -247: orgId = cfg.oidc.flyio.org-id; -248: appName = cfg.oidc.flyio.app-name; -249: }; -250: rolesAnywhere = { -251: trustAnchorArn = cfg.oidc.roles-anywhere.trust-anchor-arn; -252: }; -253: }; -254: ssm = { -255: enable = cfg.ssm.enable; -256: keyPrefix = cfg.ssm.key-prefix; -257: additionalPaths = cfg.ssm.additional-paths; -258: groupPaths = groupSsmPaths; -259: }; -260: }; -261: dependencies = { -262: "@aws-sdk/client-sts" = "catalog:"; # AccountId from alchemy/aws -263: "@aws-sdk/client-iam" = "catalog:"; # Role, GitHubOIDCProvider -264: "@aws-sdk/client-kms" = "catalog:"; # KmsKey, KmsAlias -265: "@aws-sdk/client-ssm" = "catalog:"; # SSM for group key storage -266: }; -267: outputs = -268: let -269: # Build output declarations — all default to sync=true if in sync-outputs list -270: mkOutput = key: desc: { -271: description = desc; -272: sensitive = false; -273: sync = builtins.elem key cfg.sync-outputs; -274: }; -275: in -276: { -277: roleArn = mkOutput "roleArn" "IAM role ARN for OIDC authentication"; -278: roleName = mkOutput "roleName" "IAM role name"; -279: kmsKeyArn = mkOutput "kmsKeyArn" "KMS key ARN for secrets encryption"; -280: kmsKeyId = mkOutput "kmsKeyId" "KMS key ID"; -281: kmsAliasName = mkOutput "kmsAliasName" "KMS key alias name"; -282: oidcProviderArn = mkOutput "oidcProviderArn" "OIDC provider ARN"; -283: }; -284: }; -285: }; -286: } - -(End of file - total 286 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.2s) - -_Thinking:_ - - - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix", - "offset": 1, - "limit": 130 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-key-pairs/module.nix -3: # -4: # AWS Key Pair provisioning module. -5: # ============================================================================== -6: { -7: lib, -8: config, -9: ... -10: }: -11: let -12: cfg = config.stack.infra.aws-key-pairs; -13: -14: keyType = lib.types.submodule { -15: options = { -16: public-key = lib.mkOption { -17: type = lib.types.str; -18: description = "Public key material for the key pair."; -19: }; -20: -21: tags = lib.mkOption { -22: type = lib.types.attrsOf lib.types.str; -23: default = { }; -24: description = "Tags applied to the key pair."; -25: }; -26: -27: destroy-on-delete = lib.mkOption { -28: type = lib.types.bool; -29: default = false; -30: description = "Delete the key pair when running alchemy destroy."; -31: }; -32: }; -33: }; -34: in -35: { -36: options.stack.infra.aws-key-pairs = { -37: enable = lib.mkOption { -38: type = lib.types.bool; -39: default = false; -40: description = "Enable AWS key pair provisioning."; -41: }; -42: -43: keys = lib.mkOption { -44: type = lib.types.attrsOf keyType; -45: default = { }; -46: description = "Key pair definitions keyed by key name."; -47: }; -48: -49: sync-outputs = lib.mkOption { -50: type = lib.types.listOf lib.types.str; -51: default = [ "keyNames" "keyPairIds" ]; -52: description = "Which outputs to sync to the storage backend."; -53: }; -54: }; -55: -56: config = lib.mkIf cfg.enable { -57: stack.infra.enable = lib.mkDefault true; -58: -59: stack.infra.modules.aws-key-pairs = { -60: name = "AWS Key Pairs"; -61: description = "Import or create EC2 key pairs"; -62: path = ./index.ts; -63: inputs = { -64: keys = cfg.keys; -65: }; -66: dependencies = { -67: "@aws-sdk/client-ec2" = "catalog:"; -68: }; -69: outputs = -70: let -71: mkOutput = key: desc: { -72: description = desc; -73: sensitive = false; -74: sync = builtins.elem key cfg.sync-outputs; -75: }; -76: in -77: { -78: keyNames = mkOutput "keyNames" "Key pair names (JSON)"; -79: keyPairIds = mkOutput "keyPairIds" "Key pair IDs (JSON)"; -80: }; -81: }; -82: }; -83: } - -(End of file - total 83 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix", - "offset": 1, - "limit": 150 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-network/module.nix -3: # -4: # AWS network discovery module (default VPC + subnets). -5: # -6: # Discovers VPC and subnet IDs and exposes them as infra outputs for use -7: # in downstream modules. -8: # ============================================================================== -9: { -10: lib, -11: config, -12: ... -13: }: -14: let -15: cfg = config.stack.infra.aws-network; -16: in -17: { -18: options.stack.infra.aws-network = { -19: enable = lib.mkOption { -20: type = lib.types.bool; -21: default = false; -22: description = "Enable AWS network discovery (VPC + subnets)."; -23: }; -24: -25: region = lib.mkOption { -26: type = lib.types.nullOr lib.types.str; -27: default = config.stack.aws.roles-anywhere.region or null; -28: description = "AWS region for network discovery (defaults to AWS env)."; -29: }; -30: -31: vpc = { -32: id = lib.mkOption { -33: type = lib.types.nullOr lib.types.str; -34: default = null; -35: description = "Optional VPC ID to use (otherwise discover default VPC)."; -36: }; -37: -38: use-default = lib.mkOption { -39: type = lib.types.bool; -40: default = true; -41: description = "Use the AWS default VPC when no VPC ID is provided."; -42: }; -43: }; -44: -45: subnets = { -46: ids = lib.mkOption { -47: type = lib.types.listOf lib.types.str; -48: default = [ ]; -49: description = "Optional subnet IDs to use (otherwise discover from VPC)."; -50: }; -51: -52: use-default = lib.mkOption { -53: type = lib.types.bool; -54: default = true; -55: description = "Discover subnets from the resolved VPC when no IDs are provided."; -56: }; -57: }; -58: -59: sync-outputs = lib.mkOption { -60: type = lib.types.listOf lib.types.str; -61: default = [ "vpcId" "subnetIds" ]; -62: description = "Which outputs to sync to the storage backend."; -63: }; -64: }; -65: -66: config = lib.mkIf cfg.enable { -67: stack.infra.enable = lib.mkDefault true; -68: -69: stack.infra.modules.aws-network = { -70: name = "AWS Network"; -71: description = "Discover default VPC and subnet IDs"; -72: path = ./index.ts; -73: inputs = { -74: region = cfg.region; -75: vpc = { -76: id = cfg.vpc.id; -77: useDefault = cfg.vpc.use-default; -78: }; -79: subnets = { -80: ids = cfg.subnets.ids; -81: useDefault = cfg.subnets.use-default; -82: }; -83: }; -84: dependencies = { -85: "@aws-sdk/client-ec2" = "catalog:"; -86: }; -87: outputs = -88: let -89: mkOutput = key: desc: { -90: description = desc; -91: sensitive = false; -92: sync = builtins.elem key cfg.sync-outputs; -93: }; -94: in -95: { -96: vpcId = mkOutput "vpcId" "Resolved VPC ID"; -97: subnetIds = mkOutput "subnetIds" "Resolved subnet IDs (JSON)"; -98: subnetAzs = mkOutput "subnetAzs" "Resolved subnet AZs (JSON)"; -99: }; -100: }; -101: }; -102: } - -(End of file - total 102 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix", - "offset": 1, - "limit": 160 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-iam/module.nix -3: # -4: # AWS IAM role + instance profile module (EC2 oriented). -5: # ============================================================================== -6: { -7: lib, -8: config, -9: ... -10: }: -11: let -12: cfg = config.stack.infra.aws-iam; -13: -14: inlinePolicyType = lib.types.submodule { -15: options = { -16: name = lib.mkOption { -17: type = lib.types.str; -18: description = "Inline policy name."; -19: }; -20: -21: document = lib.mkOption { -22: type = lib.types.attrsOf lib.types.anything; -23: description = "IAM policy document (JSON object)."; -24: }; -25: }; -26: }; -27: in -28: { -29: options.stack.infra.aws-iam = { -30: enable = lib.mkOption { -31: type = lib.types.bool; -32: default = false; -33: description = "Enable AWS IAM role + instance profile provisioning."; -34: }; -35: -36: role = { -37: name = lib.mkOption { -38: type = lib.types.str; -39: description = "IAM role name."; -40: }; -41: -42: assume-role-policy = lib.mkOption { -43: type = lib.types.nullOr (lib.types.attrsOf lib.types.anything); -44: default = null; -45: description = "Optional assume role policy document (defaults to EC2 trust)."; -46: }; -47: -48: managed-policy-arns = lib.mkOption { -49: type = lib.types.listOf lib.types.str; -50: default = [ ]; -51: description = "Managed policy ARNs to attach."; -52: }; -53: -54: inline-policies = lib.mkOption { -55: type = lib.types.listOf inlinePolicyType; -56: default = [ ]; -57: description = "Inline policies to attach to the role."; -58: }; -59: -60: tags = lib.mkOption { -61: type = lib.types.attrsOf lib.types.str; -62: default = { }; -63: description = "Tags applied to the IAM role."; -64: }; -65: }; -66: -67: instance-profile = { -68: name = lib.mkOption { -69: type = lib.types.nullOr lib.types.str; -70: default = null; -71: description = "Instance profile name (defaults to role name + '-profile')."; -72: }; -73: -74: tags = lib.mkOption { -75: type = lib.types.attrsOf lib.types.str; -76: default = { }; -77: description = "Tags applied to the instance profile."; -78: }; -79: }; -80: -81: sync-outputs = lib.mkOption { -82: type = lib.types.listOf lib.types.str; -83: default = [ "roleArn" "roleName" "instanceProfileArn" "instanceProfileName" ]; -84: description = "Which outputs to sync to the storage backend."; -85: }; -86: }; -87: -88: config = lib.mkIf cfg.enable { -89: stack.infra.enable = lib.mkDefault true; -90: -91: stack.infra.modules.aws-iam = { -92: name = "AWS IAM"; -93: description = "IAM role and instance profile provisioning"; -94: path = ./index.ts; -95: inputs = { -96: role = { -97: name = cfg.role.name; -98: assumeRolePolicy = cfg.role.assume-role-policy; -99: managedPolicyArns = cfg.role.managed-policy-arns; -100: inlinePolicies = cfg.role.inline-policies; -101: tags = cfg.role.tags; -102: }; -103: instanceProfile = { -104: name = cfg.instance-profile.name; -105: tags = cfg.instance-profile.tags; -106: }; -107: }; -108: dependencies = { -109: "@aws-sdk/client-iam" = "catalog:"; -110: }; -111: outputs = -112: let -113: mkOutput = key: desc: { -114: description = desc; -115: sensitive = false; -116: sync = builtins.elem key cfg.sync-outputs; -117: }; -118: in -119: { -120: roleArn = mkOutput "roleArn" "IAM role ARN"; -121: roleName = mkOutput "roleName" "IAM role name"; -122: instanceProfileArn = mkOutput "instanceProfileArn" "Instance profile ARN"; -123: instanceProfileName = mkOutput "instanceProfileName" "Instance profile name"; -124: }; -125: }; -126: }; -127: } - -(End of file - total 127 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.0s) - -_Thinking:_ - -**Listing required infrastructure components** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix", - "offset": 1, - "limit": 250 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix -file -1: # ============================================================================== -2: # infra/modules/aws-ec2/module.nix -3: # -4: # AWS EC2 instance provisioning module. -5: # ============================================================================== -6: { -7: lib, -8: config, -9: ... -10: }: -11: let -12: cfg = config.stack.infra.aws-ec2; -13: -14: sshType = lib.types.submodule { -15: options = { -16: user = lib.mkOption { -17: type = lib.types.str; -18: default = "root"; -19: description = "SSH user for the instance."; -20: }; -21: -22: port = lib.mkOption { -23: type = lib.types.int; -24: default = 22; -25: description = "SSH port for the instance."; -26: }; -27: -28: key-path = lib.mkOption { -29: type = lib.types.nullOr lib.types.str; -30: default = null; -31: description = "SSH private key path for the instance."; -32: }; -33: }; -34: }; -35: -36: machineMetaType = lib.types.submodule { -37: options = { -38: tags = lib.mkOption { -39: type = lib.types.listOf lib.types.str; -40: default = [ ]; -41: description = "Machine tags for Colmena targeting."; -42: }; -43: -44: roles = lib.mkOption { -45: type = lib.types.listOf lib.types.str; -46: default = [ ]; -47: description = "Machine roles for Colmena targeting."; -48: }; -49: -50: target-env = lib.mkOption { -51: type = lib.types.nullOr lib.types.str; -52: default = null; -53: description = "Deployment environment label for the machine."; -54: }; -55: -56: arch = lib.mkOption { -57: type = lib.types.nullOr lib.types.str; -58: default = null; -59: description = "Target system architecture (e.g., x86_64-linux)."; -60: }; -61: -62: ssh = lib.mkOption { -63: type = sshType; -64: default = { }; -65: description = "SSH settings for the instance."; -66: }; -67: }; -68: }; -69: -70: instanceType = lib.types.submodule { -71: options = { -72: name = lib.mkOption { -73: type = lib.types.str; -74: description = "Instance name."; -75: }; -76: -77: ami = lib.mkOption { -78: type = lib.types.str; -79: description = "AMI ID."; -80: }; -81: -82: instance-type = lib.mkOption { -83: type = lib.types.str; -84: default = "t3.micro"; -85: description = "EC2 instance type."; -86: }; -87: -88: subnet-id = lib.mkOption { -89: type = lib.types.str; -90: description = "Subnet ID for the instance."; -91: }; -92: -93: security-group-ids = lib.mkOption { -94: type = lib.types.listOf lib.types.str; -95: default = [ ]; -96: description = "Security group IDs for the instance."; -97: }; -98: -99: key-name = lib.mkOption { -100: type = lib.types.nullOr lib.types.str; -101: default = null; -102: description = "EC2 key pair name."; -103: }; -104: -105: iam-instance-profile = lib.mkOption { -106: type = lib.types.nullOr lib.types.str; -107: default = null; -108: description = "IAM instance profile name."; -109: }; -110: -111: user-data = lib.mkOption { -112: type = lib.types.nullOr lib.types.str; -113: default = null; -114: description = "User data script."; -115: }; -116: -117: root-volume-size = lib.mkOption { -118: type = lib.types.nullOr lib.types.int; -119: default = null; -120: description = "Root volume size in GB."; -121: }; -122: -123: associate-public-ip = lib.mkOption { -124: type = lib.types.bool; -125: default = true; -126: description = "Associate a public IP address."; -127: }; -128: -129: tags = lib.mkOption { -130: type = lib.types.attrsOf lib.types.str; -131: default = { }; -132: description = "Tags applied to the EC2 instance."; -133: }; -134: -135: machine = lib.mkOption { -136: type = machineMetaType; -137: default = { }; -138: description = "Machine metadata for Colmena."; -139: }; -140: }; -141: }; -142: in -143: { -144: options.stack.infra.aws-ec2 = { -145: enable = lib.mkOption { -146: type = lib.types.bool; -147: default = false; -148: description = "Enable AWS EC2 instance provisioning."; -149: }; -150: -151: instances = lib.mkOption { -152: type = lib.types.listOf instanceType; -153: default = [ ]; -154: description = "EC2 instance definitions."; -155: }; -156: -157: defaults = lib.mkOption { -158: type = instanceType; -159: default = { -160: name = "default"; -161: ami = ""; -162: instance-type = "t3.micro"; -163: subnet-id = ""; -164: }; -165: description = "Default values merged into each instance."; -166: }; -167: -168: sync-outputs = lib.mkOption { -169: type = lib.types.listOf lib.types.str; -170: default = [ "instanceIds" "publicIps" "publicDns" "privateIps" "machines" ]; -171: description = "Which outputs to sync to the storage backend."; -172: }; -173: }; -174: -175: config = lib.mkIf cfg.enable { -176: stack.infra.enable = lib.mkDefault true; -177: -178: stack.infra.modules.aws-ec2 = { -179: name = "AWS EC2"; -180: description = "Provision EC2 instances"; -181: path = ./index.ts; -182: inputs = { -183: defaults = cfg.defaults; -184: instances = cfg.instances; -185: }; -186: dependencies = { -187: "@aws-sdk/client-ec2" = "catalog:"; -188: }; -189: outputs = -190: let -191: mkOutput = key: desc: { -192: description = desc; -193: sensitive = false; -194: sync = builtins.elem key cfg.sync-outputs; -195: }; -196: in -197: { -198: instanceIds = mkOutput "instanceIds" "Instance IDs (JSON)"; -199: publicIps = mkOutput "publicIps" "Instance public IPs (JSON)"; -200: publicDns = mkOutput "publicDns" "Instance public DNS (JSON)"; -201: privateIps = mkOutput "privateIps" "Instance private IPs (JSON)"; -202: machines = mkOutput "machines" "Machine inventory (JSON)"; -203: }; -204: }; -205: }; -206: } - -(End of file - total 206 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix", - "offset": 840, - "limit": 100 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix -file -840: description = "SSM parameter wiring for app environment."; -841: }; -842: -843: machine = lib.mkOption { -844: type = machineMetaType; -845: default = { }; -846: description = "Machine metadata defaults for Colmena."; -847: }; -848: }; -849: }; -850: in -851: { -852: options.stack.infra.aws-ec2-app = { -853: enable = lib.mkOption { -854: type = lib.types.bool; -855: default = false; -856: description = "Enable EC2 app provisioning."; -857: }; -858: -859: defaults = lib.mkOption { -860: type = appType; -861: default = { }; -862: description = "Default settings applied to all apps."; -863: }; -864: -865: apps = lib.mkOption { -866: type = lib.types.attrsOf appType; -867: default = { }; -868: description = "App definitions keyed by app name."; -869: }; -870: -871: sync-outputs = lib.mkOption { -872: type = lib.types.listOf lib.types.str; -873: default = [ "instanceIds" "publicIps" "publicDns" "privateIps" "machines" "albOutputs" "ecrOutputs" "ssmOutputs" ]; -874: description = "Which outputs to sync to the storage backend."; -875: }; -876: }; -877: -878: config = lib.mkIf cfg.enable { -879: stack.infra.enable = lib.mkDefault true; -880: -881: stack.infra.modules.aws-ec2-app = { -882: name = "AWS EC2 Apps"; -883: description = "Provision EC2 instances for apps and emit machine inventory"; -884: path = ./index.ts; -885: inputs = { -886: defaults = cfg.defaults; -887: apps = cfg.apps; -888: }; -889: dependencies = { -890: "@aws-sdk/client-ec2" = "catalog:"; -891: "@aws-sdk/client-iam" = "catalog:"; -892: "@aws-sdk/client-elastic-load-balancing-v2" = "catalog:"; -893: "@aws-sdk/client-ecr" = "catalog:"; -894: "@aws-sdk/client-ssm" = "catalog:"; -895: "@aws-sdk/client-sts" = "catalog:"; -896: }; -897: outputs = -898: let -899: mkOutput = key: desc: { -900: description = desc; -901: sensitive = false; -902: sync = builtins.elem key cfg.sync-outputs; -903: }; -904: in -905: { -906: instanceIds = mkOutput "instanceIds" "Instance IDs (JSON)"; -907: publicIps = mkOutput "publicIps" "Instance public IPs (JSON)"; -908: publicDns = mkOutput "publicDns" "Instance public DNS (JSON)"; -909: privateIps = mkOutput "privateIps" "Instance private IPs (JSON)"; -910: machines = mkOutput "machines" "Machine inventory (JSON)"; -911: albOutputs = mkOutput "albOutputs" "ALB outputs per app (JSON)"; -912: ecrOutputs = mkOutput "ecrOutputs" "ECR outputs per app (JSON)"; -913: ssmOutputs = mkOutput "ssmOutputs" "SSM outputs per app (JSON)"; -914: }; -915: }; -916: }; -917: } - -(End of file - total 917 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix", - "offset": 220, - "limit": 50 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix -file -220: path = ./index.ts; -221: inputs = { -222: inherit projectName; -223: inherit (cfg) name provider; -224: neon = { -225: inherit (cfg.neon) region enable-branching; -226: pgVersion = cfg.neon.pg-version; -227: apiKeySsmPath = cfg.neon.api-key-ssm-path; -228: }; -229: devenv = { -230: inherit (cfg.devenv) database user password host port; -231: }; -232: docker = { -233: inherit (cfg.docker) image tag user password port network; -234: }; -235: ssm = { -236: inherit (cfg.ssm) enable; -237: pathPrefix = cfg.ssm.path-prefix; -238: }; -239: }; -240: dependencies = { -241: "alchemy" = "catalog:"; -242: } -243: // lib.optionalAttrs (config.stack.alchemy.enable) { -244: ${config.stack.alchemy.package.name} = "workspace:*"; -245: }; -246: outputs = { -247: databaseUrl = { -248: description = "PostgreSQL connection URL"; -249: sensitive = true; -250: sync = true; -251: }; -252: provider = { -253: description = "Active database provider (neon, devenv, docker)"; -254: sync = true; -255: }; -256: }; -257: }; -258: }; -259: } - -(End of file - total 259 lines) - -``` - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix", - "offset": 150, - "limit": 60 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix -file -150: # Config: register in infra module system -151: # ============================================================================ -152: config = lib.mkIf (infraCfg.enable && cfg.enable) { -153: stack.infra.modules.cache = { -154: name = "Cache"; -155: description = "Redis/Valkey cache provisioning (Upstash / devenv / Docker)"; -156: path = ./index.ts; -157: inputs = { -158: inherit projectName; -159: inherit (cfg) provider; -160: upstash = { -161: inherit (cfg.upstash) region; -162: apiKeySsmPath = cfg.upstash.api-key-ssm-path; -163: emailSsmPath = cfg.upstash.email-ssm-path; -164: }; -165: devenv = { -166: inherit (cfg.devenv) host port; -167: }; -168: docker = { -169: inherit (cfg.docker) image tag port network; -170: }; -171: ssm = { -172: inherit (cfg.ssm) enable; -173: pathPrefix = cfg.ssm.path-prefix; -174: }; -175: }; -176: dependencies = { -177: "alchemy" = "catalog:"; -178: } -179: // lib.optionalAttrs (config.stack.alchemy.enable) { -180: ${config.stack.alchemy.package.name} = "workspace:*"; -181: }; -182: outputs = { -183: redisUrl = { -184: description = "Redis connection URL (endpoint)"; -185: sync = true; -186: }; -187: redisToken = { -188: description = "Redis REST token (Upstash only)"; -189: sensitive = true; -190: sync = true; -191: }; -192: provider = { -193: description = "Active cache provider (upstash, devenv, docker)"; -194: sync = true; -195: }; -196: }; -197: }; -198: }; -199: } - -(End of file - total 199 lines) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 33.5s) - -_Thinking:_ - -**Removing catalog dependency references** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 770, - "limit": 40 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -770: }; -771: -772: # Add your module options here. Example: -773: # region = lib.mkOption { -774: # type = lib.types.str; -775: # default = "us-east-1"; -776: # description = "AWS region."; -777: # }; -778: }; -779: -780: config = lib.mkIf cfg.enable { -781: stack.infra.enable = lib.mkDefault true; -782: -783: stack.infra.modules.MODULE_ID = { -784: name = "MODULE_NAME"; -785: description = "MODULE_NAME infrastructure provisioning"; -786: path = ./index.ts; -787: -788: # Nix values serialized to JSON and available at runtime -789: # via infra.inputs() in the TypeScript file. -790: inputs = { -791: # Add your input values here. Example: -792: # region = cfg.region; -793: }; -794: -795: # NPM packages required by the TypeScript implementation. -796: dependencies = { -797: # "@aws-sdk/client-s3" = "catalog:"; -798: }; -799: -800: # Outputs the TypeScript module's default export must provide. -801: # Keys with sync=true are written to the storage backend. -802: outputs = { -803: # exampleArn = { -804: # description = "ARN of the provisioned resource"; -805: # sensitive = false; -806: # sync = true; -807: # }; -808: }; -809: }; - -(Showing lines 770-809 of 1181. Use offset=810 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - -**Preparing to patch all comments** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/machines/module.nix\n@@\n dependencies = lib.optionalAttrs (cfg.source == \"aws-ec2\") {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/machines/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.9s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-security-groups/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-security-groups/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-secrets/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-sts\" = \"catalog:\"; # AccountId from alchemy/aws\n- \"@aws-sdk/client-iam\" = \"catalog:\"; # Role, GitHubOIDCProvider\n- \"@aws-sdk/client-kms\" = \"catalog:\"; # KmsKey, KmsAlias\n- \"@aws-sdk/client-ssm\" = \"catalog:\"; # SSM for group key storage\n+ \"@aws-sdk/client-sts\" = \"^3.953.0\"; # AccountId from alchemy/aws\n+ \"@aws-sdk/client-iam\" = \"^3.953.0\"; # Role, GitHubOIDCProvider\n+ \"@aws-sdk/client-kms\" = \"^3.953.0\"; # KmsKey, KmsAlias\n+ \"@aws-sdk/client-ssm\" = \"^3.953.0\"; # SSM for group key storage\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-secrets/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.6s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-key-pairs/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-key-pairs/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.9s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-network/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-network/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.7s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-iam/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-iam\" = \"catalog:\";\n+ \"@aws-sdk/client-iam\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-iam/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.7s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-ec2/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.3s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/aws-ec2-app/module.nix\n@@\n dependencies = {\n- \"@aws-sdk/client-ec2\" = \"catalog:\";\n- \"@aws-sdk/client-iam\" = \"catalog:\";\n- \"@aws-sdk/client-elastic-load-balancing-v2\" = \"catalog:\";\n- \"@aws-sdk/client-ecr\" = \"catalog:\";\n- \"@aws-sdk/client-ssm\" = \"catalog:\";\n- \"@aws-sdk/client-sts\" = \"catalog:\";\n+ \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n+ \"@aws-sdk/client-iam\" = \"^3.953.0\";\n+ \"@aws-sdk/client-elastic-load-balancing-v2\" = \"^3.953.0\";\n+ \"@aws-sdk/client-ecr\" = \"^3.953.0\";\n+ \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n+ \"@aws-sdk/client-sts\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/aws-ec2-app/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.5s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/database/module.nix\n@@\n dependencies = {\n- \"alchemy\" = \"catalog:\";\n+ \"alchemy\" = config.stack.alchemy.version;\n }\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/database/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.7s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/modules/cache/module.nix\n@@\n dependencies = {\n- \"alchemy\" = \"catalog:\";\n+ \"alchemy\" = config.stack.alchemy.version;\n }\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/modules/cache/module.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.6s) - -_Thinking:_ - -**Applying major codegen patch** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix\n@@\n- # ============================================================================\n- # Catalog versions for NPM packages used by infra modules via \"catalog:\".\n- #\n- # Defined once here so that:\n- # 1. Module dependencies reference \"catalog:\" (workspace dedup)\n- # 2. stack.bun.catalog gets the real version (root package.json)\n- #\n- # When adding a new AWS SDK or other catalog dependency to an infra module,\n- # add the version here too.\n- # ============================================================================\n- infraCatalogVersions = {\n- \"@aws-sdk/client-ec2\" = \"^3.953.0\";\n- \"@aws-sdk/client-ecr\" = \"^3.953.0\";\n- \"@aws-sdk/client-elastic-load-balancing-v2\" = \"^3.953.0\";\n- \"@aws-sdk/client-iam\" = \"^3.953.0\";\n- \"@aws-sdk/client-kms\" = \"^3.953.0\";\n- \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n- \"@aws-sdk/client-sts\" = \"^3.953.0\";\n- \"@pulumi/aws\" = \"^7.15.0\";\n- \"sst\" = \"^3.17.25\";\n- };\n sortedModuleIds = lib.sort (a: b: a < b) moduleIds;\n@@\n- # Collect catalog versions for every \"catalog:\" dependency across all modules.\n- # Only includes deps whose value is literally \"catalog:\" — direct version\n- # strings are left alone (they don't need a catalog entry).\n- allCatalogDeps = lib.filterAttrs (_: v: v == \"catalog:\") allDeps;\n- catalogEntries = lib.mapAttrs (\n- name: _:\n- infraCatalogVersions.${name}\n- or (builtins.throw \"infra module dependency '${name}' uses catalog: but has no version in infraCatalogVersions — add it to infra/codegen.nix\")\n- ) allCatalogDeps;\n+ # Infra modules must declare concrete dependency versions/ranges.\n+ # Reject unresolved catalog placeholders to keep generated package.json explicit.\n+ catalogDepNames = builtins.attrNames (lib.filterAttrs (_: v: v == \"catalog:\") allDeps);\n+ validatedDeps =\n+ if catalogDepNames != [ ] then\n+ builtins.throw \"infra module dependencies must declare explicit versions (found catalog: for ${lib.concatStringsSep \", \" catalogDepNames})\"\n+ else\n+ allDeps;\n@@\n- # ==========================================================================\n- # Bun catalog — register actual versions for all \"catalog:\" references\n- # ==========================================================================\n- stack.bun.catalog = catalogEntries;\n-\n- # ==========================================================================\n # Turbo workspace package (generates package.json + turbo.json tasks)\n # ==========================================================================\n stack.turbo.packages.infra = {\n name = cfg.package.name;\n path = outputDir;\n- dependencies = {\n- alchemy = \"catalog:\";\n- }\n- // lib.optionalAttrs config.stack.alchemy.enable {\n- ${config.stack.alchemy.package.name} = \"workspace:*\";\n- }\n- // allDeps\n- // cfg.package.dependencies;\n+ dependencies =\n+ let\n+ packageDeps = {\n+ alchemy = config.stack.alchemy.version;\n+ }\n+ // lib.optionalAttrs config.stack.alchemy.enable {\n+ ${config.stack.alchemy.package.name} = \"workspace:*\";\n+ }\n+ // validatedDeps\n+ // cfg.package.dependencies;\n+ invalidCatalogDeps = builtins.attrNames (lib.filterAttrs (_: v: v == \"catalog:\") packageDeps);\n+ in\n+ if invalidCatalogDeps != [ ] then\n+ builtins.throw \"infra package dependencies must declare explicit versions (found catalog: for ${lib.concatStringsSep \", \" invalidCatalogDeps})\"\n+ else\n+ packageDeps;\n@@\n # NPM packages required by the TypeScript implementation.\n dependencies = {\n- # \"@aws-sdk/client-s3\" = \"catalog:\";\n+ # \"@aws-sdk/client-s3\" = \"^3.953.0\";\n };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/infra/codegen.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.0s) - -_Thinking:_ - -**Checking legacy catalog references** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "infraCatalogVersions|catalogEntries|allCatalogDeps|catalog:", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix" -} -``` - -**Output:** -``` -Found 4 matches -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 353: catalogDepNames = builtins.attrNames (lib.filterAttrs (_: v: v == "catalog:") allDeps); - Line 356: builtins.throw "infra module dependencies must declare explicit versions (found catalog: for ${lib.concatStringsSep ", " catalogDepNames})" - Line 663: invalidCatalogDeps = builtins.attrNames (lib.filterAttrs (_: v: v == "catalog:") packageDeps); - Line 666: builtins.throw "infra package dependencies must declare explicit versions (found catalog: for ${lib.concatStringsSep ", " invalidCatalogDeps})" -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.1s) - -_Thinking:_ - - - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix\n@@\n # ============================================================================\n # Determine dependencies based on enabled helpers\n # ============================================================================\n baseDeps = {\n- alchemy = \"catalog:\";\n+ alchemy = cfg.version;\n };\n \n helperDeps = lib.optionalAttrs cfg.helpers.ssm {\n- \"@aws-sdk/client-ssm\" = \"catalog:\";\n+ \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n };\n@@\n config = lib.mkIf cfg.enable {\n- # ==========================================================================\n- # Bun catalog — register actual versions for \"catalog:\" references\n- # ==========================================================================\n- stack.bun.catalog = {\n- alchemy = cfg.version;\n- } // lib.optionalAttrs cfg.helpers.ssm {\n- \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n- };\n-\n # ==========================================================================\n # File generation\n # ==========================================================================\n*** End Patch" -} -``` - -**Error:** -``` -Error: apply_patch verification failed: Error: Failed to find expected lines in /Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - # ============================================================================ - # Determine dependencies based on enabled helpers - # ============================================================================ - baseDeps = { - alchemy = "catalog:"; - }; - - helperDeps = lib.optionalAttrs cfg.helpers.ssm { - "@aws-sdk/client-ssm" = "catalog:"; - }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.1s) - -_Thinking:_ - -**Verifying spacing and formatting accuracy** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix", - "offset": 270, - "limit": 60 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix -file -270: rootDir = "."; -271: }; -272: include = [ "src/**/*.ts" ]; -273: exclude = [ -274: "node_modules" -275: "dist" -276: ]; -277: }; -278: -279: # ============================================================================ -280: # Determine dependencies based on enabled helpers -281: # ============================================================================ -282: baseDeps = { -283: alchemy = "^0.87.0:"; -284: }; -285: -286: helperDeps = lib.optionalAttrs cfg.helpers.ssm { -287: "@aws-sdk/client-ssm" = "^3.1000.0"; -288: }; -289: -290: allDeps = baseDeps // helperDeps // cfg.package.extra-dependencies; -291: -292: # ============================================================================ -293: # Flags for conditional features -294: # ============================================================================ -295: hasSecrets = -296: cfg.secrets.state-token-sops-path != null || cfg.secrets.cloudflare-token-sops-path != null; -297: -298: in -299: { -300: config = lib.mkIf cfg.enable { -301: # ========================================================================== -302: # Bun catalog — register actual versions for "catalog:" references -303: # ========================================================================== -304: stack.bun.catalog = { -305: alchemy = cfg.version; -306: } // lib.optionalAttrs cfg.helpers.ssm { -307: "@aws-sdk/client-ssm" = "^3.953.0"; -308: }; -309: -310: # ========================================================================== -311: # File generation -312: # ========================================================================== -313: stack.files.entries = { -314: # Main entry: createApp factory + re-exports -315: "${outputDir}/src/index.ts" = { -316: text = indexTs; -317: mode = "0644"; -318: description = "Alchemy app factory and re-exports (@gen/alchemy)"; -319: source = "alchemy"; -320: }; -321: -322: # State store provider factory -323: "${outputDir}/src/state-store.ts" = { -324: text = stateStoreTs; -325: mode = "0644"; -326: description = "Alchemy state store provider factory"; -327: source = "alchemy"; -328: }; -329: - -(Showing lines 270-329 of 513. Use offset=330 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.1s) - -_Thinking:_ - -**Correcting dependency versions and removing block** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix\n@@\n # ============================================================================\n # Determine dependencies based on enabled helpers\n # ============================================================================\n baseDeps = {\n- alchemy = \"^0.87.0:\";\n+ alchemy = cfg.version;\n };\n \n helperDeps = lib.optionalAttrs cfg.helpers.ssm {\n- \"@aws-sdk/client-ssm\" = \"^3.1000.0\";\n+ \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n };\n@@\n in\n {\n config = lib.mkIf cfg.enable {\n- # ==========================================================================\n- # Bun catalog — register actual versions for \"catalog:\" references\n- # ==========================================================================\n- stack.bun.catalog = {\n- alchemy = cfg.version;\n- } // lib.optionalAttrs cfg.helpers.ssm {\n- \"@aws-sdk/client-ssm\" = \"^3.953.0\";\n- };\n-\n # ==========================================================================\n # File generation\n # ==========================================================================\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/alchemy/codegen.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.9s) - -_Thinking:_ - -**Fixing version issues and patching module** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/sst/sst.nix\n@@\n defaultPackageDeps = {\n- sst = \"catalog:\";\n- \"@pulumi/aws\" = \"catalog:\";\n+ sst = \"^3.17.25\";\n+ \"@pulumi/aws\" = \"^7.15.0\";\n };\n@@\n (lib.mkIf cfg.enable {\n- # =========================================================================\n- # Bun catalog — register actual versions for \"catalog:\" references\n- # =========================================================================\n- stack.bun.catalog = {\n- sst = \"^3.17.25\";\n- \"@pulumi/aws\" = \"^7.15.0\";\n- };\n-\n # =========================================================================\n # Extension Registration\n # =========================================================================\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/sst/sst.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.7s) - -_Thinking:_ - -**Patching catalog references** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix\n@@\n # Usage:\n # stack.turbo.packages.infra = {\n # name = \"@stack/infra\";\n # path = \"packages/infra\";\n-# dependencies = { alchemy = \"catalog:\"; };\n+# dependencies = { alchemy = \"^0.81.2\"; };\n # scripts.deploy = {\n # exec = \"alchemy deploy\";\n # turbo = { enable = true; cache = false; };\n # };\n@@\n infra = {\n name = \"@stack/infra\";\n path = \"packages/infra\";\n- dependencies = { alchemy = \"catalog:\"; };\n+ dependencies = { alchemy = \"^0.81.2\"; };\n scripts.deploy = {\n exec = \"alchemy deploy\";\n turbo = { enable = true; cache = false; };\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/modules/turbo/packages.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 3.7s) - -_Thinking:_ - -**Reviewing codegen comments** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "=\\s*\"catalog:\";", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 1 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix: - Line 8: # Modules declare dependencies as `"@aws-sdk/client-ecr" = "catalog:";` in -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.0s) - -_Thinking:_ - -**Running nix develop to test version fixes** - -**Tool: bash** - -**Input:** -```json -{ - "command": "nix develop --impure", - "timeout": 120000, - "description": "Tests nix develop after dependency version changes" -} -``` - -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: - … while evaluating the attribute 'packages' - at /nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the option `stack.outputs': - - … while evaluating definitions from `/nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/nix/stack/devshell/scripts.nix': - - … while evaluating the option `stack.scripts': - - … while evaluating definitions from `/nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/nix/stack/files': - - … while evaluating the option `stack.files.entries': - - … while evaluating definitions from `/nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/nix/stack/modules/turbo/packages.nix': - - … while evaluating the option `stack.turbo.packages': - - … while evaluating definitions from `/nix/store/dfj4j49sly3mibrxv8cdb47k25vjvcjg-source/nix/stack/devshell/scripts.nix': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: infinite recursion encountered - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 9.5s) - -_Thinking:_ - -**Investigating recursion source in scripts module** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "stack\\.scripts\\.", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 16 matches -/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix: - Line 775: stack.scripts."write-files" = { - Line 788: stack.scripts."check-files" = { - -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix: - Line 20: # stack.scripts.db-seed = { - Line 26: # stack.scripts.db-seed = { - Line 33: # stack.scripts."sst:deploy" = { - -/Users/cm/git/darkmatter/stack/nix/stack/lib/default.nix: - Line 191: # config = cfg: { stack.scripts.my-cmd = { ... }; }; - -/Users/cm/git/darkmatter/stack/.stack/modules/generate-config-example.nix: - Line 150: config.stack.scripts.generate-config-example = lib.mkIf cfg.enable { - -/Users/cm/git/darkmatter/stack/nix/stack/lib/mkModule.nix: - Line 26: # stack.scripts.my-command = { ... }; - -/Users/cm/git/darkmatter/stack/nix/stack/db/schemas/tasks.proto.nix: - Line 10: # For ad-hoc utility scripts without dependencies, use stack.scripts. - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/healthchecks.nix: - Line 16: # - scriptRef: Reference to a stack.scripts.* entry - Line 38: # scriptRef = "db-seed"; # Uses stack.scripts.db-seed - Line 142: Reference to a stack.scripts.* entry. - Line 278: # Reference to a stack.scripts.* entry - use its derivation - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/extensions.nix: - Line 355: - src/scripts/*.sh -> stack.scripts.: - -/Users/cm/git/darkmatter/stack/.stack/modules/generate-docs.nix: - Line 75: config.stack.scripts.generate-docs = { - -/Users/cm/git/darkmatter/stack/.stack/modules/prek-wrapper.nix: - Line 28: stack.scripts."stack:prek" = { -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.7s) - -_Thinking:_ - -**Investigating script and file module dependencies** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix", - "offset": 700, - "limit": 160 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix -file -700: "block" -701: ]; -702: default = "full"; -703: description = '' -704: How the file is managed: -705: - 'full': the entire file is owned by stack (default). The file is -706: overwritten on write and deleted when stale. -707: - 'block': only a marker-delimited block within the file is managed. -708: Content outside the block is preserved. On uninstall, only the block -709: is removed (the file itself is kept unless empty). This is useful for -710: files like .gitignore where user content must coexist with managed content. -711: ''; -712: }; -713: -714: blockLabel = lib.mkOption { -715: type = lib.types.str; -716: default = "stack"; -717: description = '' -718: Label used in the BEGIN/END markers for block-managed files. -719: The markers will be: "# ── BEGIN -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.0s) - -_Thinking:_ - -**Analyzing potential config evaluation cycle** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "generateTurboPackage|scriptsConfig\\.generateTurboPackage|turboPackageId|turboPackageName|scriptsConfig", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 12 matches -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix: - Line 49: scriptsCfg = config.stack.scriptsConfig; - Line 269: When turbo.enable = true and scriptsConfig.generateTurboPackage is enabled, - Line 349: options.stack.scriptsConfig = { - Line 356: generateTurboPackage = lib.mkOption { - Line 361: scriptsConfig.turboPackagePath (default: packages/gen/scripts). - Line 369: turboPackageId = lib.mkOption { - Line 375: turboPackageName = lib.mkOption { - Line 400: Use to reference specific scripts: config.stack.scriptsConfig.packages.my-script - Line 428: (lib.mkIf scriptsCfg.generateTurboPackage { - Line 429: stack.turbo.packages.${scriptsCfg.turboPackageId} = { - Line 430: name = scriptsCfg.turboPackageName; - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/healthchecks.nix: - Line 281: scriptPkgs = config.stack.scriptsConfig.packages or { }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.1s) - -_Thinking:_ - -**Searching config for assignments** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "scriptsConfig\\.generateTurboPackage|generateTurboPackage\\s*=\\s*true|turboPackageId\\s*=|stack\\.scriptsConfig", - "path": "/Users/cm/git/darkmatter/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 6 matches -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix: - Line 49: scriptsCfg = config.stack.scriptsConfig; - Line 269: When turbo.enable = true and scriptsConfig.generateTurboPackage is enabled, - Line 349: options.stack.scriptsConfig = { - Line 369: turboPackageId = lib.mkOption { - Line 400: Use to reference specific scripts: config.stack.scriptsConfig.packages.my-script - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/healthchecks.nix: - Line 281: scriptPkgs = config.stack.scriptsConfig.packages or { }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 6.6s) - -_Thinking:_ - -**Analyzing package config cycle cause** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 700, - "limit": 120 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -700: }; -701: }; -702: }; -703: -704: # ========================================================================== -705: # Shell scripts -706: # ========================================================================== -707: stack.scripts = { -708: "infra:new-module" = { -709: exec = '' -710: MODULE_ID="''${1:?Usage: infra:new-module }" -711: -712: # Validate module ID -713: if ! echo "$MODULE_ID" | grep -qE '^[a-z][a-z0-9-]*$'; then -714: echo "Error: Module ID must be lowercase alphanumeric with hyphens (e.g., my-module)" -715: exit 1 -716: fi -717: -718: MODULE_DIR="nix/stack/infra/modules/$MODULE_ID" -719: if [ -d "$MODULE_DIR" ]; then -720: echo "Error: Module directory $MODULE_DIR already exists" -721: exit 1 -722: fi -723: -724: MODULE_NAME=$(echo "$MODULE_ID" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)} 1') -725: -726: echo "Creating infra module: $MODULE_ID ($MODULE_NAME)" -727: mkdir -p "$MODULE_DIR" -728: -729: # Generate module.nix -730: cat > "$MODULE_DIR/module.nix" << 'NIXEOF' -731: # ============================================================================== -732: # infra/modules/MODULE_ID/module.nix -733: # ============================================================================== -734: { -735: lib, -736: config, -737: ... -738: }: -739: let -740: cfg = config.stack.infra.MODULE_ID; -741: in -742: { -743: options.stack.infra.MODULE_ID = { -744: enable = lib.mkOption { -745: type = lib.types.bool; -746: default = false; -747: description = "Enable MODULE_NAME provisioning."; -748: }; -749: -750: # Add your module options here. Example: -751: # region = lib.mkOption { -752: # type = lib.types.str; -753: # default = "us-east-1"; -754: # description = "AWS region."; -755: # }; -756: }; -757: -758: config = lib.mkIf cfg.enable { -759: stack.infra.enable = lib.mkDefault true; -760: -761: stack.infra.modules.MODULE_ID = { -762: name = "MODULE_NAME"; -763: description = "MODULE_NAME infrastructure provisioning"; -764: path = ./index.ts; -765: -766: # Nix values serialized to JSON and available at runtime -767: # via infra.inputs() in the TypeScript file. -768: inputs = { -769: # Add your input values here. Example: -770: # region = cfg.region; -771: }; -772: -773: # NPM packages required by the TypeScript implementation. -774: dependencies = { -775: # "@aws-sdk/client-s3" = "^3.953.0"; -776: }; -777: -778: # Outputs the TypeScript module's default export must provide. -779: # Keys with sync=true are written to the storage backend. -780: outputs = { -781: # exampleArn = { -782: # description = "ARN of the provisioned resource"; -783: # sensitive = false; -784: # sync = true; -785: # }; -786: }; -787: }; -788: }; -789: } -790: NIXEOF -791: -792: # Replace placeholders -793: sed -i "" "s/MODULE_ID/$MODULE_ID/g" "$MODULE_DIR/module.nix" -794: sed -i "" "s/MODULE_NAME/$MODULE_NAME/g" "$MODULE_DIR/module.nix" -795: -796: # Generate index.ts -797: cat > "$MODULE_DIR/index.ts" << 'TSEOF' -798: // ============================================================================== -799: // MODULE_NAME Infra Module -800: // -801: // This module is executed by `infra:deploy` via Alchemy. -802: // -803: // The default export must be a Record matching the output -804: // keys declared in module.nix. -805: // ============================================================================== -806: import Infra from "@stack/infra"; -807: -808: // Typed inputs — define an interface matching your module.nix inputs. -809: interface Inputs { -810: // region: string; -811: } -812: -813: const infra = new Infra("MODULE_ID"); -814: const inputs = infra.inputs( -815: process.env.STACKPANEL_INFRA_INPUTS_OVERRIDES, -816: ); -817: -818: // --------------------------------------------------------------------------- -819: // Provision resources here. - -(Showing lines 700-819 of 1159. Use offset=820 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 8.7s) - -_Thinking:_ - -**Tracing script dependencies in infra codegen** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix", - "offset": 680, - "limit": 70 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix -file -680: scripts = { -681: "alchemy:deploy" = { -682: exec = "alchemy deploy"; -683: turbo = { -684: enable = true; -685: cache = false; -686: }; -687: }; -688: "alchemy:destroy" = { -689: exec = "alchemy destroy"; -690: turbo = { -691: enable = true; -692: cache = false; -693: }; -694: }; -695: "alchemy:dev" = { -696: exec = "alchemy dev"; -697: }; -698: postinstall = { -699: exec = "test -f bun.lock && bun2nix -o bun.nix || true"; -700: }; -701: }; -702: }; -703: -704: # ========================================================================== -705: # Shell scripts -706: # ========================================================================== -707: stack.scripts = { -708: "infra:new-module" = { -709: exec = '' -710: MODULE_ID="''${1:?Usage: infra:new-module }" -711: -712: # Validate module ID -713: if ! echo "$MODULE_ID" | grep -qE '^[a-z][a-z0-9-]*$'; then -714: echo "Error: Module ID must be lowercase alphanumeric with hyphens (e.g., my-module)" -715: exit 1 -716: fi -717: -718: MODULE_DIR="nix/stack/infra/modules/$MODULE_ID" -719: if [ -d "$MODULE_DIR" ]; then -720: echo "Error: Module directory $MODULE_DIR already exists" -721: exit 1 -722: fi -723: -724: MODULE_NAME=$(echo "$MODULE_ID" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)} 1') -725: -726: echo "Creating infra module: $MODULE_ID ($MODULE_NAME)" -727: mkdir -p "$MODULE_DIR" -728: -729: # Generate module.nix -730: cat > "$MODULE_DIR/module.nix" << 'NIXEOF' -731: # ============================================================================== -732: # infra/modules/MODULE_ID/module.nix -733: # ============================================================================== -734: { -735: lib, -736: config, -737: ... -738: }: -739: let -740: cfg = config.stack.infra.MODULE_ID; -741: in -742: { -743: options.stack.infra.MODULE_ID = { -744: enable = lib.mkOption { -745: type = lib.types.bool; -746: default = false; -747: description = "Enable MODULE_NAME provisioning."; -748: }; -749: - -(Showing lines 680-749 of 1159. Use offset=750 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 15.8s) - -_Thinking:_ - -**Tracing recursive config dependencies** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "package\\s*=|name = lib.mkOption|default =", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/alchemy/options.nix" -} -``` - -**Output:** -``` -Found 26 matches -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/options.nix: - Line 32: default = false; - Line 38: default = "^0.81.2"; - Line 56: default = "auto"; - Line 72: default = "CLOUDFLARE_API_TOKEN"; - Line 80: default = ".alchemy"; - Line 89: app-name = lib.mkOption { - Line 91: default = projectName; - Line 100: default = null; - Line 110: package = { - Line 111: name = lib.mkOption { - Line 113: default = "@gen/alchemy"; - Line 119: default = "packages/gen/alchemy"; - Line 125: default = { }; - Line 134: default = { }; - Line 148: default = "ALCHEMY_STATE_TOKEN"; - Line 154: default = null; - Line 164: default = "CLOUDFLARE_API_TOKEN"; - Line 170: default = null; - Line 180: default = "common"; - Line 196: default = true; - Line 202: default = true; - Line 208: default = true; - Line 219: default = true; - Line 236: default = true; - Line 252: default = "profile"; - Line 265: default = "alchemy.run.ts"; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 11.1s) - -_Thinking:_ - -**Diagnosing recursive attrset evaluation causing cycles** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "hasFiles\\s*=|!= \\{ \\};|== \\{ \\};", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix" -} -``` - -**Output:** -``` -Found 1 matches -/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix: - Line 87: hasFiles = enabledFiles != { }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.6s) - -_Thinking:_ - -**Planning recursion break by length checks** - -**Tool: read** - -**Input:** -```json -{ - "filePath": "/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix", - "offset": 60, - "limit": 40 -} -``` - -**Output:** -``` -/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix -file -60: # # DO NOT EDIT between these markers — managed by stack -61: # .env -62: # node_modules -63: # # ── END stack ── -64: # -65: # User content outside the markers is never touched. On uninstall, -66: # only the managed block is removed (the file is kept if non-empty). -67: # -68: # ============================================================================== -69: { -70: config, -71: lib, -72: pkgs, -73: ... -74: }: -75: let -76: cfg = config.stack.files; -77: -78: # Import util for debug logging -79: util = config.stack.util; -80: -81: q = lib.escapeShellArg; -82: -83: # Filter to only enabled files -84: enabledFiles = cfg.entries; -85: -86: # Check if there are any files to write (and global enable is true) -87: hasFiles = enabledFiles != { }; -88: -89: fileCount = builtins.length (builtins.attrNames enabledFiles); -90: -91: # ── Resolve content ────────────────────────────────────────────────────── -92: # Resolve text content from either text, path, or jsonValue -93: resolveTextContent = -94: path: fileConfig: -95: let -96: fileType = fileConfig.type or "text"; -97: hasText = fileConfig.text or null != null; -98: hasPath = fileConfig.path or null != null; -99: in - -(Showing lines 60-99 of 802. Use offset=100 to continue.) - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.6s) - -_Thinking:_ - -**Applying three-file patch** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix\n@@\n- hasScripts = cfg != { };\n+ hasScripts = builtins.length (builtins.attrNames cfg) > 0;\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/devshell/scripts.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.9s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix\n@@\n cfg = config.stack.turbo;\n pkgsCfg = cfg.packages;\n- hasPkgs = pkgsCfg != { };\n+ hasPkgs = builtins.length (builtins.attrNames pkgsCfg) > 0;\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/modules/turbo/packages.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.8s) - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/files/default.nix\n@@\n # Filter to only enabled files\n enabledFiles = cfg.entries;\n \n # Check if there are any files to write (and global enable is true)\n- hasFiles = enabledFiles != { };\n+ hasFiles = builtins.length (builtins.attrNames enabledFiles) > 0;\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/files/default.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.8s) - -_Thinking:_ - -**Searching for attrset comparisons** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "!= \\{ \\}|== \\{ \\}", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 52 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix: - Line 181: // lib.optionalAttrs (pkg.scripts != { }) { - Line 184: // lib.optionalAttrs (pkg.dependencies != { }) { - Line 187: // lib.optionalAttrs (pkg.devDependencies != { }) { - Line 190: // lib.optionalAttrs (pkg.exports != { }) { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 386: ${lib.optionalString (mod.dependencies != { }) "Dependencies: ${depsStr}\n"} - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/catalog.nix: - Line 52: hasCatalog = catalogCfg != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/services/aws/vault/module.nix: - Line 286: else if cfg.awsProfiles != { } && cfg.generateConfigFile then - -/Users/cm/git/darkmatter/stack/nix/stack/infra/modules/deployment/module.nix: - Line 81: hasDeployableApps = deployableApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/services/global-services.nix: - Line 103: (lib.optionalString (cfg.caddy.enable && cfg.caddy.sites != { }) '' - -/Users/cm/git/darkmatter/stack/nix/stack/secrets/default.nix: - Line 920: ${lib.optionalString (keysByGroup != { }) '' - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/module.nix: - Line 355: hasTasks = tasksCfg != { }; - Line 450: stack.motd.commands = lib.mkIf (taskScripts != { }) [ - Line 485: (appCfg.turbo.enable or true) && (appCfg ? tasks) && (appCfg.tasks != null) && (appCfg.tasks != { }) - Line 488: hasAppsWithTasks = appsWithTasks != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/process-compose/module.nix: - Line 251: // lib.optionalAttrs ((svc.process-compose.availability or { }) != { }) { - Line 254: // lib.optionalAttrs ((svc.process-compose.depends_on or { }) != { }) { - -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/module.nix: - Line 35: hasOxlintApps = oxlintApps != { }; - Line 105: hasOxlintAppsLocal = oxlintAppsLocal != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/module.nix: - Line 404: hasGoApps = goApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/module.nix: - Line 249: hasApps = appsWithEntrypoints != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/module.nix: - Line 59: hasBunApps = bunApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/module.nix: - Line 361: cmds != null && cmds != { } - Line 386: if checkCommands == { } then - Line 409: hasAppsWithCommands = appsWithCommands != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/lib/codegen/env-package.nix: - Line 266: if envVars == { } then - Line 326: lib.optional ((envCfg.env or { }) != { }) { - Line 359: envNames = lib.attrNames (lib.filterAttrs (_: e: (e.env or { }) != { }) envs); - Line 376: lib.any (e: (e.env or { }) != { }) (lib.attrValues envs) - Line 389: hasEnvs = (appCfg.environments or { }) != { }; - Line 400: entrypointsBarrel = lib.optionalAttrs (entrypoints != { }) { - Line 434: lib.attrNames (lib.filterAttrs (_: e: (e.env or { }) != { }) envs) - Line 514: apps != { } && lib.any (appCfg: (appCfg.environments or { }) != { }) (lib.attrValues apps); - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/module.nix: - Line 372: hasDeployableApps = deployableApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/core/lib/global-services.nix: - Line 185: ++ lib.optional (cfg.caddy.enable && cfg.caddy.sites != { }) '' - -/Users/cm/git/darkmatter/stack/nix/stack/containers/module.nix: - Line 464: (lib.mkIf (appsWithContainers != { }) { - Line 469: (lib.mkIf (containersCfg.images != { } && containerLib.hasDockerTools) { - -/Users/cm/git/darkmatter/stack/nix/stack/apps/apps.nix: - Line 132: config = lib.mkIf (rawApps != { }) { - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/ui.nix: - Line 27: hasFlyApps = flyApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/cloudflare/ui.nix: - Line 27: hasCloudflareApps = cloudflareApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/network/ports.nix: - Line 49: hasApps = appsComputedCfg != { }; - Line 50: hasServices = cfg.services != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/ui.nix: - Line 15: hasTasks = tasksCfg != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/oxlint/ui.nix: - Line 24: hasOxlintApps = oxlintApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/go/ui.nix: - Line 26: hasGoApps = goApps != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/entrypoints/ui.nix: - Line 24: hasApps = appsWithEntrypoints != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/app-commands/ui.nix: - Line 21: cmds != null && cmds != { } - Line 24: hasAppsWithCommands = appsWithCommands != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/modules/bun/ui.nix: - Line 53: status = if bunApps != { } then "ok" else "warning"; - -/Users/cm/git/darkmatter/stack/nix/stack/lib/codegen.nix: - Line 79: hasFiles = files != { }; - -/Users/cm/git/darkmatter/stack/nix/stack/deployment/fly/container.nix: - Line 141: flyEnv = lib.optionals (appCfg.deployment.fly.env or { } != { }) ( - -/Users/cm/git/darkmatter/stack/nix/stack/core/options/services.nix: - Line 226: config = lib.mkIf (enabledServices != { }) { - -/Users/cm/git/darkmatter/stack/nix/stack/containers/ui.nix: - Line 27: hasContainerApps = appsWithContainers != { }; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 5.9s) - -_Thinking:_ - -**Observing recursion triggered by config changes** - -**Tool: bash** - -**Input:** -```json -{ - "command": "nix develop --impure", - "timeout": 120000, - "description": "Re-run nix develop after recursion guard fixes" -} -``` - -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: - … while evaluating the attribute 'packages' - at /nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the option `stack.outputs': - - … while evaluating definitions from `/nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/nix/stack/devshell/scripts.nix': - - … while evaluating the option `stack.scripts': - - … while evaluating definitions from `/nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/nix/stack/files': - - … while evaluating the option `stack.files.entries': - - … while evaluating definitions from `/nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/nix/stack/modules/turbo/packages.nix': - - … while evaluating the option `stack.turbo.packages': - - … while evaluating definitions from `/nix/store/r7rzxq279jyk2f1srg0rbnaqy88wg1ya-source/nix/stack/devshell/scripts.nix': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: infinite recursion encountered - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 2.7s) - -_Thinking:_ - -**Running deeper trace for line references** - -**Tool: bash** - -**Input:** -```json -{ - "command": "nix develop --impure --show-trace", - "timeout": 120000, - "description": "Runs nix develop with full trace after changes" -} -``` - -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: - … while evaluating the attribute 'packages' - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/flake.nix:91:9: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … in the right operand of the update (//) operator - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/flake.nix:91:29: - 90| # Merge stack packages with outputs packages - 91| packages = packages // (spOutputs.packages or { }); - | ^ - 92| - - … while evaluating the attribute 'packages' - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/flake/default.nix:437:3: - 436| - 437| packages = if enabled then directPkgs // containerPackages else { }; - | ^ - 438| - - … in the left operand of the update (//) operator - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/flake/default.nix:437:41: - 436| - 437| packages = if enabled then directPkgs // containerPackages else { }; - | ^ - 438| - - … from call site - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/flake/default.nix:368:16: - 367| spOutputs = spConfig.outputs or { }; - 368| directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) spOutputs; - | ^ - 369| nestedPkgs = lib.filterAttrs (_: v: builtins.isAttrs v && !(lib.isDerivation v)) spOutputs; - - … while calling 'filterAttrs' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:663:23: - 662| */ - 663| filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set)); - | ^ - 664| - - … while calling the 'removeAttrs' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:663:28: - 662| */ - 663| filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set)); - | ^ - 664| - - … from call site - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/flake/default.nix:367:15: - 366| # Stack outputs (packages from outputs option) - 367| spOutputs = spConfig.outputs or { }; - | ^ - 368| directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) spOutputs; - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.outputs': - - … while evaluating the attribute 'mergedValue' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1192:5: - 1191| # Type-check the remaining definitions, and merge them. Or throw if no definitions. - 1192| mergedValue = - | ^ - 1193| if isDefined then - - … while evaluating a branch condition - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1193:7: - 1192| mergedValue = - 1193| if isDefined then - | ^ - 1194| if type.merge ? v2 then - - … while evaluating the attribute 'values' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1186:9: - 1185| { - 1186| values = defsSorted; - | ^ - 1187| inherit (defsFiltered) highestPrio; - - … while evaluating a branch condition - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1180:11: - 1179| # Avoid sorting if we don't have to. - 1180| if any (def: def.value._type or "" == "order") defsFiltered.values then - | ^ - 1181| sortProperties defsFiltered.values - - … while calling the 'any' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1180:14: - 1179| # Avoid sorting if we don't have to. - 1180| if any (def: def.value._type or "" == "order") defsFiltered.values then - | ^ - 1181| sortProperties defsFiltered.values - - … while evaluating the attribute 'values' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1360:7: - 1359| { - 1360| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; - | ^ - 1361| inherit highestPrio; - - … while calling the 'concatMap' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1360:16: - 1359| { - 1360| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; - | ^ - 1361| inherit highestPrio; - - … while calling the 'concatMap' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1160:26: - 1159| # Process mkMerge and mkIf properties. - 1160| defsNormalized = concatMap ( - | ^ - 1161| m: - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1161:11: - 1160| defsNormalized = concatMap ( - 1161| m: - | ^ - 1162| map ( - - … while calling the 'map' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1162:11: - 1161| m: - 1162| map ( - | ^ - 1163| value: - - … while evaluating definitions from `/nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix': - - … from call site - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1171:80: - 1170| } - 1171| ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) - | ^ - 1172| ) defs; - - … while calling 'dischargeProperties' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1311:5: - 1310| dischargeProperties = - 1311| def: - | ^ - 1312| if def._type or "" == "merge" then - - … while evaluating a branch condition - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1315:7: - 1314| else if def._type or "" == "if" then - 1315| if isBool def.condition then - | ^ - 1316| if def.condition then dischargeProperties def.content else [ ] - - … while calling the 'isBool' builtin - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1315:10: - 1314| else if def._type or "" == "if" then - 1315| if isBool def.condition then - | ^ - 1316| if def.condition then dischargeProperties def.content else [ ] - - … while evaluating the attribute 'condition' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … while evaluating the attribute 'condition' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … while evaluating the attribute 'condition' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1471:13: - 1470| _type = "if"; - 1471| inherit condition content; - | ^ - 1472| }; - - … in the left operand of the AND (&&) operator - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix:407:33: - 406| - 407| config = lib.mkIf (hasScripts && scriptsCfg.enable) ( - | ^ - 408| lib.mkMerge [ - - … while calling the 'lessThan' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix:160:57: - 159| - 160| hasScripts = builtins.length (builtins.attrNames cfg) > 0; - | ^ - 161| - - … while calling the 'length' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix:160:16: - 159| - 160| hasScripts = builtins.length (builtins.attrNames cfg) > 0; - | ^ - 161| - - … while calling the 'attrNames' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix:160:33: - 159| - 160| hasScripts = builtins.length (builtins.attrNames cfg) > 0; - | ^ - 161| - - … from call site - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/devshell/scripts.nix:48:9: - 47| - 48| cfg = config.stack.scripts; - | ^ - 49| scriptsCfg = config.stack.scriptsConfig; - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.scripts': - - (10 duplicate frames omitted) - - … while evaluating definitions from `/nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/files': - - (7 duplicate frames omitted) - - … while calling the 'lessThan' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/files/default.nix:87:64: - 86| # Check if there are any files to write (and global enable is true) - 87| hasFiles = builtins.length (builtins.attrNames enabledFiles) > 0; - | ^ - 88| - - … while calling the 'length' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/files/default.nix:87:14: - 86| # Check if there are any files to write (and global enable is true) - 87| hasFiles = builtins.length (builtins.attrNames enabledFiles) > 0; - | ^ - 88| - - … while calling the 'attrNames' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/files/default.nix:87:31: - 86| # Check if there are any files to write (and global enable is true) - 87| hasFiles = builtins.length (builtins.attrNames enabledFiles) > 0; - | ^ - 88| - - … from call site - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/files/default.nix:84:18: - 83| # Filter to only enabled files - 84| enabledFiles = cfg.entries; - | ^ - 85| - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.files.entries': - - (10 duplicate frames omitted) - - … while evaluating definitions from `/nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/modules/turbo/packages.nix': - - (8 duplicate frames omitted) - - … while calling the 'lessThan' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/modules/turbo/packages.nix:30:58: - 29| pkgsCfg = cfg.packages; - 30| hasPkgs = builtins.length (builtins.attrNames pkgsCfg) > 0; - | ^ - 31| - - … while calling the 'length' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/modules/turbo/packages.nix:30:13: - 29| pkgsCfg = cfg.packages; - 30| hasPkgs = builtins.length (builtins.attrNames pkgsCfg) > 0; - | ^ - 31| - - … while calling the 'attrNames' builtin - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/modules/turbo/packages.nix:30:30: - 29| pkgsCfg = cfg.packages; - 30| hasPkgs = builtins.length (builtins.attrNames pkgsCfg) > 0; - | ^ - 31| - - … from call site - at /nix/store/225f97im3fmc037skf7jhlpx5iz98s81-source/nix/stack/modules/turbo/packages.nix:29:13: - 28| cfg = config.stack.turbo; - 29| pkgsCfg = cfg.packages; - | ^ - 30| hasPkgs = builtins.length (builtins.attrNames pkgsCfg) > 0; - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1188:17: - 1187| mapAttrs ( - 1188| name: value: - | ^ - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - - … from call site - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/attrsets.nix:1189:85: - 1188| name: value: - 1189| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value - | ^ - 1190| ); - - … while calling anonymous lambda - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:275:71: - 274| # For definitions that have an associated option - 275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - | ^ - 276| - - … while evaluating the attribute 'value' - at /nix/store/s0lqijj71x45qgyy7rin0b4fhkxdv761-source/lib/modules.nix:1118:7: - 1117| // { - 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; - | ^ - 1119| inherit (res.defsFinal') highestPrio; - - … while evaluating the option `stack.turbo.packages': - - (19 duplicate frames omitted) - - error: infinite recursion encountered - -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 11.8s) - -_Thinking:_ - -**Analyzing recursive config attribute cycles** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "writerDrv|driftCheckScript|hasFiles|enabledFiles", - "path": "/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix" -} -``` - -**Output:** -``` -Found 19 matches -/Users/cm/git/darkmatter/stack/nix/stack/files/default.nix: - Line 84: enabledFiles = cfg.entries; - Line 87: hasFiles = builtins.length (builtins.attrNames enabledFiles) > 0; - Line 89: fileCount = builtins.length (builtins.attrNames enabledFiles); - Line 155: ) enabledFiles; - Line 194: ) enabledFiles; - Line 334: currentPathsJson = builtins.toJSON (builtins.attrNames enabledFiles); - Line 337: writerDrv = pkgs.writeShellApplication { - Line 450: ${lib.concatLines (lib.mapAttrsToList mkWriteSnippet enabledFiles)} - Line 484: driftCheckScript = - Line 491: path: _: (enabledFiles.${path}.managed or "full") == "full" - Line 510: path: _: (enabledFiles.${path}.managed or "full") == "block" - Line 515: fc = enabledFiles.${path}; - Line 546: symlinkFiles = lib.filterAttrs (_: fc: (fc.type or "text") == "symlink") enabledFiles; - Line 759: config = lib.mkIf hasFiles { - Line 762: writerDrv - Line 763: driftCheckScript - Line 770: ${writerDrv}/bin/write-files - Line 776: exec = ''${writerDrv}/bin/write-files "$@"''; - Line 789: exec = ''${driftCheckScript}/bin/check-files-drift "$@"''; -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 13.7s) - -_Thinking:_ - -**Analyzing cyclical attribute dependency** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "generateTurboPackage|scriptsConfig", - "path": "/Users/cm/git/darkmatter/stack/.stack", - "include": "*.nix" -} -``` - -**Output:** -``` -No files found -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 25.8s) - -_Thinking:_ - -**Analyzing config attrset evaluation cycle** - -**Tool: grep** - -**Input:** -```json -{ - "pattern": "stack\\.turbo\\.packages\\.", - "path": "/Users/cm/git/darkmatter/stack/nix/stack", - "include": "*.nix" -} -``` - -**Output:** -``` -Found 4 matches -/Users/cm/git/darkmatter/stack/nix/stack/modules/turbo/packages.nix: - Line 8: # stack.turbo.packages.infra = { - -/Users/cm/git/darkmatter/stack/nix/stack/devshell/scripts.nix: - Line 432: stack.turbo.packages.${scriptsCfg.turboPackageId} = { - -/Users/cm/git/darkmatter/stack/nix/stack/alchemy/codegen.nix: - Line 356: stack.turbo.packages.alchemy = { - -/Users/cm/git/darkmatter/stack/nix/stack/infra/codegen.nix: - Line 650: stack.turbo.packages.infra = { -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 28.9s) - -_Thinking:_ - -**Breaking configuration evaluation cycle** - -**Tool: apply_patch** - -**Input:** -```json -{ - "patchText": "*** Begin Patch\n*** Update File: /Users/cm/git/darkmatter/stack/nix/stack/files/default.nix\n@@\n- # Also expose as stack scripts\n- stack.scripts.\"write-files\" = {\n- exec = ''${writerDrv}/bin/write-files \"$@\"'';\n- description = \"Write generated files to the project (with hash-check caching)\";\n- turbo = {\n- enable = true;\n- cache = false;\n- inputs = [\n- \".stack/**\"\n- \"nix/stack/**\"\n- ];\n- };\n- };\n-\n- stack.scripts.\"check-files\" = {\n- exec = ''${driftCheckScript}/bin/check-files-drift \"$@\"'';\n- description = \"Check if generated files are up-to-date (drift detection)\";\n- turbo = {\n- enable = true;\n- cache = false;\n- dependsOn = [ \"write-files\" ];\n- inputs = [\n- \".stack/**\"\n- \"nix/stack/**\"\n- ];\n- };\n- };\n };\n }\n*** End Patch" -} -``` - -**Output:** -``` -Success. Updated the following files: -M nix/stack/files/default.nix -``` - ---- - -## Assistant (Build · gpt-5.3-codex · 4.5s) - -_Thinking:_ - -**Reassessing module dependencies** - -**Tool: bash** - -**Input:** -```json -{ - "command": "nix develop --impure", - "timeout": 120000, - "description": "Tests nix develop after removing files script registrations" -} -``` - -**Output:** -``` -warning: Git tree '/Users/cm/git/darkmatter/stack' has uncommitted changes -Using saved setting for 'allow-import-from-derivation = true' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-experimental-features = nix-command flakes' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-substituters = https://nix-community.cachix.org https://darkmatter.cachix.org https://nixpkgs-python.cachix.org' from ~/.local/share/nix/trusted-settings.json. -Using saved setting for 'extra-trusted-public-keys = darkmatter.cachix.org-1:7R5qAiOVHxDpFy7yguECfC1JqVDgMdckGc+CDKk2pWA= nixpkgs-python.cachix.org-1:hxjI7pFxTyuTHn2NkvWCrAUcNZLNS3ZAvfYNuYifcEU= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=' from ~/.local/share/nix/trusted-settings.json. -error: - … while calling the 'derivationStrict' builtin - at «nix-internal»/derivation-internal.nix:38:12: - 37| - 38| strict = drvFunc drvAttrs; - | ^ - 39| - - … while evaluating derivation 'stack-stack' - whose name attribute is located at /nix/store/2wh0cxf8bjdb3jb8ziyh4902qhj8i7dn-source/pkgs/stdenv/generic/make-derivation.nix:541:13 - - … while evaluating attribute 'STACKPANEL_SHELL_HOOK_PATH' of derivation 'stack-stack' - at /nix/store/45lm5xpsjv2fnk9yi4hmvb1kyak6b4k3-source/nix/flake/default.nix:308:5: - 307| # Export path to shellHook file for inspection/debugging - 308| STACKPANEL_SHELL_HOOK_PATH = "${shellHookFile}/shellhook.sh"; - | ^ - 309| - - … while evaluating the option `process.manager.implementation': - - … while evaluating the option `devenv.cliVersion': - - … while evaluating the option `devenv.cli.version': - - (stack trace truncated; use '--show-trace' to show the full, detailed trace) - - error: The option `devenv.cli.version' was accessed but has no value defined. Try setting the option. - -``` - ---- - -## Assistant (Build · gpt-5.3-codex) - ---- - diff --git a/infra/sst/apps/docs.ts b/infra/sst/apps/docs.ts deleted file mode 100644 index 073937fb..00000000 --- a/infra/sst/apps/docs.ts +++ /dev/null @@ -1,21 +0,0 @@ -/// - -import { getDomain } from "../config"; - -interface DocsOptions { - isProd: boolean; -} - -/** - * Docs: Fumadocs (Next.js Static Export) on Cloudflare - */ -export function createDocs({ isProd }: DocsOptions) { - return new sst.cloudflare.StaticSite("Docs", { - path: "apps/docs", - build: { - command: "bun run build", - output: "out", - }, - domain: getDomain("docs", isProd), - }); -} diff --git a/infra/sst/apps/index.ts b/infra/sst/apps/index.ts deleted file mode 100644 index 6bfcdbb9..00000000 --- a/infra/sst/apps/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/// - -export { createDocs } from "./docs"; -export { createWebApp } from "./web"; diff --git a/infra/sst/apps/web.ts b/infra/sst/apps/web.ts deleted file mode 100644 index 53cc6fbd..00000000 --- a/infra/sst/apps/web.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// - -import { config, getDomain, getEnvironment } from "../config"; -import type { Secrets } from "../secrets"; - -interface WebAppOptions { - secrets: Secrets; - isProd: boolean; -} - -/** - * Web App: TanStack Start on Cloudflare Workers - * - * Workers are globally distributed - no load balancer needed! - */ -export function createWebApp({ secrets, isProd }: WebAppOptions) { - return new sst.cloudflare.Worker("Web", { - handler: "apps/web/.output/server/index.mjs", - url: true, - domain: getDomain("web", isProd), - link: Object.values(secrets), - environment: getEnvironment(isProd), - }); -} diff --git a/infra/sst/config.ts b/infra/sst/config.ts deleted file mode 100644 index 5e67413e..00000000 --- a/infra/sst/config.ts +++ /dev/null @@ -1,47 +0,0 @@ -/// - -/** - * SST Configuration - * - * Centralized configuration for domains, environments, and settings. - */ - -export const config = { - name: "stackpanel", - - domains: { - web: "stackpanel.com", - docs: "docs.stackpanel.com", - }, - - // Local development URLs - localUrls: { - web: "http://localhost:3001", - docs: "http://localhost:4000", - }, -} as const; - -/** - * Get environment variables for a given stage - */ -export function getEnvironment(isProd: boolean) { - const baseUrl = isProd - ? `https://${config.domains.web}` - : config.localUrls.web; - - return { - CORS_ORIGIN: baseUrl, - BETTER_AUTH_URL: baseUrl, - POLAR_SUCCESS_URL: `${baseUrl}/success?checkout_id={CHECKOUT_ID}`, - }; -} - -/** - * Get domain for a resource, or undefined in non-prod - */ -export function getDomain( - key: keyof typeof config.domains, - isProd: boolean, -): string | undefined { - return isProd ? config.domains[key] : undefined; -} diff --git a/infra/sst/index.ts b/infra/sst/index.ts deleted file mode 100644 index ff68b61d..00000000 --- a/infra/sst/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/// - -/** - * SST Infrastructure Modules - * - * This is the main entry point for all SST infrastructure code. - */ - -// Apps -export { createDocs, createWebApp } from "./apps"; -// Configuration -export { config, getDomain, getEnvironment } from "./config"; -// Secrets -export { createSecrets, type Secrets } from "./secrets"; - -// Utilities -export { SopsOutput } from "./sops-output"; diff --git a/infra/sst/secrets.ts b/infra/sst/secrets.ts deleted file mode 100644 index 112acbec..00000000 --- a/infra/sst/secrets.ts +++ /dev/null @@ -1,28 +0,0 @@ -/// - -/** - * SST Secrets - * - * All secrets used by the application. - * Set via: bunx sst secret set SecretName value - */ - -export function createSecrets() { - return { - // Database - databaseUrl: new sst.Secret("DatabaseUrl"), - - // Redis/Cache - redisUrl: new sst.Secret("RedisUrl"), - redisToken: new sst.Secret("RedisToken"), - - // Authentication - betterAuthSecret: new sst.Secret("BetterAuthSecret"), - - // Third-party APIs - polarAccessToken: new sst.Secret("PolarAccessToken"), - googleAiKey: new sst.Secret("GoogleAiKey"), - }; -} - -export type Secrets = ReturnType; diff --git a/infra/sst/sops-output.ts b/infra/sst/sops-output.ts deleted file mode 100644 index 3c504cb5..00000000 --- a/infra/sst/sops-output.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * SOPS Output Component - * - * Writes SST outputs to a SOPS-encrypted YAML file. - * Requires SOPS to be installed and configured with .sops.yaml - */ - -/// - -import { execSync } from "node:child_process"; -import * as fs from "node:fs"; -import * as path from "node:path"; - -interface SopsOutputArgs { - /** - * Path to the output file (will be encrypted) - */ - path: string; - - /** - * Key-value pairs to write to the file - * Values can be Pulumi Outputs or plain strings - */ - values: Record>; -} - -/** - * Write outputs to a SOPS-encrypted YAML file - * - * Usage: - * ```ts - * new SopsOutput("Secrets", { - * path: `.stack/secrets/${stage}.yaml`, - * values: { - * database_url: secrets.databaseUrl.value, - * web_url: workers.us.url, - * }, - * }); - * ``` - */ -export class SopsOutput { - constructor(name: string, args: SopsOutputArgs) { - // Resolve all values at once using $resolve with the values object - $resolve(args.values).apply((resolved) => { - const lines = Object.entries(resolved).map(([key, value]) => - value !== undefined ? `${key}: "${value}"` : `# ${key}: (not set)`, - ); - const content = lines.join("\n"); - - const tmpPath = `${args.path}.tmp`; - const dir = path.dirname(args.path); - - // Ensure directory exists - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - // Write temp file - fs.writeFileSync(tmpPath, content); - - try { - // Encrypt with SOPS - execSync(`sops --encrypt "${tmpPath}" > "${args.path}"`, { - stdio: "pipe", - }); - console.log(`✓ Wrote encrypted secrets to ${args.path}`); - } catch (err) { - console.error(`✗ Failed to encrypt ${args.path}:`, err); - // Leave unencrypted file for debugging - fs.renameSync(tmpPath, args.path); - console.log(` Wrote unencrypted file for debugging`); - return; - } - - // Clean up temp file - if (fs.existsSync(tmpPath)) { - fs.unlinkSync(tmpPath); - } - }); - } -} - -/** - * Helper: Write outputs synchronously (for use outside SST) - */ -export function writeSopsSync( - filePath: string, - values: Record, -): void { - const yaml = Object.entries(values) - .filter(([_, v]) => v !== undefined) - .map(([k, v]) => `${k}: "${v}"`) - .join("\n"); - - const tmpPath = `${filePath}.tmp`; - const dir = path.dirname(filePath); - - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - fs.writeFileSync(tmpPath, yaml); - - try { - execSync(`sops --encrypt "${tmpPath}" > "${filePath}"`, { - stdio: "inherit", - }); - } finally { - if (fs.existsSync(tmpPath)) { - fs.unlinkSync(tmpPath); - } - } - - console.log(`✓ Wrote encrypted secrets to ${filePath}`); -} diff --git a/infra/sst/sst.config.ts b/infra/sst/sst.config.ts deleted file mode 100644 index 06cd1d8f..00000000 --- a/infra/sst/sst.config.ts +++ /dev/null @@ -1,211 +0,0 @@ -/// - -/** - * SST Configuration for Stackpanel Infrastructure - * - * This configures: - * 1. OIDC provider for GitHub Actions - * 2. IAM role for secrets access - * 3. KMS key for secrets encryption/decryption - * - * Generated by stackpanel - do not edit manually. - * Regenerate with: nix run .#generate - */ - -// =========================================================================== -// Interpolated values from Nix configuration -// =========================================================================== -const projectName = "stackpanel"; -const awsRegion = "us-west-2"; - -export default $config({ - app(input) { - return { - name: projectName, - removal: input?.stage === "production" ? "retain" : "remove", - protect: ["production"].includes(input?.stage ?? ""), - home: "aws", - providers: { - aws: { - region: awsRegion, - }, - }, - }; - }, - - async run() { -// syntax: ts -// ======================================================================== -// GitHub Actions OIDC Provider -// ======================================================================== -const githubOrg = "darkmatter"; -const githubRepo = "stackpanel"; - -// Check if GitHub OIDC provider already exists (it's a singleton per account) -const existingOidcArn = `arn:aws:iam::950224716579:oidc-provider/token.actions.githubusercontent.com`; - -// Use aws.iam.OpenIdConnectProvider.get to adopt existing provider -// This avoids the "EntityAlreadyExists" error when the provider was created outside SST -const githubOidcProvider = aws.iam.OpenIdConnectProvider.get("github-oidc", existingOidcArn); - -const oidcProviderArn = githubOidcProvider.arn; -const oidcProviderUrl = "token.actions.githubusercontent.com"; -const oidcConditions = [ - { - test: "StringEquals", - variable: `${oidcProviderUrl}:aud`, - values: ["sts.amazonaws.com"], - }, - { - test: "StringLike", - variable: `${oidcProviderUrl}:sub`, - values: [`repo:${githubOrg}/${githubRepo}:*`], - }, -]; - -// syntax: ts -// ======================================================================== -// IAM Role for OIDC Authentication -// ======================================================================== -const roleName = "stackpanel-secrets-role"; - -const secretsRole = new aws.iam.Role(roleName, { - name: roleName, - assumeRolePolicy: aws.iam.getPolicyDocumentOutput({ - statements: [{ - effect: "Allow", - principals: [{ - type: "Federated", - identifiers: [oidcProviderArn], - }], - actions: ["sts:AssumeRoleWithWebIdentity"], - conditions: oidcConditions, - }], - }).json, - tags: { - Name: roleName, - Environment: $app.stage, - ManagedBy: "sst", - }, -}); - -// syntax: ts -// ======================================================================== -// KMS Key for Secrets Encryption -// ======================================================================== -const kmsAlias = "stackpanel-secrets"; -const kmsDeletionDays = 30; -const awsAccountId = "950224716579"; - -const secretsKey = new aws.kms.Key(kmsAlias, { - description: "KMS key for encrypting stackpanel secrets", - enableKeyRotation: true, - deletionWindowInDays: kmsDeletionDays, - policy: aws.iam.getPolicyDocumentOutput({ - statements: [ - // Allow root account full access - { - effect: "Allow", - principals: [{ - type: "AWS", - identifiers: [`arn:aws:iam::${awsAccountId}:root`], - }], - actions: ["kms:*"], - resources: ["*"], - }, - // Allow the secrets role to use the key - { - effect: "Allow", - principals: [{ - type: "AWS", - identifiers: [secretsRole.arn], - }], - actions: [ - "kms:Decrypt", - "kms:Encrypt", - "kms:GenerateDataKey*", - "kms:DescribeKey", - ], - resources: ["*"], - }, - ], - }).json, - tags: { - Name: kmsAlias, - Environment: $app.stage, - ManagedBy: "sst", - }, -}); - -const secretsKeyAlias = new aws.kms.Alias(`${kmsAlias}-alias`, { - name: `alias/${kmsAlias}`, - targetKeyId: secretsKey.id, -}); - -// syntax: ts -// ======================================================================== -// IAM Policy for KMS Access -// ======================================================================== -new aws.iam.RolePolicy(`${roleName}-kms`, { - name: `${roleName}-kms-policy`, - role: secretsRole.id, - policy: aws.iam.getPolicyDocumentOutput({ - statements: [ - { - effect: "Allow", - actions: [ - "kms:Decrypt", - "kms:Encrypt", - "kms:GenerateDataKey*", - "kms:DescribeKey", - ], - resources: [secretsKey.arn], - }, - { - effect: "Allow", - actions: ["kms:Decrypt"], - resources: ["*"], - conditions: [ - { - test: "StringLike", - variable: "kms:RequestAlias", - values: [`alias/${kmsAlias}`], - }, - ], - }, - ], - }).json, -}); - - // ======================================================================== - // Outputs - // ======================================================================== - const providerType = "github-actions"; - const providerDisplayName = "GitHub Actions"; - - return { - roleArn: secretsRole.arn, - roleName: secretsRole.name, - kmsKeyArn: secretsKey.arn, -kmsKeyId: secretsKey.id, -kmsKeyAlias: secretsKeyAlias.name, -oidcProviderArn: githubOidcProvider.arn, -provider: providerType, - setupInstructions: $interpolate` -╔════════════════════════════════════════════════════════════════════╗ -║ Stackpanel Infrastructure Setup Complete ║ -╚════════════════════════════════════════════════════════════════════╝ - -Role ARN: ${secretsRole.arn} -KMS Key Alias: alias/${kmsAlias} - -Provider: ${providerDisplayName} - -To use this infrastructure: -1. Configure your secrets with the KMS key -2. Use the IAM role for decryption in your applications -`, - }; - - }, -}); diff --git a/nix/NOTES.md b/nix/NOTES.md deleted file mode 100644 index 10b6d3bf..00000000 --- a/nix/NOTES.md +++ /dev/null @@ -1,26 +0,0 @@ -# Nix notes - -This file previously sketched an experimental standalone devshell/devenv adapter design. Stackpanel now uses a single flake-parts path instead. - -Use `stackpanel.lib.mkFlake` for project flakes: - -```nix -{ - inputs.stackpanel.url = "github:darkmatter/stackpanel"; - - outputs = inputs @ { self, stackpanel, ... }: - stackpanel.lib.mkFlake { - inherit inputs self; - stackpanelImports = [ ./stackpanel-module.nix ]; - }; -} -``` - -Project configuration lives in `.stack/config.nix` and uses native options such as `stackpanel.languages.*`, `stackpanel.packages`, `stackpanel.devshell.*`, and `stackpanel.process-compose`. - -Removed legacy APIs: - -- `devenvModules` -- `lib.wrapDevenv` -- standalone devenv templates -- public `devshellModules` / `mkDevShell` examples from the old adapter design diff --git a/nix/README.md b/nix/README.md index 4f332860..7da88927 100644 --- a/nix/README.md +++ b/nix/README.md @@ -7,7 +7,6 @@ This directory contains all Nix code for the Stackpanel project, organized into ```text nix/ ├── README.md # This file -├── NOTES.md # Development notes and scratch │ ├── stackpanel/ # Main module system (for users) │ ├── core/ # Core options and services @@ -23,12 +22,9 @@ nix/ │ ├── flake/ # Flake outputs (exported to users) │ ├── default.nix # flake-parts module -│ ├── per-system-outputs.nix # Per-system output builder -│ ├── devshells/ # Devshell factory functions │ └── templates/ # Project templates │ └── internal/ # Internal config (for this repo only) - ├── flake/ # Flake-parts module used by this repo └── stackpanel/ # This repo's stackpanel configuration ``` @@ -53,7 +49,6 @@ What gets exported in the flake for users to consume: - **`flakeModules.default`** - Import into your `flake.nix` (flake-parts) - **`lib.mkFlake`** - High-level helper for wiring up stackpanel flakes - **`templates`** - `nix flake init -t github:darkmatter/stackpanel` -- **`devshells`** - Factory functions for creating shells See [flake/README.md](flake/README.md) for details. @@ -134,7 +129,7 @@ dev ```text ┌─────────────────────────────────────────────────────────────┐ │ flake.nix │ -│ (imports nix/internal/flake/ for flake-parts) │ +│ (imports nix/flake/exports.nix for flake-parts) │ └─────────────────────────────────────────────────────────────┘ │ ▼ diff --git a/nix/flake/README.md b/nix/flake/README.md index 63fc9a8e..c524a6bf 100644 --- a/nix/flake/README.md +++ b/nix/flake/README.md @@ -74,14 +74,6 @@ stackpanel.lib.mkFlake { } ``` -### `lib.mkOutputs` - -Lower-level helper for building per-system outputs manually when you already have a `pkgs`: - -```nix -import stackpanel.lib.mkOutputs { inherit pkgs inputs self system; } -``` - ### `templates` Project templates for bootstrapping new projects: diff --git a/nix/flake/devshells/README.md b/nix/flake/devshells/README.md deleted file mode 100644 index 1a378f17..00000000 --- a/nix/flake/devshells/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# nix/flake/devshells/ - -Historical notes for the old standalone devshell factory. - -Active Stackpanel shells are produced by the flake-parts module in `nix/flake/default.nix` and exposed through `stackpanel.lib.mkFlake`: - -```nix -{ - inputs.stackpanel.url = "github:darkmatter/stackpanel"; - - outputs = inputs @ { self, stackpanel, ... }: - stackpanel.lib.mkFlake { - inherit inputs self; - - perSystem = { pkgs, ... }: { - packages.hello = pkgs.hello; - }; - }; -} -``` - -Configure shell packages, environment, hooks, and language toolchains in `.stack/config.nix`: - -```nix -{ - stackpanel = { - packages = pkgs: [ pkgs.nodejs_22 pkgs.bun ]; - devshell.env.NODE_ENV = "development"; - languages.javascript.enable = true; - }; -} -``` - -Reusable Stackpanel extensions should be passed through `stackpanelImports`: - -```nix -stackpanel.lib.mkFlake { - inherit inputs self; - stackpanelImports = [ ./stackpanel-module.nix ]; -} -``` - -Legacy `inputs.stack.lib.mkDevShell` and `inputs.stack.lib.devshellModules.*` examples are no longer part of the public flake API. diff --git a/nix/flake/devshells/default.nix b/nix/flake/devshells/default.nix deleted file mode 100644 index 2b98276c..00000000 --- a/nix/flake/devshells/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -# ============================================================================== -# default.nix -# -# Entry point for stackpanel devshells. -# Points to the main stackpanel module for use by the flakeModule. -# -# The flakeModule (nix/flake/default.nix) handles shell creation via mkShell. -# This file exists for backwards compatibility and module reference. -# ============================================================================== -_: { - # Main stackpanel module - imports all features - # Features only activate when their .enable option is set - core = ../../stackpanel; -} diff --git a/nix/flake/exports.nix b/nix/flake/exports.nix index 88b449d9..640fad75 100644 --- a/nix/flake/exports.nix +++ b/nix/flake/exports.nix @@ -68,9 +68,6 @@ let in evaluated.options.stackpanel; - # Compatibility helper for callers that still build one system manually. - mkOutputs = args: import ./per-system-outputs.nix args; - exported = rec { inherit supportedSystems; @@ -94,8 +91,6 @@ let # LIBRARY FUNCTIONS # ========================================================================== lib = { - inherit mkOutputs; - # Main entry point for complete stackpanel flakes. This delegates to # flake-parts and imports flakeModules.default plus caller imports. mkFlake = diff --git a/nix/flake/global-outputs.nix b/nix/flake/global-outputs.nix index 0f1507bd..8c21f8e9 100644 --- a/nix/flake/global-outputs.nix +++ b/nix/flake/global-outputs.nix @@ -19,7 +19,7 @@ # build host system anywhere in this path. # # Future non-deploy global outputs (overlays, templates, etc.) should -# also live here rather than in per-system-outputs.nix. +# also live here rather than in per-system/outputs.nix. # ============================================================================== { inputs, @@ -43,7 +43,7 @@ let # reaches for pkgs in this path, evaluation fails loudly rather than # silently producing a wrong result. # =================================================================== - stubPkgs = lib.warn "stackpanel global-outputs: a module accessed `pkgs`, which is not available in the lib-only eval path. Move any pkgs-dependent logic to per-system-outputs.nix." ( + stubPkgs = lib.warn "stackpanel global-outputs: a module accessed `pkgs`, which is not available in the lib-only eval path. Move any pkgs-dependent logic to per-system/outputs.nix." ( throw "pkgs is not available in global-outputs eval" ); diff --git a/nix/flake/per-system-outputs.nix b/nix/flake/per-system-outputs.nix deleted file mode 100644 index 1de5ccc1..00000000 --- a/nix/flake/per-system-outputs.nix +++ /dev/null @@ -1,376 +0,0 @@ -# ============================================================================== -# per-system-outputs.nix - Stackpanel Per-System Flake Outputs -# -# A pure function that generates per-system flake outputs for a given system. -# Usage: -# import ./per-system-outputs.nix { inherit pkgs inputs self system; } -# -# Returns: -# { devShells, checks, apps, legacyPackages, packages } -# -# Architecture: -# - Auto-loads config from .stackpanel/_internal.nix or .stackpanel/config.nix -# via the shared load-config.nix helper. -# - Uses lib.evalModules for internal stackpanel config (NixOS module system) -# - Creates devShells.default via pkgs.mkShell with full passthru -# -# Global outputs (nixosConfigurations, colmenaHive, nixosModules) live in -# global-outputs.nix, which performs a lib-only evaluation without pkgs. -# ============================================================================== -{ - pkgs, - inputs, - self, - system, - # Optional: additional stackpanel module imports - stackpanelImports ? [ ], - # Optional: project root for pure eval (containers, fly deploy, etc.). - # Defaults to `toString self` — the flake source as copied into the - # Nix store, which is the canonical project root in pure eval. - projectRoot ? toString self, -}: -let - inherit (pkgs) lib; - - # Returns a list of all the entries in a folder - - # Serialization helpers for JSON-safe config - serializeLib = import ../stackpanel/lib/serialize.nix { inherit lib; }; - - # Check if user's flake has these optional inputs - hasProcessCompose = inputs ? process-compose-flake; - hasGitHooks = inputs ? git-hooks; - - # =================================================================== - # Auto-load stackpanel config from .stackpanel/ - # Always use `self` for file discovery (works in pure evaluation) - # =================================================================== - configLoader = import ./load-config.nix { inherit self inputs; }; - - stackpanelConfigModule = configLoader.mkStackpanelModule { - inherit lib pkgs; - }; - - loadedConfig = configLoader.evalResolved { - inherit lib pkgs; - config = spConfig; - }; - - # Git hooks config (from stack config) - gitHooksConfig = loadedConfig.git-hooks or { }; - - # =================================================================== - # Evaluate stackpanel modules - # =================================================================== - # evalWith overlays extra modules on the same module set; used by the - # speculative evaluation behind `stack setup` (see per-system/outputs.nix). - evalWith = - extraModules: - lib.evalModules { - modules = [ - ../stackpanel - stackpanelConfigModule - ] - ++ lib.optional (projectRoot != null) { - # Injected so containers + infra modules resolve files relative - # to the user's working tree, not the read-only Nix store copy. - stackpanel.root = projectRoot; - } - ++ stackpanelImports - ++ extraModules; - specialArgs = { - inherit - pkgs - lib - inputs - self - ; - localFlake = self; - }; - }; - - stackpanelEval = evalWith [ ]; - - stackpanelSpeculate = - { - modules ? [ ], - config ? { }, - }: - let - sp = (evalWith (modules ++ [ { stackpanel = config; } ])).config.stackpanel; - writer = sp.files._writerDrv; - in - { - files = sp.files._plan; - doctor = sp.doctorList; - addons = sp.addonsList; - writerDrvPath = builtins.toString writer.drvPath; - writerOutPath = builtins.toString writer; - preflightManifestDrvPath = builtins.toString sp.files._preflightManifestDrv.drvPath; - preflightManifestOutPath = builtins.toString sp.files._preflightManifestDrv; - }; - - spConfig = stackpanelEval.config.stackpanel; - devshellOutputs = spConfig.devshell; - # =================================================================== - # Build shell hook from stackpanel hooks - # =================================================================== - hooks = - devshellOutputs.hooks or { - before = [ ]; - main = [ ]; - after = [ ]; - }; - - # When debug is enabled, wrap each hook entry with timing - wrapWithTimer = - label: hookStr: - if spConfig.debug or spConfig.hooks.timing or false then - '' - TIMEFORMAT=$'time ${label} completed in %3Rs' - time { - ${hookStr} - : - } - '' - else - hookStr; - - timedHookList = - phase: hooks': - lib.imap0 (idx: hookStr: wrapWithTimer "hooks.${phase}[${toString idx}]" hookStr) ( - lib.filter (hookStr: hookStr != "") hooks' - ); - - stackpanelHook = lib.concatStringsSep "\n\n" ( - lib.flatten [ - (timedHookList "before" hooks.before) - (timedHookList "main" hooks.main) - (timedHookList "after" hooks.after) - ] - ); - - # =================================================================== - # Combine all packages - # NOTE: process-compose's `dev` wrapper is provided by the devshell. - # Enter with `nix develop`, then run `dev`. - # =================================================================== - allPackages = (devshellOutputs.packages or [ ]) ++ (devshellOutputs._commandPkgs or [ ]); - - # =================================================================== - # Combine all env vars - # =================================================================== - allEnv = devshellOutputs.env or { }; - - # =================================================================== - # Build complete shellHook content - # =================================================================== - shellHookContent = '' - # ================================================================ - # Stackpanel Shell Hook (wrapper) - # Generated by: nix/flake/per-system-outputs.nix - # ================================================================ - - __stackpanel_shell_hook_main() { - # Export environment variables - ${lib.concatStringsSep "\n" ( - lib.mapAttrsToList (k: v: "export ${k}=${lib.escapeShellArg (toString v)}") allEnv - )} - - # Add language bin directories to PATH. - if [[ -n "''${GOPATH:-}" ]]; then - export PATH="$GOPATH/bin:$PATH" - fi - if [[ -n "''${CARGO_HOME:-}" ]]; then - export PATH="$CARGO_HOME/bin:$PATH" - fi - - # Stackpanel hooks (all output automatically goes to stderr) - ${stackpanelHook} - } - - # Run the hook with all output to stderr (so direnv doesn't capture/evaluate it) - # Optionally tee to log file if state dir exists - if [[ -d "''${STACKPANEL_STATE_DIR:-.stackpanel/state}" ]] 2>/dev/null; then - CLICOLOR_FORCE=1 \ - FORCE_COLOR=3 \ - COLORTERM=truecolor \ - __stackpanel_shell_hook_main 2> \ - >(tee -a "''${STACKPANEL_STATE_DIR:-.stackpanel/state}/shell.log" >&2) \ - || status=$? - else - __stackpanel_shell_hook_main >&2 \ - || status=$? - fi - if [[ $status -ne 0 ]]; then - echo "❌ Stackpanel shell hook exited with status $status" >&2 - exit $status - fi - ''; - - # Write shellHook to a file in the Nix store - shellHookFile = pkgs.writeTextFile { - name = "stackpanel-shellhook"; - text = shellHookContent; - executable = true; - destination = "/shellhook.sh"; - }; - - # =================================================================== - # JSON-safe serialized config - # =================================================================== - stackpanelSerializable = serializeLib.filterSerializable spConfig; - - serializedPackages = map serializeLib.serializePackage allPackages; - - userPackagesCfg = - spConfig.userPackages or { - enable = false; - serialized = [ ]; - }; - userPackagesSerialized = - if userPackagesCfg.enable or false then userPackagesCfg.serialized or [ ] else [ ]; - - allSerializedPackages = serializedPackages ++ userPackagesSerialized; - - # =================================================================== - # Create OUR shell with pkgs.mkShell - # =================================================================== - stackpanelShell = pkgs.mkShell { - name = "stackpanel-${spConfig.name or "dev"}"; - - packages = allPackages; - nativeBuildInputs = devshellOutputs.nativeBuildInputs or [ ]; - buildInputs = devshellOutputs.buildInputs or [ ]; - - # Export path to shellHook file for inspection/debugging - STACKPANEL_SHELL_HOOK_PATH = "${shellHookFile}/shellhook.sh"; - - # Minimal shellHook that sources the full hook from the store - # The full hook is at $STACKPANEL_SHELL_HOOK_PATH (also symlinked to .stackpanel/state/shellhook.sh) - shellHook = '' - # Source the full shellHook from the Nix store - source "${shellHookFile}/shellhook.sh" - - # Symlink to state dir for easy inspection (after STACKPANEL_STATE_DIR is set) - if [[ -n "''${STACKPANEL_STATE_DIR:-}" ]]; then - mkdir -p "$STACKPANEL_STATE_DIR" - ln -sf "${shellHookFile}/shellhook.sh" "$STACKPANEL_STATE_DIR/shellhook.sh" - fi - ''; - - # FULL CONTROL over passthru - passthru = { - # Stackpanel config (serializable version for JSON/CLI access) - # Full config is available via legacyPackages.stackpanelFullConfig - stackpanelConfig = stackpanelSerializable; - - # JSON-safe serialized config for CLI/agent - inherit stackpanelSerializable; - - # Pre-serialized packages for fast access - stackpanelPackages = allSerializedPackages; - - # Devshell outputs for introspection - devshellConfig = devshellOutputs; - - # All packages in the shell - packages = allPackages; - - # All env vars - env = allEnv; - - }; - }; - - # =================================================================== - # Build outputs conditionally based on config - # =================================================================== - enabled = spConfig.enable or false; - - # Stackpanel outputs (packages from outputs option) - spOutputs = spConfig.outputs or { }; - directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) spOutputs; - nestedPkgs = lib.filterAttrs (_: v: builtins.isAttrs v && !(lib.isDerivation v)) spOutputs; - - # Container outputs - containersComputed = spConfig.containersComputed or { }; - containerImages = containersComputed.images or { }; - copyScripts = containersComputed.copyScripts or { }; - - containerPackages = lib.mapAttrs' (name: image: { - name = "container-${name}"; - value = image; - }) containerImages; - - containerApps = lib.mapAttrs' (name: script: { - name = "copy-container-${name}"; - value = { - type = "app"; - program = "${script}"; - }; - }) (lib.filterAttrs (_: v: v != null) copyScripts); - - # Checks - simpleChecks = spConfig.checks or { }; - moduleChecks = spConfig.moduleChecksFlattened or { }; - allChecks = simpleChecks // moduleChecks; - - # Git hooks check - # Note: src must be a path (self), not a string (effectiveRoot) - gitHooksCheck = - if hasGitHooks && (gitHooksConfig.enable or false) then - { - pre-commit-check = inputs.git-hooks.lib.${system}.run { - src = self; - hooks = builtins.removeAttrs gitHooksConfig [ "enable" ]; - }; - } - else - { }; - - # Flake apps - spApps = spConfig.flakeApps or { }; - - # Process-compose integration - processes = stackpanelShell.passthru.processes or { }; - hasProcesses = processes != { }; - processComposeApp = - if hasProcessCompose && enabled && hasProcesses then - { - # dev is the deafult command for developing on the respective repo. - # The "golden path" of using stackpanel is: - # ``` - # $ nix develop - # $ dev - # ``` - # This would start process-compose which starts all apps, services, etc. - dev = { - type = "app"; - program = "${pkgs.process-compose}/bin/process-compose"; - }; - } - else - { }; -in -# Return the per-system flake outputs -{ - devShells = if enabled then { default = stackpanelShell; } else { }; - - packages = if enabled then directPkgs // containerPackages else { }; - - # Helpers for introspection - can be used to get LSP features for nixd and nil - legacyPackages = { - stackpanelConfig = stackpanelSerializable; - stackpanelFullConfig = spConfig; - stackpanelPackages = allSerializedPackages; - stackpanelOptions = stackpanelEval.options.stackpanel or { }; - stackpanelRawConfig = serializeLib.filterSerializable loadedConfig; - inherit stackpanelSpeculate; - } - // (if enabled then nestedPkgs else { }); - - checks = if enabled then allChecks // gitHooksCheck else { }; - - apps = if enabled then spApps // containerApps // processComposeApp else { }; -} diff --git a/nix/flake/templates/devenv/.envrc b/nix/flake/templates/devenv/.envrc deleted file mode 100644 index bb13dae3..00000000 --- a/nix/flake/templates/devenv/.envrc +++ /dev/null @@ -1 +0,0 @@ -use devenv diff --git a/nix/flake/templates/example-monorepo/.env.example b/nix/flake/templates/example-monorepo/.env.example deleted file mode 100644 index db1762f4..00000000 --- a/nix/flake/templates/example-monorepo/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -API_KEY=foo -ALCHEMY_PASSWORD=todo \ No newline at end of file diff --git a/nix/flake/templates/example-monorepo/.gitignore b/nix/flake/templates/example-monorepo/.gitignore deleted file mode 100644 index a585169a..00000000 --- a/nix/flake/templates/example-monorepo/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.alchemy/ -.turbo/ -node_modules/ -lib/ \ No newline at end of file diff --git a/nix/flake/templates/example-monorepo/apps/analytics/alchemy.run.ts b/nix/flake/templates/example-monorepo/apps/analytics/alchemy.run.ts deleted file mode 100644 index 3cfcfa4f..00000000 --- a/nix/flake/templates/example-monorepo/apps/analytics/alchemy.run.ts +++ /dev/null @@ -1,18 +0,0 @@ -import alchemy from "alchemy"; -import { Worker } from "alchemy/cloudflare"; -import path from "node:path"; - -const app = await alchemy("analytics"); - -export const analytics = await Worker("worker", { - entrypoint: path.join(import.meta.dirname, "src", "index.ts"), - bindings: { - API_KEY: alchemy.secret.env.API_KEY, - }, -}); - -if (import.meta.main) { - console.log({ url: analytics.url }); -} - -await app.finalize(); diff --git a/nix/flake/templates/example-monorepo/apps/analytics/package.json b/nix/flake/templates/example-monorepo/apps/analytics/package.json deleted file mode 100644 index ffd17f24..00000000 --- a/nix/flake/templates/example-monorepo/apps/analytics/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "analytics", - "private": true, - "type": "module", - "scripts": { - "dev": "alchemy dev --adopt --app analytics", - "deploy": "alchemy deploy --adopt --app analytics", - "destroy": "alchemy destroy --app analytics", - "check": "tsc --noEmit" - }, - "exports": { - "./alchemy": "./alchemy.run.ts" - }, - "dependencies": { - "alchemy": "catalog:" - } -} diff --git a/nix/flake/templates/example-monorepo/apps/analytics/src/index.ts b/nix/flake/templates/example-monorepo/apps/analytics/src/index.ts deleted file mode 100644 index 868e9101..00000000 --- a/nix/flake/templates/example-monorepo/apps/analytics/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { analytics } from "../alchemy.run.js"; - -export default { - fetch: async (req, env) => { - const bearer = req.headers.get("Authorization"); - if (bearer !== `Bearer ${env.API_KEY}`) { - return new Response("Unauthorized", { status: 401 }); - } - return new Response("Welcome, you are authorized!"); - }, -} as ExportedHandler; diff --git a/nix/flake/templates/example-monorepo/apps/analytics/tsconfig.json b/nix/flake/templates/example-monorepo/apps/analytics/tsconfig.json deleted file mode 100644 index 7963a924..00000000 --- a/nix/flake/templates/example-monorepo/apps/analytics/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "include": ["alchemy.run.ts", "src/**/*.ts"], - "exclude": ["node_modules", "lib"], - "compilerOptions": { - "rootDir": ".", - "outDir": "./lib", - "composite": true, - "module": "ESNext", - "target": "ESNext", - "moduleResolution": "bundler", - "types": ["@cloudflare/workers-types"], - "declaration": true, - "declarationMap": true, - "skipLibCheck": true - } - } \ No newline at end of file diff --git a/nix/flake/templates/example-monorepo/apps/backend/alchemy.run.ts b/nix/flake/templates/example-monorepo/apps/backend/alchemy.run.ts deleted file mode 100644 index 68355549..00000000 --- a/nix/flake/templates/example-monorepo/apps/backend/alchemy.run.ts +++ /dev/null @@ -1,22 +0,0 @@ -import alchemy from "alchemy"; -import { D1Database, Worker } from "alchemy/cloudflare"; -import path from "node:path"; - -const app = await alchemy("backend"); - -const db = await D1Database("db"); - -export const backend = await Worker("worker", { - entrypoint: path.join(import.meta.dirname, "src", "index.ts"), - bindings: { - db, - API_KEY: alchemy.secret.env.API_KEY, - key: "value", - }, -}); - -if (import.meta.main) { - console.log({ url: backend.url }); -} - -await app.finalize(); diff --git a/nix/flake/templates/example-monorepo/apps/backend/package.json b/nix/flake/templates/example-monorepo/apps/backend/package.json deleted file mode 100644 index 7daea0be..00000000 --- a/nix/flake/templates/example-monorepo/apps/backend/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "backend", - "private": true, - "type": "module", - "module": "./lib/src/index.js", - "scripts": { - "dev": "alchemy dev --adopt --app backend", - "deploy": "alchemy deploy --adopt --app backend", - "destroy": "alchemy destroy --app backend", - "check": "tsc --noEmit" - }, - "exports": { - "./alchemy": "./alchemy.run.ts" - }, - "dependencies": { - "alchemy": "catalog:" - } -} diff --git a/nix/flake/templates/example-monorepo/apps/backend/src/index.ts b/nix/flake/templates/example-monorepo/apps/backend/src/index.ts deleted file mode 100644 index 11ad6823..00000000 --- a/nix/flake/templates/example-monorepo/apps/backend/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { backend } from "../alchemy.run.ts"; - -export default { - fetch: async (req, env) => { - const bearer = req.headers.get("Authorization"); - if (bearer !== `Bearer ${env.API_KEY}`) { - return new Response("Unauthorized", { status: 401 }); - } - return new Response("Welcome, you are authorized!"); - }, -} as ExportedHandler; diff --git a/nix/flake/templates/example-monorepo/apps/backend/tsconfig.json b/nix/flake/templates/example-monorepo/apps/backend/tsconfig.json deleted file mode 100644 index 0f141882..00000000 --- a/nix/flake/templates/example-monorepo/apps/backend/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "include": ["alchemy.run.ts", "src/**/*.ts"], - "exclude": ["node_modules", "lib"], - "compilerOptions": { - "rootDir": ".", - "outDir": "./lib", - "composite": true, - "module": "ESNext", - "target": "ESNext", - "moduleResolution": "bundler", - "types": ["@cloudflare/workers-types"], - "declaration": true, - "declarationMap": true, - "skipLibCheck": true - } -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/README.md b/nix/flake/templates/example-monorepo/apps/frontend/README.md deleted file mode 100644 index 1cb7d588..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# React + TypeScript + Vite - -[![Deployed with Alchemy](https://alchemy.run/alchemy-badge.svg)](https://alchemy.run) - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default tseslint.config({ - extends: [ - // Remove ...tseslint.configs.recommended and replace with this - ...tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - ...tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - ...tseslint.configs.stylisticTypeChecked, - ], - languageOptions: { - // other options... - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - }, -}) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default tseslint.config({ - plugins: { - // Add the react-x and react-dom plugins - 'react-x': reactX, - 'react-dom': reactDom, - }, - rules: { - // other rules... - // Enable its recommended typescript rules - ...reactX.configs['recommended-typescript'].rules, - ...reactDom.configs.recommended.rules, - }, -}) -``` diff --git a/nix/flake/templates/example-monorepo/apps/frontend/alchemy.run.ts b/nix/flake/templates/example-monorepo/apps/frontend/alchemy.run.ts deleted file mode 100644 index cf52bb2d..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/alchemy.run.ts +++ /dev/null @@ -1,19 +0,0 @@ -import alchemy from "alchemy"; -import { Vite } from "alchemy/cloudflare"; -import { analytics } from "analytics/alchemy"; -import { backend } from "backend/alchemy"; - -const app = await alchemy("frontend"); - -export const frontend = await Vite("website", { - bindings: { - backend, - analytics, - }, -}); - -console.log({ - url: frontend.url, -}); - -await app.finalize(); diff --git a/nix/flake/templates/example-monorepo/apps/frontend/eslint.config.js b/nix/flake/templates/example-monorepo/apps/frontend/eslint.config.js deleted file mode 100644 index 79a552ea..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -import js from "@eslint/js"; -import globals from "globals"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import tseslint from "typescript-eslint"; - -export default tseslint.config( - { ignores: ["dist"] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ["**/*.{ts,tsx}"], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - "react-hooks": reactHooks, - "react-refresh": reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - }, - }, -); diff --git a/nix/flake/templates/example-monorepo/apps/frontend/index.html b/nix/flake/templates/example-monorepo/apps/frontend/index.html deleted file mode 100644 index e4b78eae..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Vite + React + TS - - -
- - - diff --git a/nix/flake/templates/example-monorepo/apps/frontend/package.json b/nix/flake/templates/example-monorepo/apps/frontend/package.json deleted file mode 100644 index 2884d6ce..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "type": "module", - "name": "frontend", - "private": true, - "version": "0.0.0", - "scripts": { - "dev": "alchemy dev --adopt --app frontend", - "deploy": "alchemy deploy --adopt --app frontend", - "destroy": "alchemy destroy --app frontend", - "lint": "eslint .", - "preview": "vite preview", - "check": "tsc --noEmit" - }, - "dependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0" - }, - "devDependencies": { - "analytics": "workspace:*", - "backend": "workspace:*", - "alchemy": "catalog:", - "@cloudflare/vite-plugin": "catalog:", - "@cloudflare/workers-types": "catalog:", - "miniflare": "^4.20250617.3", - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "eslint": "^9.25.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "typescript": "^5.8.3", - "typescript-eslint": "^8.30.1", - "vite": "catalog:" - } -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/public/vite.svg b/nix/flake/templates/example-monorepo/apps/frontend/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/App.css b/nix/flake/templates/example-monorepo/apps/frontend/src/App.css deleted file mode 100644 index b9d355df..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/App.tsx b/nix/flake/templates/example-monorepo/apps/frontend/src/App.tsx deleted file mode 100644 index e25f329e..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/App.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState } from "react"; -import reactLogo from "./assets/react.svg"; -import viteLogo from "/vite.svg"; -import "./App.css"; - -function App() { - const [count, setCount] = useState(0); - - return ( - <> - -

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ); -} - -export default App; diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/assets/react.svg b/nix/flake/templates/example-monorepo/apps/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/index.css b/nix/flake/templates/example-monorepo/apps/frontend/src/index.css deleted file mode 100644 index 08a3ac9e..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/main.tsx b/nix/flake/templates/example-monorepo/apps/frontend/src/main.tsx deleted file mode 100644 index eff7ccc6..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import "./index.css"; -import App from "./App.tsx"; - -createRoot(document.getElementById("root")!).render( - - - , -); diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/vite-env.d.ts b/nix/flake/templates/example-monorepo/apps/frontend/src/vite-env.d.ts deleted file mode 100644 index bc2d8a36..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/nix/flake/templates/example-monorepo/apps/frontend/src/worker.ts b/nix/flake/templates/example-monorepo/apps/frontend/src/worker.ts deleted file mode 100644 index a8100c5e..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/src/worker.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { frontend } from "../alchemy.run.ts"; - -export default { - fetch(request) { - const url = new URL(request.url); - - if (url.pathname.startsWith("/api/")) { - return Response.json({ - name: "Cloudflare", - }); - } - return new Response(null, { status: 404 }); - }, -} satisfies ExportedHandler; diff --git a/nix/flake/templates/example-monorepo/apps/frontend/tsconfig.json b/nix/flake/templates/example-monorepo/apps/frontend/tsconfig.json deleted file mode 100644 index 73a085db..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/tsconfig.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "exclude": ["node_modules", "lib", "test"], - "include": [ - "types/**/*.ts", - "src/**/*.ts", - "alchemy.run.ts", - "vite.config.ts" - ], - "compilerOptions": { - "outDir": "lib", - "target": "es2021", - "lib": ["es2021"], - "jsx": "react-jsx", - "module": "es2022", - "moduleResolution": "Bundler", - "resolveJsonModule": true, - "allowJs": true, - "checkJs": false, - "isolatedModules": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "allowImportingTsExtensions": true, - "rewriteRelativeImportExtensions": true, - "strict": true, - "skipLibCheck": true, - "types": ["@cloudflare/workers-types", "./types/env.d.ts"] - }, - "references": [ - { - "path": "../analytics" - }, - { - "path": "../backend" - } - ] -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/types/env.d.ts b/nix/flake/templates/example-monorepo/apps/frontend/types/env.d.ts deleted file mode 100644 index 445d146c..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/types/env.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -// This file infers types for the cloudflare:workers environment from your Alchemy Worker. -// @see https://alchemy.run/concepts/bindings/#type-safe-bindings - -import type { website } from "../alchemy.run.ts"; - -export type CloudflareEnv = typeof website.Env; - -declare global { - type Env = CloudflareEnv; -} - -declare module "cloudflare:workers" { - namespace Cloudflare { - export interface Env extends CloudflareEnv {} - } -} diff --git a/nix/flake/templates/example-monorepo/apps/frontend/vite.config.ts b/nix/flake/templates/example-monorepo/apps/frontend/vite.config.ts deleted file mode 100644 index de929a24..00000000 --- a/nix/flake/templates/example-monorepo/apps/frontend/vite.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import react from "@vitejs/plugin-react"; -import alchemy from "alchemy/cloudflare/vite"; -import { defineConfig, type PluginOption } from "vite-plus"; - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react(), alchemy() as PluginOption], -}); diff --git a/nix/flake/templates/example-monorepo/bun.lock b/nix/flake/templates/example-monorepo/bun.lock deleted file mode 100644 index f0b95143..00000000 --- a/nix/flake/templates/example-monorepo/bun.lock +++ /dev/null @@ -1,797 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 0, - "workspaces": { - "": { - "name": "monorepo", - "dependencies": { - "miniflare": "^4.20250906.0", - }, - "devDependencies": { - "alchemy": "catalog:", - "turbo": "^2.5.6", - }, - }, - "apps/analytics": { - "name": "analytics", - "dependencies": { - "alchemy": "catalog:", - }, - }, - "apps/backend": { - "name": "backend", - "dependencies": { - "alchemy": "catalog:", - }, - }, - "apps/frontend": { - "name": "frontend", - "version": "0.0.0", - "dependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0", - }, - "devDependencies": { - "@cloudflare/vite-plugin": "catalog:", - "@cloudflare/workers-types": "catalog:", - "@eslint/js": "^9.25.0", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "@vitejs/plugin-react": "^4.4.1", - "alchemy": "catalog:", - "analytics": "workspace:*", - "backend": "workspace:*", - "eslint": "^9.25.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^16.0.0", - "miniflare": "^4.20250617.3", - "typescript": "^5.8.3", - "typescript-eslint": "^8.30.1", - "vite": "catalog:", - }, - }, - }, - "catalog": { - "@cloudflare/vite-plugin": "^1.13.14", - "@cloudflare/workers-types": "^4.20250805.0", - "alchemy": "link:alchemy", - "typescript": "^5.8.3", - "vite": "^7.1.9", - }, - "packages": { - "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - - "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], - - "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], - - "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], - - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - - "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], - - "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], - - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], - - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - - "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], - - "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], - - "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], - - "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], - - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.8", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Ky929MfHh+qPhwCapYrRPwPVHtA2Ioex/DbGZyskGyNRDe9Ru3WThYZivyNVaPy5ergQSgMs9OKrM9Ajtz9F6w=="], - - "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.13.18", "", { "dependencies": { "@cloudflare/unenv-preset": "2.7.8", "@remix-run/node-fetch-server": "^0.8.0", "get-port": "^7.1.0", "miniflare": "4.20251011.1", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.21", "wrangler": "4.45.3", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-1NO7oQWdO3hQMRAYIEd1yiGrd3hVvmLNeSHzTcpURMGh8LMbDGCPUsNWHdF6AoMGH3xC411o0FiwDh16nxWtqg=="], - - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250906.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-E+X/YYH9BmX0ew2j/mAWFif2z05NMNuhCTlNYEGLkqMe99K15UewBqajL9pMcMUKxylnlrEoK3VNxl33DkbnPA=="], - - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250906.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-X5apsZ1SFW4FYTM19ISHf8005FJMPfrcf4U5rO0tdj+TeJgQgXuZ57IG0WeW7SpLVeBo8hM6WC8CovZh41AfnA=="], - - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250906.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rlKzWgsLnlQ5Nt9W69YBJKcmTmZbOGu0edUsenXPmc6wzULUxoQpi7ZE9k3TfTonJx4WoQsQlzCUamRYFsX+0Q=="], - - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250906.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-DdedhiQ+SeLzpg7BpcLrIPEZ33QKioJQ1wvL4X7nuLzEB9rWzS37NNNahQzc1+44rhG4fyiHbXBPOeox4B9XVA=="], - - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250906.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q8Qjfs8jGVILnZL6vUpQ90q/8MTCYaGR3d1LGxZMBqte8Vr7xF3KFHPEy7tFs0j0mMjnqCYzlofmPNY+9ZaDRg=="], - - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250909.0", "", {}, "sha512-3WvDDOvFGOAlBUZpWe2N+9BxB1kRt9eHo3AhIKg1fihNdqrGxyh9cJe1MpYs5BI8gXSRxjkDvzPVebRED8iwOQ=="], - - "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], - - "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], - - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], - - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], - - "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], - - "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], - - "@eslint/js": ["@eslint/js@9.35.0", "", {}, "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw=="], - - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], - - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], - - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], - - "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], - - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], - - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], - - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], - - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], - - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], - - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], - - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], - - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], - - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], - - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], - - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], - - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], - - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - - "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], - - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - - "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="], - - "@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="], - - "@poppinss/exception": ["@poppinss/exception@1.2.2", "", {}, "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg=="], - - "@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.0", "", {}, "sha512-8/sKegb4HrM6IdcQeU0KPhj9VOHm5SUqswJDHuMCS3mwbr/NRx078QDbySmn0xslahvvZoOENd7EnK40kWKxkg=="], - - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w=="], - - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.1", "", { "os": "none", "cpu": "arm64" }, "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.1", "", { "os": "win32", "cpu": "x64" }, "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA=="], - - "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], - - "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="], - - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], - - "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], - - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], - - "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - - "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="], - - "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="], - - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.43.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw=="], - - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.43.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.43.0", "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0" } }, "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg=="], - - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.43.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA=="], - - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@8.43.0", "", {}, "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.43.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.43.0", "@typescript-eslint/tsconfig-utils": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.43.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g=="], - - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw=="], - - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - - "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], - - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - - "acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="], - - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "alchemy": ["alchemy@link:alchemy", { "bin": { "alchemy": "bin/alchemy.js" } }], - - "analytics": ["analytics@workspace:apps/analytics"], - - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "backend": ["backend@workspace:apps/backend"], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], - - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "browserslist": ["browserslist@4.25.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001737", "electron-to-chromium": "^1.5.211", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg=="], - - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001741", "", {}, "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw=="], - - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], - - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - - "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], - - "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], - - "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.215", "", {}, "sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ=="], - - "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], - - "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@9.35.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg=="], - - "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], - - "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.20", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA=="], - - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - - "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], - - "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - - "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - - "frontend": ["frontend@workspace:apps/frontend"], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - - "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], - - "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], - - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - - "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], - - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], - - "miniflare": ["miniflare@4.20250906.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250906.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-T/RWn1sa0ien80s6NjU+Un/tj12gR6wqScZoiLeMJDD4/fK0UXfnbWXJDubnUED8Xjm7RPQ5ESYdE+mhPmMtuQ=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - - "node-releases": ["node-releases@2.0.20", "", {}, "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA=="], - - "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], - - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], - - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - - "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], - - "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], - - "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - - "rollup": ["rollup@4.50.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.1", "@rollup/rollup-android-arm64": "4.50.1", "@rollup/rollup-darwin-arm64": "4.50.1", "@rollup/rollup-darwin-x64": "4.50.1", "@rollup/rollup-freebsd-arm64": "4.50.1", "@rollup/rollup-freebsd-x64": "4.50.1", "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", "@rollup/rollup-linux-arm-musleabihf": "4.50.1", "@rollup/rollup-linux-arm64-gnu": "4.50.1", "@rollup/rollup-linux-arm64-musl": "4.50.1", "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", "@rollup/rollup-linux-ppc64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-musl": "4.50.1", "@rollup/rollup-linux-s390x-gnu": "4.50.1", "@rollup/rollup-linux-x64-gnu": "4.50.1", "@rollup/rollup-linux-x64-musl": "4.50.1", "@rollup/rollup-openharmony-arm64": "4.50.1", "@rollup/rollup-win32-arm64-msvc": "4.50.1", "@rollup/rollup-win32-ia32-msvc": "4.50.1", "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA=="], - - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], - - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - - "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], - - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="], - - "turbo-darwin-64": ["turbo-darwin-64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A=="], - - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA=="], - - "turbo-linux-64": ["turbo-linux-64@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA=="], - - "turbo-linux-arm64": ["turbo-linux-arm64@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ=="], - - "turbo-windows-64": ["turbo-windows-64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg=="], - - "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="], - - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - - "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], - - "typescript-eslint": ["typescript-eslint@8.43.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.43.0", "@typescript-eslint/parser": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w=="], - - "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], - - "undici": ["undici@7.15.0", "", {}, "sha512-7oZJCPvvMvTd0OlqWsIxTuItTpJBpU1tcbVl24FMn3xt3+VSunwUasmfPJRE57oNO1KsZ4PgA1xTdAX4hq8NyQ=="], - - "unenv": ["unenv@2.0.0-rc.21", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="], - - "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], - - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - - "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - - "workerd": ["workerd@1.20250906.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250906.0", "@cloudflare/workerd-darwin-arm64": "1.20250906.0", "@cloudflare/workerd-linux-64": "1.20250906.0", "@cloudflare/workerd-linux-arm64": "1.20250906.0", "@cloudflare/workerd-windows-64": "1.20250906.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ryVyEaqXPPsr/AxccRmYZZmDAkfQVjhfRqrNTlEeN8aftBk6Ca1u7/VqmfOayjCXrA+O547TauebU+J3IpvFXw=="], - - "wrangler": ["wrangler@4.45.3", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.8", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-0ddEA9t4HeBgSVTVTcqtBHl7Z5CorWZ8tGgTQCP5XuL+9E1TJRwS6t/zzG51Ruwjb17SZYCaLchoM8V629S8cw=="], - - "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], - - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - - "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], - - "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - - "zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], - - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], - - "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "@cloudflare/vite-plugin/miniflare": ["miniflare@4.20251011.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Qbw1Z8HTYM1adWl6FAtzhrj34/6dPRDPwdYOx21dkae8a/EaxbMzRIPbb4HKVGMVvtqbK1FaRCgDLVLolNzGHg=="], - - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - - "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], - - "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], - - "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "espree/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "frontend/miniflare": ["miniflare@4.20251011.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Qbw1Z8HTYM1adWl6FAtzhrj34/6dPRDPwdYOx21dkae8a/EaxbMzRIPbb4HKVGMVvtqbK1FaRCgDLVLolNzGHg=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], - - "wrangler/miniflare": ["miniflare@4.20251011.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Qbw1Z8HTYM1adWl6FAtzhrj34/6dPRDPwdYOx21dkae8a/EaxbMzRIPbb4HKVGMVvtqbK1FaRCgDLVLolNzGHg=="], - - "wrangler/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="], - - "@cloudflare/vite-plugin/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="], - - "@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="], - - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "frontend/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="], - - "frontend/miniflare/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="], - - "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], - - "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], - - "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], - - "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], - - "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], - - "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], - - "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], - - "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], - - "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], - - "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], - - "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], - - "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], - - "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], - - "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], - - "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], - - "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], - - "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], - - "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], - - "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], - - "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], - - "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], - - "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], - - "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], - - "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], - - "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], - - "wrangler/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="], - - "wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="], - - "wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="], - - "wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="], - - "wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="], - - "wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="], - - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="], - - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="], - - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="], - - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="], - - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="], - - "frontend/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="], - - "frontend/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="], - - "frontend/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="], - - "frontend/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="], - - "frontend/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="], - } -} diff --git a/nix/flake/templates/example-monorepo/package.json b/nix/flake/templates/example-monorepo/package.json deleted file mode 100644 index a97a2fe6..00000000 --- a/nix/flake/templates/example-monorepo/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "monorepo", - "private": true, - "scripts": { - "build": "tsc -b", - "dev": "tsc -b && turbo run dev", - "deploy": "tsc -b && turbo run deploy --ui=stream", - "destroy": "tsc -b && turbo run destroy --ui=stream", - "link:alchemy": "cd ../alchemy && bun tsc -b && bun link && cd ../example-monorepo && bun i && cp .env.example .env" - }, - "type": "module", - "packageManager": "bun@1.2.21", - "workspaces": { - "packages": [ - "apps/*" - ], - "catalog": { - "alchemy": "link:alchemy", - "@cloudflare/workers-types": "^4.20250805.0", - "@cloudflare/vite-plugin": "^1.13.14", - "vite": "^7.1.9", - "typescript": "^5.8.3" - } - }, - "devDependencies": { - "alchemy": "catalog:", - "turbo": "^2.5.6" - }, - "dependencies": { - "miniflare": "^4.20250906.0" - } -} diff --git a/nix/flake/templates/example-monorepo/tsconfig.json b/nix/flake/templates/example-monorepo/tsconfig.json deleted file mode 100644 index a627fa03..00000000 --- a/nix/flake/templates/example-monorepo/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./apps/backend" - }, - { - "path": "./apps/analytics" - }, - { - "path": "./apps/frontend" - } - ] -} diff --git a/nix/flake/templates/example-monorepo/turbo.json b/nix/flake/templates/example-monorepo/turbo.json deleted file mode 100644 index cad059d7..00000000 --- a/nix/flake/templates/example-monorepo/turbo.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "https://turborepo.com/schema.json", - "ui": "tui", - "tasks": { - "deploy": { - "dependsOn": ["^deploy"], - "cache": false - }, - "dev": { - "persistent": true, - "cache": false - }, - "analytics#destroy": { - "cache": false, - "dependsOn": ["frontend#destroy"] - }, - "backend#destroy": { - "cache": false, - "dependsOn": ["frontend#destroy"] - }, - "frontend#destroy": { - "cache": false - } - } -} diff --git a/nix/internal/flake/README.md b/nix/internal/flake/README.md deleted file mode 100644 index 1df4e92c..00000000 --- a/nix/internal/flake/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# nix/internal/flake/ - -Historical notes for earlier flake output experiments. - -Active stackpanel flake integration now lives in `nix/flake/default.nix` and is exposed as `inputs.stackpanel.flakeModules.default`. Most consumers should use the higher-level wrapper: - -```nix -{ - inputs.stackpanel.url = "github:darkmatter/stackpanel"; - - outputs = inputs @ { self, stackpanel, ... }: - stackpanel.lib.mkFlake { - inherit inputs self; - - perSystem = { pkgs, ... }: { - packages.hello = pkgs.hello; - }; - }; -} -``` - -For extension modules, pass flake-parts modules through `imports` and Stackpanel modules through `stackpanelImports`: - -```nix -stackpanel.lib.mkFlake { - inherit inputs self; - imports = [ inputs.some-extension.flakeModules.default ]; - stackpanelImports = [ ./stackpanel-module.nix ]; -} -``` - -Configuration belongs in `.stack/config.nix` using native options such as `stackpanel.languages.*`, `stackpanel.packages`, `stackpanel.devshell.*`, and `stackpanel.process-compose`. - -Legacy `devenvModules`, `lib.wrapDevenv`, and standalone devenv template APIs were removed during the flake-parts migration. diff --git a/nix/internal/flake/default.nix b/nix/internal/flake/default.nix deleted file mode 100644 index 53448b40..00000000 --- a/nix/internal/flake/default.nix +++ /dev/null @@ -1,463 +0,0 @@ -# ============================================================================== -# default.nix - Stackpanel Flake Module -# -# THE single flake-parts module for stackpanel. This module: -# 1. Auto-loads config from .stack/config.nix -# 2. Evaluates stackpanel modules via lib.evalModules -# 3. Creates devShells.default via pkgs.mkShell with full passthru -# 4. Full control over passthru, shellHook ordering, packages -# -# Usage: -# imports = [ inputs.stackpanel.flakeModules.default ]; -# ============================================================================== -{ - localFlake, - withSystem, -}: -{ - lib, - self, - inputs, - config, - flake-parts-lib, - ... -}: -let - inherit (flake-parts-lib) mkPerSystemOption; - - # Serialization helpers for JSON-safe config - serializeLib = import ../stackpanel/lib/serialize.nix { inherit lib; }; - - # Check if user's flake has these optional inputs - hasProcessCompose = inputs ? process-compose-flake; - hasGitHooks = inputs ? git-hooks; -in -{ - # =========================================================================== - # Imports - # =========================================================================== - imports = [ - # Stackpanel options (pkgs-free, safe for flake-parts top-level) - ../stackpanel/core/options - ] - ++ lib.optional hasProcessCompose inputs.process-compose-flake.flakeModule - ++ lib.optional hasGitHooks inputs.git-hooks.flakeModule; - - # =========================================================================== - # Flake-level options - # =========================================================================== - options.stackpanel = { - projectRoot = lib.mkOption { - type = lib.types.nullOr (lib.types.either lib.types.path lib.types.str); - default = null; - description = "Project root path (string or path). Defaults to self (flake root)."; - }; - }; - - # =========================================================================== - # perSystem options - # =========================================================================== - options.perSystem = mkPerSystemOption ( - { lib, ... }: - { - options.stackpanel = { - imports = lib.mkOption { - type = lib.types.listOf lib.types.unspecified; - default = [ ]; - description = "Additional stackpanel module imports."; - }; - }; - } - ); - - # =========================================================================== - # Configuration - # =========================================================================== - config = - let - # Capture flake-level stackpanel config in the enclosing scope - # This makes it available to perSystem without needing _module.args - flakeLevelStackpanelConfig = config.stackpanel or { }; - in - lib.mkMerge [ - # ------------------------------------------------------------------------- - # perSystem configuration - # ------------------------------------------------------------------------- - { - perSystem = - { - system, - pkgs, - lib, - config, - ... - }: - let - # Top-level flake config (captured from enclosing scope) - flakeCfg = flakeLevelStackpanelConfig; - - # perSystem stackpanel config - perSystemCfg = config.stackpanel or { }; - - # Project root for loading config files - projectRoot = flakeCfg.projectRoot or self; - - # =================================================================== - # Auto-load stackpanel config from .stack/ - # Always use `self` for file discovery (works in pure evaluation) - # =================================================================== - internalConfigPath = self + "/.stack/_internal.nix"; - simpleConfigPath = self + "/.stack/config.nix"; - - hasInternalConfig = builtins.pathExists internalConfigPath; - hasSimpleConfig = builtins.pathExists simpleConfigPath; - - loadConfig = - path: - let - raw = import path; - result = - if builtins.isFunction raw then - raw { - inherit - pkgs - lib - inputs - self - ; - config = result; - } - else - raw; - in - result; - - loadedConfig = - if hasInternalConfig then - loadConfig internalConfigPath - else if hasSimpleConfig then - loadConfig simpleConfigPath - else - { }; - - # Compute the effective root - prefer flake-level projectRoot, fallback to self - effectiveRoot = - let - flakeRoot = flakeCfg.projectRoot or null; - in - if flakeRoot != null then flakeRoot else toString self; - - stackpanelConfigModule = { - stackpanel = loadedConfig // { - # Defaults to the flake source store path; users can override - # via the flake-level `stackpanel.projectRoot` option. - root = lib.mkDefault effectiveRoot; - }; - }; - - # Git hooks config (from stack config) - gitHooksConfig = loadedConfig.git-hooks or { }; - - # =================================================================== - # Evaluate stackpanel modules - # =================================================================== - stackpanelEval = lib.evalModules { - modules = [ - ../stackpanel - stackpanelConfigModule - ] - ++ (perSystemCfg.imports or [ ]); - specialArgs = { - inherit - pkgs - lib - inputs - self - localFlake - ; - }; - }; - - spConfig = stackpanelEval.config.stackpanel; - devshellOutputs = spConfig.devshell; - - # =================================================================== - # Build shell hook from stackpanel hooks - # =================================================================== - hooks = - devshellOutputs.hooks or { - before = [ ]; - main = [ ]; - after = [ ]; - }; - - stackpanelHook = lib.concatStringsSep "\n\n" ( - lib.flatten [ - hooks.before - hooks.main - hooks.after - ] - ); - - # =================================================================== - # Combine all packages - # =================================================================== - allPackages = (devshellOutputs.packages or [ ]) ++ (devshellOutputs._commandPkgs or [ ]); - - # =================================================================== - # Unified profile: single buildEnv instead of 90+ PATH entries - # - # When the profile module is enabled, we pass the profile derivation - # to mkShell instead of the full individual package list. This - # collapses PATH down to a single entry. - # - # allPackages is still kept around for serialization / passthru. - # =================================================================== - profileEnabled = devshellOutputs.profile.enable or false; - profileDrv = devshellOutputs.profile.package or null; - - shellPackages = if profileEnabled && profileDrv != null then [ profileDrv ] else allPackages; - - # =================================================================== - # All env vars (from stackpanel modules only) - # =================================================================== - allEnv = devshellOutputs.env or { }; - - # =================================================================== - # Build complete shellHook content - # =================================================================== - shellHookContent = '' - # ================================================================ - # Stackpanel Shell Hook (wrapper) - # Generated by: nix/flake/default.nix - # ================================================================ - - __stackpanel_shell_hook_main() { - # Prevent the hook from running twice in the same shell - if [[ -n "''${__STACKPANEL_HOOK_RAN:-}" ]]; then - return 0 - fi - __STACKPANEL_HOOK_RAN=1 - - # Export environment variables - ${lib.concatStringsSep "\n" ( - lib.mapAttrsToList (k: v: "export ${k}=${lib.escapeShellArg (toString v)}") allEnv - )} - - # Stackpanel hooks (includes language toolchain setup from - # stackpanel.languages.* modules: GOPATH/bin, node_modules/.bin, etc.) - ${stackpanelHook} - } - - # Run the hook in the CURRENT shell so exports and eval statements - # (like starship init) persist. We must NOT use a pipe here because - # pipes run the left-hand side in a subshell, losing all side effects. - # Output goes to stderr so direnv doesn't capture it. - __stackpanel_shell_hook_main >&2 - ''; - - # Write shellHook to a file in the Nix store - shellHookFile = pkgs.writeTextFile { - name = "stackpanel-shellhook"; - text = shellHookContent; - executable = true; - destination = "/shellhook.sh"; - }; - - # =================================================================== - # JSON-safe serialized config - # =================================================================== - stackpanelSerializable = serializeLib.filterSerializable spConfig; - - serializedPackages = map serializeLib.serializePackage allPackages; - - userPackagesCfg = - spConfig.userPackages or { - enable = false; - serialized = [ ]; - }; - userPackagesSerialized = - if userPackagesCfg.enable or false then userPackagesCfg.serialized or [ ] else [ ]; - - allSerializedPackages = serializedPackages ++ userPackagesSerialized; - - # =================================================================== - # Create shell with pkgs.mkShell - # =================================================================== - stackpanelShell = pkgs.mkShell { - name = "stackpanel-${spConfig.name or "dev"}"; - - packages = shellPackages; - nativeBuildInputs = devshellOutputs.nativeBuildInputs or [ ]; - buildInputs = devshellOutputs.buildInputs or [ ]; - - # Export path to shellHook file for inspection/debugging - STACKPANEL_SHELL_HOOK_PATH = "${shellHookFile}/shellhook.sh"; - - # Minimal shellHook that sources the full hook from the store - shellHook = '' - # Source the full shellHook from the Nix store - source "${shellHookFile}/shellhook.sh" - - # Symlink to state dir for easy inspection (after STACKPANEL_STATE_DIR is set) - if [[ -n "''${STACKPANEL_STATE_DIR:-}" ]]; then - mkdir -p "$STACKPANEL_STATE_DIR" - ln -sf "${shellHookFile}/shellhook.sh" "$STACKPANEL_STATE_DIR/shellhook.sh" - fi - ''; - - # FULL CONTROL over passthru - passthru = { - stackpanelConfig = stackpanelSerializable; - inherit stackpanelSerializable; - stackpanelPackages = allSerializedPackages; - devshellConfig = devshellOutputs; - packages = allPackages; - env = allEnv; - processes = spConfig.process-compose.processes or { }; - }; - }; - - in - lib.mkMerge [ - # Make stackpanel helpers available - { - _module.args.stackpanel = { - inherit localFlake; - packages = withSystem system ({ config, ... }: config.packages or { }); - }; - } - - # Expose config via legacyPackages - { - legacyPackages = { - stackpanelConfig = stackpanelSerializable; - stackpanelFullConfig = spConfig; - stackpanelPackages = allSerializedPackages; - stackpanelOptions = stackpanelEval.options.stackpanel or { }; - stackpanelRawConfig = serializeLib.filterSerializable loadedConfig; - }; - } - - # Set devShells.default - (lib.mkIf (spConfig.enable or false) { - devShells.default = lib.mkForce stackpanelShell; - }) - - # Expose stackpanel.outputs as flake packages - (lib.mkIf (spConfig.enable or false) ( - let - outputs = spConfig.outputs or { }; - directPkgs = lib.filterAttrs (_: v: lib.isDerivation v) outputs; - nestedPkgs = lib.filterAttrs (_: v: builtins.isAttrs v && !(lib.isDerivation v)) outputs; - in - { - packages = directPkgs; - legacyPackages = nestedPkgs; - } - )) - - # Expose container images and copy scripts as flake outputs - (lib.mkIf (spConfig.enable or false) ( - let - containersComputed = spConfig.containersComputed or { }; - containerImages = containersComputed.images or { }; - copyScripts = containersComputed.copyScripts or { }; - in - lib.mkIf (containerImages != { }) { - packages = lib.mapAttrs' (name: image: { - name = "container-${name}"; - value = image; - }) containerImages; - - apps = lib.mapAttrs' (name: script: { - name = "copy-container-${name}"; - value = { - type = "app"; - program = "${script}"; - }; - }) copyScripts; - } - )) - - # Expose checks - (lib.mkIf (spConfig.enable or false) ( - let - simpleChecks = spConfig.checks or { }; - moduleChecks = spConfig.moduleChecksFlattened or { }; - allChecks = simpleChecks // moduleChecks; - in - lib.mkIf (allChecks != { }) { - checks = allChecks; - } - )) - - # Expose flakeApps - (lib.mkIf (spConfig.enable or false) ( - let - spApps = spConfig.flakeApps or { }; - in - lib.mkIf (spApps != { }) { - apps = spApps; - } - )) - - # Git hooks check - (lib.mkIf (hasGitHooks && (gitHooksConfig.enable or false)) { - checks.pre-commit-check = inputs.git-hooks.lib.${system}.run { - src = projectRoot; - hooks = builtins.removeAttrs gitHooksConfig [ "enable" ]; - }; - }) - ]; - } - - # ------------------------------------------------------------------------- - # Process-compose integration (when process-compose-flake is present) - # ------------------------------------------------------------------------- - (lib.mkIf hasProcessCompose { - perSystem = - { config, lib, ... }: - let - shell = config.devShells.default or null; - processes = if shell != null then shell.passthru.processes or { } else { }; - hasProcesses = processes != { }; - sp = config.legacyPackages.stackpanelFullConfig or null; - enabled = sp != null && (sp.enable or false) && hasProcesses; - in - lib.mkIf enabled { - process-compose.dev.settings = { - environment = sp.process-compose.environment or { }; - inherit processes; - }; - }; - }) - - # ------------------------------------------------------------------------- - # Flake-level outputs - # ------------------------------------------------------------------------- - { - flake = { - stackpanelConfig = withSystem "aarch64-darwin" ( - { config, ... }: config.legacyPackages.stackpanelConfig or { } - ); - - stackpanelFullConfig = withSystem "aarch64-darwin" ( - { config, ... }: config.legacyPackages.stackpanelFullConfig or { } - ); - - stackpanelRawConfig = withSystem "aarch64-darwin" ( - { config, ... }: config.legacyPackages.stackpanelRawConfig or { } - ); - - stackpanelPackages = withSystem "aarch64-darwin" ( - { config, ... }: config.legacyPackages.stackpanelPackages or [ ] - ); - - stackpanelOptions = withSystem "aarch64-darwin" ( - { config, ... }: config.legacyPackages.stackpanelOptions or { } - ); - }; - } - ]; -} diff --git a/nix/internal/flake/devshells/README.md b/nix/internal/flake/devshells/README.md deleted file mode 100644 index fdca0768..00000000 --- a/nix/internal/flake/devshells/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# nix/internal/flake/devshells/ - -Historical notes for the old standalone devshell factory. - -Active Stackpanel shells are now produced through the flake-parts module exported as `inputs.stackpanel.flakeModules.default`, normally via `stackpanel.lib.mkFlake`: - -```nix -{ - inputs.stackpanel.url = "github:darkmatter/stackpanel"; - - outputs = inputs @ { self, stackpanel, ... }: - stackpanel.lib.mkFlake { - inherit inputs self; - }; -} -``` - -Configure shell behavior in `.stack/config.nix`: - -```nix -{ - stackpanel = { - packages = pkgs: [ pkgs.nodejs_22 pkgs.bun ]; - - devshell.env.NODE_ENV = "development"; - - languages.javascript.enable = true; - languages.go.enable = true; - }; -} -``` - -For reusable extensions, provide Stackpanel modules through `stackpanelImports`: - -```nix -stackpanel.lib.mkFlake { - inherit inputs self; - stackpanelImports = [ ./stackpanel-module.nix ]; -} -``` - -Legacy `inputs.stack.devenvModules.*`, `inputs.stack.lib.mkDevShell`, and `inputs.stack.lib.devshellModules.*` examples are no longer part of the public API. diff --git a/nix/internal/flake/exports.nix b/nix/internal/flake/exports.nix deleted file mode 100644 index 75009a3b..00000000 --- a/nix/internal/flake/exports.nix +++ /dev/null @@ -1,153 +0,0 @@ -# ============================================================================== -# exports.nix -# -# Consolidated flake exports for stackpanel. -# All user-facing outputs are defined here and imported by flake.nix. -# -# Simplified architecture: -# - ONE flakeModule (default) that handles everything -# - Config auto-loaded from .stack/ -# ============================================================================== -{ - withSystem, - flake-parts-lib, - self, -}: -let - inherit (flake-parts-lib) importApply; - - supportedSystems = [ - "x86_64-linux" - "aarch64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - # Function to get stackpanel options. - # Usage: inputs.stackpanel.lib.getOptions { inherit pkgs; } - # - # Returns the full stackpanel options attrset for introspection. - getOptions = - { pkgs }: - let - inherit (pkgs) lib; - evaluated = lib.evalModules { - modules = [ - ../stackpanel - { - _module.args = { - inherit pkgs lib; - inputs = { }; - }; - stackpanel.enable = true; - stackpanel.name = "options-eval"; - } - ]; - }; - in - evaluated.options.stackpanel; -in -{ - inherit supportedSystems; - - # =========================================================================== - # FLAKE MODULES (for flake-parts users) - # =========================================================================== - flakeModules = { - # Main stackpanel flake-parts module - # Auto-loads config, creates devShells.default, exposes outputs - # Usage: imports = [ inputs.stackpanel.flakeModules.default ]; - default = importApply ./default.nix { - localFlake = self; - inherit withSystem; - }; - }; - - # =========================================================================== - # NIXOS MODULES (for NixOS users) - # =========================================================================== - nixosModules = { - default = ../stackpanel/default.nix; - aws = ../stackpanel/integrations/services/aws; - network = ../stackpanel/network/network.nix; - secrets = ../stackpanel/secrets/default.nix; - theme = ../stackpanel/lib/theme.nix; - caddy = ../stackpanel/integrations/services/caddy.nix; - ci = ../stackpanel/apps/ci.nix; - }; - - # =========================================================================== - # LIBRARY FUNCTIONS - # =========================================================================== - lib = { - # AWS credential helpers - mkAwsCredScripts = import ../stackpanel/integrations/services/aws/lib.nix; - - # Step CA certificate helpers - mkStepScripts = import ../stackpanel/lib/services/step.nix; - - # Fly.io OIDC to AWS authentication - # Usage: inputs.stackpanel.lib.flyOidc { pkgs = pkgsLinux; } - flyOidc = import ../stackpanel/lib/services/fly-oidc.nix; - - # Get stackpanel module options for introspection - # Usage: inputs.stackpanel.lib.getOptions { inherit pkgs; } - inherit getOptions; - - # ========================================================================= - # DB Schema and Scaffolding - # ========================================================================= - - # Database schema module - contains all proto.nix schemas - # Usage: inputs.stackpanel.lib.db - db = import ../stackpanel/db { }; - - # Init files for scaffolding new projects - # Returns a map of relative paths to file contents: - # { ".stack/config.nix" = "..."; ".stack/_internal.nix" = "..."; ... } - # - # Usage from CLI: - # nix eval github:darkmatter/stackpanel#lib.initFiles --json - # - # Usage from Nix: - # inputs.stackpanel.lib.initFiles - inherit ((import ../stackpanel/db { })) initFiles; - - # All schemas for codegen/introspection - # Usage: inputs.stackpanel.lib.schemas - inherit ((import ../stackpanel/db { })) schemas; - }; - - # =========================================================================== - # TEMPLATES - # =========================================================================== - templates = { - default = { - path = ./templates/default; - description = "Stackpanel + flake-parts (recommended)"; - }; - minimal = { - path = ./templates/minimal; - description = "Stackpanel minimal setup"; - }; - # ========================================================================= - # Test Fixtures (for module authors and CI testing) - # ========================================================================= - test-basic = { - path = ./templates/_test-fixtures/basic; - description = "Test fixture: minimal config, no apps"; - }; - test-with-oxlint = { - path = ./templates/_test-fixtures/with-oxlint; - description = "Test fixture: OxLint module enabled"; - }; - test-full-stack = { - path = ./templates/_test-fixtures/full-stack; - description = "Test fixture: all features (multiple apps, modules)"; - }; - test-external-module = { - path = ./templates/_test-fixtures/external-module; - description = "Test fixture: for testing external modules"; - }; - }; -} diff --git a/nix/internal/flake/packages.nix b/nix/internal/flake/packages.nix deleted file mode 100644 index 7ac0944a..00000000 --- a/nix/internal/flake/packages.nix +++ /dev/null @@ -1,27 +0,0 @@ -# ============================================================================== -# packages.nix -# -# Package definitions for stackpanel. -# Defines the unified CLI/agent package built by this flake. -# -# The CLI and agent have been merged into a single application at apps/stackpanel-go. -# The agent is now a subcommand: `stack agent` -# ============================================================================== -{ - pkgs, -}: -let - # Unified CLI + Agent package - stackpanel = pkgs.callPackage ../stackpanel/packages/stackpanel-cli { }; -in -{ - # Main stackpanel package (CLI + agent unified) - inherit stackpanel; - - # Alias for backwards compatibility - stackpanel-cli = stackpanel; - - # Default package - default = stackpanel; - -} diff --git a/nix/internal/flake/templates/README.md b/nix/internal/flake/templates/README.md deleted file mode 100644 index 4e8eb705..00000000 --- a/nix/internal/flake/templates/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# Stack Templates - -Project templates for bootstrapping new Stack projects. - -## Available Templates - -| Template | Description | Use Case | -|----------|-------------|----------| -| `default` | devenv + flake-parts | Full-featured, recommended for most projects | -| `native` | Native Nix shell + flake-parts | When you want Stack without devenv | -| `devenv` | Standalone devenv.yaml | For devenv-first workflows (no flake.nix) | -| `minimal` | devenv without flake-parts | Simple flake without flake-parts | - -## Quick Start - -```bash -# Create a new directory -mkdir myproject && cd myproject - -# Initialize from template (choose one) -nix flake init -t github:darkmatter/stack # default -nix flake init -t github:darkmatter/stack#native # native -nix flake init -t github:darkmatter/stack#devenv # devenv -nix flake init -t github:darkmatter/stack#minimal # minimal - -# Enter the dev environment -direnv allow # or: nix develop --impure -``` - -## Template Details - -### default (Recommended) - -Full-featured setup with devenv and flake-parts integration. - -```bash -nix flake init -t github:darkmatter/stack -``` - -**Structure:** -``` -. -├── flake.nix # Flake entry with flake-parts -├── nix/ -│ └── devenv.nix # Devenv options (packages, languages, etc.) -├── .stack/ -│ └── config.nix # Stack options -└── .envrc # direnv configuration -``` - -**Features:** -- Multi-platform support (Linux/Darwin, x86_64/aarch64) -- All devenv features (processes, services, languages) -- All Stack features (CLI, IDE, theme, services) -- Clean separation of concerns - ---- - -### native - -Lightweight setup using native `mkShell` instead of devenv. - -```bash -nix flake init -t github:darkmatter/stack#native -``` - -**Structure:** -``` -. -├── flake.nix # Flake with flakeModules.native -├── .stack/ -│ └── config.nix # Stack options -└── .envrc # direnv configuration -``` - -**Features:** -- Faster evaluation (no devenv dependency) -- Pure Nix implementation -- All Stack features -- No `devenv up` (use external process managers) - ---- - -### devenv - -Standalone devenv setup without a flake.nix. - -```bash -nix flake init -t github:darkmatter/stack#devenv -``` - -**Structure:** -``` -. -├── devenv.yaml # Devenv inputs and imports -├── devenv.nix # Devenv + Stack options -├── .stack/ -│ └── config.nix # Stack options -└── .envrc # direnv configuration -``` - -**Usage:** -```bash -devenv shell # Enter the environment -devenv up # Start processes -``` - ---- - -### minimal - -Traditional flake.nix without flake-parts. - -```bash -nix flake init -t github:darkmatter/stack#minimal -``` - -**Structure:** -``` -. -├── flake.nix # Standard Nix flake -├── .stack/ -│ └── config.nix # Stack options -└── .envrc # direnv configuration -``` - -**Features:** -- Simple, no abstraction layers -- Standard `forAllSystems` pattern -- Full control over flake outputs - -## Configuration - -All templates use the same `.stack/config.nix` structure: - -```nix -{ - enable = true; - - # Shell prompt theme - theme.enable = true; - - # VS Code integration - ide.vscode.enable = true; - - # Global services - globalServices = { - postgres.enable = true; - redis.enable = true; - }; -} -``` - -## Learn More - -- [Stack Documentation](https://stack.dev/docs) -- [Quick Start Guide](https://stack.dev/docs/quick-start) -- [devenv Documentation](https://devenv.sh) -- [Flake Parts](https://flake.parts) diff --git a/nix/internal/flake/templates/_test-fixtures/README.md b/nix/internal/flake/templates/_test-fixtures/README.md deleted file mode 100644 index 2fff4294..00000000 --- a/nix/internal/flake/templates/_test-fixtures/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Test Fixtures - -These fixtures are used for testing stack modules in CI. They are **not** meant for `nix flake init`. - -## Notes - -- Fixtures use SSH URLs (`git+ssh://git@github.com/darkmatter/stack`) which require SSH access to the repo -- Initial runs are slow due to fetching nixpkgs, flake-parts, etc. Subsequent runs use cached inputs -- Use `--no-write-lock-file` to avoid modifying the fixture's lock file -- Override `stack` input with local path for development: `--override-input stack path:.` - -## Scenarios - -| Fixture | Description | Tests | -|---------|-------------|-------| -| `basic` | Minimal config, no apps | Core evaluation, basic options | -| `with-oxlint` | OxLint enabled with web app | Linting module, file generation, scripts | -| `full-stack` | Multiple apps (web, server, docs) | Multi-app handling, all modules enabled | -| `external-module` | Test external module | Module loading from flake input | - -## Usage in CI - -### Testing stack itself - -```bash -# Using the test script (recommended) -./nix/flake/templates/_test-fixtures/run-tests.sh - -# Run specific fixtures -./nix/flake/templates/_test-fixtures/run-tests.sh basic with-oxlint - -# Manual: Run all fixture checks -for fixture in nix/flake/templates/_test-fixtures/*/; do - echo "Testing $(basename $fixture)..." - nix flake check "$fixture" --override-input stack path:. --no-write-lock-file -done -``` - -### Testing an external module - -Fixtures are exposed as flake templates for easy access: - -```bash -# Initialize a test fixture -nix flake init -t github:darkmatter/stack#test-external-module - -# Override the module input with your module -nix flake lock --override-input test-module path:../my-module - -# Run checks -nix flake check -``` - -### Available templates - -| Template | Description | -|----------|-------------| -| `test-basic` | Minimal config, no apps | -| `test-with-oxlint` | OxLint module enabled | -| `test-full-stack` | All features (multiple apps, modules) | -| `test-external-module` | For testing external modules | - -```bash -# List all available templates -nix flake show github:darkmatter/stack --json | jq '.templates | keys' -``` - -## Creating New Fixtures - -1. Copy an existing fixture as a starting point -2. Modify `.stack/config.nix` for your scenario -3. Add any test apps in `apps/` if needed -4. Add test checks in `flake.nix` if needed - -## Fixture Structure - -``` -fixture-name/ -├── flake.nix # Flake with stack + test checks -├── flake.lock # Optional: lock file for reproducibility -├── .stack/ -│ └── config.nix # Scenario configuration -└── apps/ # Optional: test apps - └── web/ - ├── src/ - │ └── index.ts - └── package.json -``` diff --git a/nix/internal/flake/templates/_test-fixtures/basic/.stack/config.nix b/nix/internal/flake/templates/_test-fixtures/basic/.stack/config.nix deleted file mode 100644 index dedbb993..00000000 --- a/nix/internal/flake/templates/_test-fixtures/basic/.stack/config.nix +++ /dev/null @@ -1,12 +0,0 @@ -# Test Fixture: Basic -# Minimal configuration with no apps -{ - enable = true; - name = "test-basic"; - - # Disable optional features for minimal test - cli.enable = false; - theme.enable = false; - ide.enable = false; - motd.enable = false; -} diff --git a/nix/internal/flake/templates/_test-fixtures/external-module/.stack/config.nix b/nix/internal/flake/templates/_test-fixtures/external-module/.stack/config.nix deleted file mode 100644 index e20ecbed..00000000 --- a/nix/internal/flake/templates/_test-fixtures/external-module/.stack/config.nix +++ /dev/null @@ -1,15 +0,0 @@ -# Test Fixture: External Module -# Basic config for testing external module integration -{ - enable = true; - name = "test-external-module"; - - cli.enable = false; - theme.enable = false; - ide.enable = false; - motd.enable = false; - - # External modules would add their config here - # Example: - # modules.my-external-module.enable = true; -} diff --git a/nix/internal/flake/templates/_test-fixtures/full-stack/.stack/config.nix b/nix/internal/flake/templates/_test-fixtures/full-stack/.stack/config.nix deleted file mode 100644 index 7edaaa40..00000000 --- a/nix/internal/flake/templates/_test-fixtures/full-stack/.stack/config.nix +++ /dev/null @@ -1,57 +0,0 @@ -# Test Fixture: Full Stack -# Tests all features enabled -{ - enable = true; - name = "test-full-stack"; - - # Disable features that require runtime (for CI testing) - cli.enable = false; - theme.enable = false; - ide.enable = false; - motd.enable = false; - - # ===================================================== - # Multiple apps with different configurations - # ===================================================== - apps = { - # Web app with React/TypeScript - web = { - path = "apps/web"; - linting.oxlint = { - enable = true; - plugins = [ - "react" - "typescript" - ]; - rules = { - "no-console" = "warn"; - "no-debugger" = "error"; - }; - paths = [ "src" ]; - }; - }; - - # Server app with Node.js - server = { - path = "apps/server"; - linting.oxlint = { - enable = true; - plugins = [ "typescript" ]; - rules = { - "no-console" = "off"; # Allow console in server - }; - paths = [ "src" ]; - }; - }; - - # Documentation app - docs = { - path = "apps/docs"; - linting.oxlint = { - enable = true; - plugins = [ "typescript" ]; - paths = [ "src" ]; - }; - }; - }; -} diff --git a/nix/internal/flake/templates/_test-fixtures/update-golden.sh b/nix/internal/flake/templates/_test-fixtures/update-golden.sh deleted file mode 100755 index 61377530..00000000 --- a/nix/internal/flake/templates/_test-fixtures/update-golden.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================== -# update-golden.sh - Update golden snapshot files for test fixtures -# -# Usage: -# ./update-golden.sh # Update all fixtures -# ./update-golden.sh basic with-oxlint # Update specific fixtures -# STACKPANEL_PATH=/path/to/stackpanel ./update-golden.sh # Override stackpanel input -# -# This script builds the snapshot derivation for each fixture and copies -# the result into the fixture's golden/ directory. The golden files are -# checked into git and used by `nix flake check` to detect regressions. -# ============================================================================== -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -STACKPANEL_PATH="${STACKPANEL_PATH:-$(cd "$SCRIPT_DIR/../../../.." && pwd)}" - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' -NC='\033[0m' # No Color - -echo "==========================================" -echo "Update Golden Snapshot Files" -echo "==========================================" -echo "Stackpanel path: $STACKPANEL_PATH" -echo "" - -# Get list of fixtures to update -if [ $# -gt 0 ]; then - FIXTURES=("$@") -else - FIXTURES=() - for dir in "$SCRIPT_DIR"/*/; do - if [ -f "$dir/flake.nix" ]; then - FIXTURES+=("$(basename "$dir")") - fi - done -fi - -echo "Fixtures to update: ${FIXTURES[*]}" -echo "" - -UPDATED=0 -SKIPPED=0 -FAILED=0 -FAILED_FIXTURES=() - -for fixture in "${FIXTURES[@]}"; do - fixture_dir="$SCRIPT_DIR/$fixture" - - if [ ! -d "$fixture_dir" ]; then - echo -e "${YELLOW}⚠ Fixture '$fixture' not found, skipping${NC}" - ((SKIPPED++)) - continue - fi - - if [ ! -f "$fixture_dir/flake.nix" ]; then - echo -e "${YELLOW}⚠ Fixture '$fixture' has no flake.nix, skipping${NC}" - ((SKIPPED++)) - continue - fi - - echo "----------------------------------------" - echo -e "${CYAN}Building snapshot: $fixture${NC}" - echo "----------------------------------------" - - link="$fixture_dir/.snapshot-result" - - # Build the snapshot derivation - if ! nix build "$fixture_dir#snapshot" \ - --override-input stackpanel "path:$STACKPANEL_PATH" \ - --no-write-lock-file \ - --out-link "$link" \ - 2>&1; then - echo -e "${RED}✗ Failed to build snapshot for $fixture${NC}" - FAILED_FIXTURES+=("$fixture") - ((FAILED++)) - continue - fi - - # Replace golden/ with snapshot contents - rm -rf "$fixture_dir/golden" - cp -rL "$link" "$fixture_dir/golden" - chmod -R u+w "$fixture_dir/golden" - rm -f "$link" - - # Show what was generated - file_count=$(find "$fixture_dir/golden" -type f | wc -l | tr -d ' ') - echo -e "${GREEN}✓ Updated golden/ for $fixture ($file_count files)${NC}" - find "$fixture_dir/golden" -type f | sort | sed 's|.*/golden/| |' - echo "" - ((UPDATED++)) -done - -echo "==========================================" -echo "Results" -echo "==========================================" -echo -e "${GREEN}Updated: $UPDATED${NC}" -if [ $SKIPPED -gt 0 ]; then - echo -e "${YELLOW}Skipped: $SKIPPED${NC}" -fi -echo -e "${RED}Failed: $FAILED${NC}" - -if [ $FAILED -gt 0 ]; then - echo "" - echo "Failed fixtures:" - for f in "${FAILED_FIXTURES[@]}"; do - echo " - $f" - done - exit 1 -fi - -echo "" -echo -e "${GREEN}Done! Remember to commit the updated golden/ directories.${NC}" diff --git a/nix/internal/flake/templates/_test-fixtures/with-oxlint/.stack/config.nix b/nix/internal/flake/templates/_test-fixtures/with-oxlint/.stack/config.nix deleted file mode 100644 index c48def19..00000000 --- a/nix/internal/flake/templates/_test-fixtures/with-oxlint/.stack/config.nix +++ /dev/null @@ -1,27 +0,0 @@ -# Test Fixture: With OxLint -# Tests oxlint module with a web app -{ - enable = true; - name = "test-with-oxlint"; - - cli.enable = false; - theme.enable = false; - ide.enable = false; - motd.enable = false; - - # Define an app with oxlint enabled - apps.web = { - path = "apps/web"; - linting.oxlint = { - enable = true; - plugins = [ - "react" - "typescript" - ]; - rules = { - "no-console" = "warn"; - }; - paths = [ "src" ]; - }; - }; -} diff --git a/nix/internal/flake/templates/default/.envrc b/nix/internal/flake/templates/default/.envrc deleted file mode 100644 index 63e8d598..00000000 --- a/nix/internal/flake/templates/default/.envrc +++ /dev/null @@ -1,40 +0,0 @@ -# -*- mode: sh -*- -# shellcheck shell=bash -GIT_ROOT=$(git rev-parse --show-toplevel || $PWD) -NIX_DIRENV_URL="https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.0/direnvrc" -NIX_DIRENV_SHA="sha256-yMJ2OVMzrFaDPn7q8nCBZFRYpL/f0RcHzhmw/i6btJM=" -NIX_DIRENV_FALLBACK_NIX=/nix/var/nix/profiles/default/bin/nix - -if [[ -n "${__STACKPANEL_CLEAN_ENV+x}" ]]; then - exit 0 -fi - -# ---------------------------------------------------------------------------- -# Hack to be able to read the absolute path of the project root -# without having to use --impure -# ---------------------------------------------------------------------------- -echo "$GIT_ROOT" > "$GIT_ROOT/.stackpanel-root" - -# ---------------------------------------------------------------------------- -# nix-direnv: direnv with better caching for nix -# ---------------------------------------------------------------------------- -if ! has nix_direnv_version || ! nix_direnv_version 3.1.0; then - source_url "$NIX_DIRENV_URL" "$NIX_DIRENV_SHA" -fi - -# ---------------------------------------------------------------------------- -# Export STACKPANEL_ROOT so _internal.nix can find config.local.nix -# ---------------------------------------------------------------------------- -export STACKPANEL_ROOT="$GIT_ROOT" - -# ---------------------------------------------------------------------------- -# The devshell entrypoint is generated on shell entry, so we need to check for -# it and have a backup plan. -# ---------------------------------------------------------------------------- -if [[ -x "$GIT_ROOT/devshell" ]]; then - echo "Using stackpanel devshell" >&2 - eval "$("$GIT_ROOT/devshell" --direnv)" -else - echo "Using flake devshell" >&2 - use flake "$GIT_ROOT" --impure -fi diff --git a/nix/internal/flake/templates/default/.gitignore b/nix/internal/flake/templates/default/.gitignore deleted file mode 100644 index bd75cb44..00000000 --- a/nix/internal/flake/templates/default/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Nix -result -result-* - -# direnv -.direnv/ - -# stackpanel state (regenerated on shell entry) -.stackpanel-root -.stack/profile/ -.stack/keys/ -.stack/config.local.nix -.stack/gen/ -.stack/state/ - -# devenv -.devenv/ -.devenv.flake.nix - -# OS -.DS_Store -Thumbs.db - -# Editor -*.swp -*.swo -*~ -.idea/ -.vscode/* -!.vscode/settings.json -!.vscode/extensions.json diff --git a/nix/internal/flake/templates/default/.stack/config.nix b/nix/internal/flake/templates/default/.stack/config.nix deleted file mode 100644 index ccc3cfce..00000000 --- a/nix/internal/flake/templates/default/.stack/config.nix +++ /dev/null @@ -1,187 +0,0 @@ -# ============================================================================== -# config.nix -# -# Stackpanel project configuration. -# Edit this file to configure your project. -# ============================================================================== -{ - enable = true; - name = "my-project"; - github = "owner/repo"; - # debug = false; - - # --------------------------------------------------------------------------- - # CLI - Stackpanel command-line tools - # --------------------------------------------------------------------------- - cli.enable = true; - - # --------------------------------------------------------------------------- - # Theme - Starship prompt with stackpanel styling - # See: https://stackpanel.dev/docs/theme - # --------------------------------------------------------------------------- - theme.enable = true; - # theme = { - # name = "default"; - # nerd-font = true; - # minimal = false; - # - # colors = { - # primary = "#7aa2f7"; - # secondary = "#bb9af7"; - # success = "#9ece6a"; - # warning = "#e0af68"; - # error = "#f7768e"; - # muted = "#565f89"; - # }; - # - # starship = { - # add-newline = true; - # scan-timeout = 30; - # command-timeout = 500; - # }; - # }; - - # --------------------------------------------------------------------------- - # IDE Integration - Auto-generate editor config files - # --------------------------------------------------------------------------- - ide.enable = true; - ide.vscode.enable = true; - - # --------------------------------------------------------------------------- - # MOTD - Message of the day shown on shell entry - # --------------------------------------------------------------------------- - motd.enable = true; - motd.commands = [ - { - name = "dev"; - description = "Start development server"; - } - { - name = "build"; - description = "Build the project"; - } - ]; - - # --------------------------------------------------------------------------- - # Users - Team members with project access - # Add overrides or additional users here. - # See: https://stackpanel.dev/docs/users - # --------------------------------------------------------------------------- - # users = { - # johndoe = { - # name = "John Doe"; - # github = "johndoe"; - # email = "john@example.com"; - # }; - # }; - - # --------------------------------------------------------------------------- - # AWS - AWS Roles Anywhere for certificate-based authentication - # See: https://stackpanel.dev/docs/aws - # --------------------------------------------------------------------------- - # aws = { - # roles-anywhere = { - # enable = true; - # region = "us-east-1"; - # account-id = "123456789012"; - # role-name = "DeveloperRole"; - # trust-anchor-arn = "arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/..."; - # profile-arn = "arn:aws:rolesanywhere:us-east-1:123456789012:profile/..."; - # cache-buffer-seconds = "300"; - # prompt-on-shell = true; - # }; - # }; - - # --------------------------------------------------------------------------- - # Step CA - Internal certificate management for local HTTPS - # See: https://stackpanel.dev/docs/step-ca - # --------------------------------------------------------------------------- - # step-ca = { - # enable = true; - # ca-url = "https://ca.internal:443"; - # ca-fingerprint = "abc123..."; # Root CA fingerprint for verification - # provisioner = "admin"; - # cert-name = "dev-workstation"; - # prompt-on-shell = true; - # }; - - # --------------------------------------------------------------------------- - # Secrets - SOPS-based secrets management with AGE encryption - # See: https://stackpanel.dev/docs/secrets - # - # On first shell entry with secrets enabled: - # - A local AGE key is auto-generated in .stack/keys/ - # - keys/.sops.yaml is configured to encrypt group keys to your local key - # - # To set up a secrets group: - # secrets:init-group dev # generates AGE keypair, encrypts to .enc.age - # # Then add the public key to config.nix: - # # secrets.groups.dev.age-pub = "age1..."; - # - # No AWS/KMS required by default. Add KMS later for team/CI access. - # --------------------------------------------------------------------------- - # secrets = { - # enable = true; - # secrets-dir = ".stack/secrets"; - # - # # Groups define access control boundaries - # # Each group has its own AGE keypair - # # groups = { - # # dev = {}; # Initialize with: secrets:init-group dev - # # prod = {}; # Initialize with: secrets:init-group prod - # # }; - # - # # Code generation for type-safe env access - # # codegen = { - # # typescript = { - # # name = "env"; - # # directory = "packages/gen/env/src"; - # # language = "CODEGEN_LANGUAGE_TYPESCRIPT"; - # # }; - # # }; - # }; - - # --------------------------------------------------------------------------- - # SST - Infrastructure as code configuration - # See: https://stackpanel.dev/docs/sst - # --------------------------------------------------------------------------- - # sst = { - # enable = true; - # project-name = "my-project"; - # region = "us-west-2"; - # account-id = "123456789012"; - # config-path = "packages/infra/sst.config.ts"; - # - # kms = { - # enable = true; - # alias = "my-project-secrets"; - # }; - # - # oidc = { - # provider = "github-actions"; - # github-actions = { - # org = "my-org"; - # repo = "*"; - # }; - # }; - # }; - - # --------------------------------------------------------------------------- - # Global Services - Shared development services - # --------------------------------------------------------------------------- - # globalServices = { - # enable = true; - # project-name = "myproject"; - # postgres.enable = true; - # redis.enable = true; - # minio.enable = true; - # }; - - # --------------------------------------------------------------------------- - # Caddy - Local HTTPS reverse proxy - # --------------------------------------------------------------------------- - # caddy = { - # enable = true; - # project-name = "myproject"; - # }; -} diff --git a/nix/internal/flake/templates/default/.stack/data.nix b/nix/internal/flake/templates/default/.stack/data.nix deleted file mode 100644 index d7231267..00000000 --- a/nix/internal/flake/templates/default/.stack/data.nix +++ /dev/null @@ -1,10 +0,0 @@ -# ============================================================================== -# data.nix - Agent-editable data -# -# This file is written by the Stackpanel agent. Do not edit manually. -# User configuration should go in config.nix instead. -# -# The agent patches values at specific paths (e.g., deployment.fly.organization) -# This file is merged with config.nix (config.nix takes precedence) -# ============================================================================== -{ } diff --git a/nix/internal/flake/templates/default/README.md b/nix/internal/flake/templates/default/README.md deleted file mode 100644 index dd5d3b21..00000000 --- a/nix/internal/flake/templates/default/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# My Project - -Powered by [Stack](https://stack.dev) - reproducible dev environments without the complexity. - -## Quick Start - -### Prerequisites - -- [Nix](https://nixos.org/download.html) with flakes enabled ([Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer) recommended) -- [direnv](https://direnv.net/) (optional but recommended) - -### Enter the Dev Environment - -```bash -# With direnv (recommended) -direnv allow - -# Or manually -nix develop --impure -``` - -You'll see a welcome message with available commands when the shell activates. - -### Start Development - -```bash -# Start all services and processes -dev - -# Or run individual commands -bun run dev -``` - -## Configuration - -### Stack Config - -Edit `.stack/config.nix` to configure your environment: - -```nix -{ - enable = true; - - # Shell prompt theme - theme.enable = true; - - # VS Code integration - ide.vscode.enable = true; - - # Enable services - globalServices = { - postgres.enable = true; - redis.enable = true; - }; -} -``` - -### Devenv Options - -Edit `nix/devenv.nix` for packages, languages, and processes: - -```nix -{ pkgs }: { - packages = with pkgs; [ - nodejs - bun - ]; - - languages.typescript.enable = true; - - env = { - NODE_ENV = "development"; - }; -} -``` - -## Commands - -| Command | Description | -|---------|-------------| -| `dev` | Start development (all processes) | -| `direnv allow` | Activate the dev environment | -| `stack status` | Check service status | -| `stack services start` | Start background services | -| `nix flake update` | Update dependencies | - -## Project Structure - -``` -. -├── .stack/ # Stack configuration -│ ├── config.nix # Main config -│ ├── profile/ # Runtime state (gitignored) -│ └── gen/ # Generated files (gitignored) -├── nix/ -│ └── devenv.nix # Devenv options -├── flake.nix # Nix flake entrypoint -└── .envrc # direnv integration -``` - -## Learn More - -- [Stack Documentation](https://stack.dev/docs) -- [Quick Start Guide](https://stack.dev/docs/quick-start) -- [Configuration Reference](https://stack.dev/docs/reference) diff --git a/nix/internal/flake/templates/devenv/.envrc b/nix/internal/flake/templates/devenv/.envrc deleted file mode 100644 index bb13dae3..00000000 --- a/nix/internal/flake/templates/devenv/.envrc +++ /dev/null @@ -1 +0,0 @@ -use devenv diff --git a/nix/internal/flake/templates/minimal/.envrc b/nix/internal/flake/templates/minimal/.envrc deleted file mode 100644 index 652cf48d..00000000 --- a/nix/internal/flake/templates/minimal/.envrc +++ /dev/null @@ -1,2 +0,0 @@ -# Activate the devenv shell -use flake . --impure diff --git a/nix/internal/flake/templates/minimal/.gitignore b/nix/internal/flake/templates/minimal/.gitignore deleted file mode 100644 index bd75cb44..00000000 --- a/nix/internal/flake/templates/minimal/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Nix -result -result-* - -# direnv -.direnv/ - -# stackpanel state (regenerated on shell entry) -.stackpanel-root -.stack/profile/ -.stack/keys/ -.stack/config.local.nix -.stack/gen/ -.stack/state/ - -# devenv -.devenv/ -.devenv.flake.nix - -# OS -.DS_Store -Thumbs.db - -# Editor -*.swp -*.swo -*~ -.idea/ -.vscode/* -!.vscode/settings.json -!.vscode/extensions.json diff --git a/nix/internal/flake/templates/minimal/.stack/config.nix b/nix/internal/flake/templates/minimal/.stack/config.nix deleted file mode 100644 index 6d167dbc..00000000 --- a/nix/internal/flake/templates/minimal/.stack/config.nix +++ /dev/null @@ -1,95 +0,0 @@ -# ============================================================================== -# config.nix -# -# Stackpanel project configuration. -# Edit this file to configure your project. -# ============================================================================== -{ - enable = true; - name = "my-project"; - github = "owner/repo"; - # debug = false; - - # --------------------------------------------------------------------------- - # CLI - Stackpanel command-line tools - # --------------------------------------------------------------------------- - cli.enable = true; - - # --------------------------------------------------------------------------- - # Theme - Starship prompt with stackpanel styling - # --------------------------------------------------------------------------- - theme.enable = true; - - # --------------------------------------------------------------------------- - # IDE Integration - Auto-generate editor config files - # --------------------------------------------------------------------------- - ide.enable = true; - ide.vscode.enable = true; - - # --------------------------------------------------------------------------- - # MOTD - Message of the day shown on shell entry - # --------------------------------------------------------------------------- - motd.enable = true; - motd.commands = [ - { - name = "dev"; - description = "Start development server"; - } - { - name = "build"; - description = "Build the project"; - } - ]; - - # --------------------------------------------------------------------------- - # Users - # Add overrides or additional users here. - # --------------------------------------------------------------------------- - # users = { - # example = { - # name = "Example User"; - # github = "example"; - # public-keys = []; - # }; - # }; - - # --------------------------------------------------------------------------- - # AWS Certificate Auth - Passwordless AWS access via Roles Anywhere - # --------------------------------------------------------------------------- - # aws.roles-anywhere = { - # enable = true; - # region = "us-west-2"; - # account-id = "123456789012"; - # role-name = "dev-role"; - # trust-anchor-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:trust-anchor/..."; - # profile-arn = "arn:aws:rolesanywhere:us-west-2:123456789012:profile/..."; - # }; - - # --------------------------------------------------------------------------- - # Step CA - Internal certificate management - # --------------------------------------------------------------------------- - # step-ca = { - # enable = true; - # ca-url = "https://ca.internal:443"; - # ca-fingerprint = "your-fingerprint-here"; - # }; - - # --------------------------------------------------------------------------- - # Global Services - Shared development services - # --------------------------------------------------------------------------- - # globalServices = { - # enable = true; - # project-name = "myproject"; - # postgres.enable = true; - # redis.enable = true; - # minio.enable = true; - # }; - - # --------------------------------------------------------------------------- - # Caddy - Local HTTPS reverse proxy - # --------------------------------------------------------------------------- - # caddy = { - # enable = true; - # project-name = "myproject"; - # }; -} diff --git a/nix/internal/flake/templates/minimal/.stack/data.nix b/nix/internal/flake/templates/minimal/.stack/data.nix deleted file mode 100644 index d7231267..00000000 --- a/nix/internal/flake/templates/minimal/.stack/data.nix +++ /dev/null @@ -1,10 +0,0 @@ -# ============================================================================== -# data.nix - Agent-editable data -# -# This file is written by the Stackpanel agent. Do not edit manually. -# User configuration should go in config.nix instead. -# -# The agent patches values at specific paths (e.g., deployment.fly.organization) -# This file is merged with config.nix (config.nix takes precedence) -# ============================================================================== -{ } diff --git a/nix/internal/flake/templates/minimal/README.md b/nix/internal/flake/templates/minimal/README.md deleted file mode 100644 index d1b4578d..00000000 --- a/nix/internal/flake/templates/minimal/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# My Project - -Powered by [Stack](https://stack.dev) - reproducible dev environments without the complexity. - -> This is the **minimal** template - a bare Nix flake without flake-parts. - -## Quick Start - -### Prerequisites - -- [Nix](https://nixos.org/download.html) with flakes enabled ([Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer) recommended) -- [direnv](https://direnv.net/) (optional but recommended) - -### Enter the Dev Environment - -```bash -# With direnv (recommended) -direnv allow - -# Or manually -nix develop --impure -``` - -### Start Development - -```bash -# Start all processes -devenv up - -# Or run individual commands -bun run dev -``` - -## Project Structure - -``` -. -├── flake.nix # Nix flake (main entry point) -├── flake.lock # Locked dependencies (auto-generated) -├── .stack/ -│ └── config.nix # Stack options -└── .envrc # direnv configuration -``` - -## Configuration - -### Stack Config - -Edit `.stack/config.nix`: - -```nix -{ - enable = true; - - # Shell prompt theme - theme.enable = true; - - # VS Code integration - ide.vscode.enable = true; - - # Enable services - globalServices = { - postgres.enable = true; - redis.enable = true; - }; -} -``` - -### Devenv Options - -Edit `flake.nix` directly: - -```nix -{ - packages = [ pkgs.nodejs pkgs.bun ]; - languages.typescript.enable = true; - env.DATABASE_URL = "postgres://localhost:5432/myapp"; -} -``` - -## Commands - -| Command | Description | -|---------|-------------| -| `direnv allow` | Activate the dev environment | -| `devenv up` | Start all processes | -| `nix develop --impure` | Enter shell manually | -| `nix flake update` | Update dependencies | - -## Why Minimal? - -This template uses a standard Nix flake without [flake-parts](https://flake.parts/). - -**Choose this if:** -- You prefer explicit over magical -- You don't need flake-parts features -- You want a simpler flake structure - -**Choose the default template if:** -- You want better modularity -- You need perSystem helpers -- You're building a complex project - -## Learn More - -- [Stack Documentation](https://stack.dev/docs) -- [Quick Start Guide](https://stack.dev/docs/quick-start) -- [devenv Documentation](https://devenv.sh) diff --git a/nix/stackpanel/core/README.md b/nix/stackpanel/core/README.md index 4fc21273..a703e2d0 100644 --- a/nix/stackpanel/core/README.md +++ b/nix/stackpanel/core/README.md @@ -13,7 +13,7 @@ Feature-specific options live in their own directories (`apps/`, `services/`, `n | File | Description | |------|-------------| | `default.nix` | Entry point — imports all core modules, sets up hooks | -| `options-core.nix` | Root options: enable, name, root, dirs, direnv, git-hooks, checks | +| `options/core.nix` | Root options: enable, name, root, dirs, direnv, git-hooks, checks | | `outputs.nix` | Flake output derivations and apps | | `checks.nix` | Module check schema for `nix flake check` | | `motd.nix` | Message of the Day configuration | diff --git a/nix/stackpanel/core/lib/envvars.nix b/nix/stackpanel/core/lib/envvars.nix deleted file mode 100644 index f2d9ca33..00000000 --- a/nix/stackpanel/core/lib/envvars.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib ? import , -}: -let - core = import ./envvars/helpers.nix { inherit lib; }; - sections = import ./envvars/sections.nix { - inherit (core) mkEnvVar; - inherit (core) categories; - }; -in -core -// sections -// import ./envvars/derived.nix { - inherit lib; - inherit (core) categories; - inherit sections; -} diff --git a/nix/stackpanel/core/lib/envvars/derived.nix b/nix/stackpanel/core/lib/envvars/derived.nix deleted file mode 100644 index f3652db9..00000000 --- a/nix/stackpanel/core/lib/envvars/derived.nix +++ /dev/null @@ -1,138 +0,0 @@ -{ - lib, - categories, - sections, -}: -let - allVariables = lib.flatten [ - (lib.attrValues sections.core) - (lib.attrValues sections.paths) - (lib.attrValues sections.agent) - (lib.attrValues sections.stepca) - (lib.attrValues sections.aws) - (lib.attrValues sections.minio) - (lib.attrValues sections.services) - (lib.attrValues sections.ide) - ]; - - # Get all required variables - requiredVariables = lib.filter (v: v.required) allVariables; - - # Get variables by category - byCategory = category: lib.filter (v: v.category == category) allVariables; - - # Get variables by source - bySource = source: lib.filter (v: v.source == source) allVariables; - - mkValidationScript = - { - pkgs, - strict ? false, - }: - pkgs.writeShellScriptBin "stackpanel-validate-env" '' - #!/usr/bin/env bash - set -euo pipefail - - ERRORS=0 - WARNINGS=0 - - check_required() { - local var_name="$1" - local description="$2" - if [[ -z "''${!var_name:-}" ]]; then - echo "❌ MISSING: $var_name - $description" - ((ERRORS++)) || true - else - echo "✓ $var_name" - fi - } - - echo "Validating Stackpanel environment variables..." - echo "" - - ${lib.concatMapStringsSep "\n" (v: '' - check_required "${v.name}" "${v.description}" - '') requiredVariables} - - echo "" - if [[ $ERRORS -gt 0 ]]; then - echo "Found $ERRORS missing required variables" - ${if strict then "exit 1" else "exit 0"} - else - echo "All required variables are set!" - fi - ''; - - # Generate markdown documentation - mkDocumentation = - let - renderVar = v: '' - ### `${v.name}` - - ${v.description} - - | Property | Value | - |----------|-------| - | Category | ${v.category} | - | Source | ${v.source} | - | Required | ${if v.required then "Yes" else "No"} | - ${lib.optionalString (v.default != null) "| Default | `${v.default}` |"} - ${lib.optionalString (v.example != null) "| Example | `${v.example}` |"} - ${lib.optionalString (v.goField != null) "| Go Field | `${v.goField}` |"} - ${lib.optionalString v.deprecated "| ⚠️ Deprecated | ${ - v.deprecationMessage or "This variable is deprecated" - } |"} - ''; - - renderCategory = name: vars: '' - ## ${name} - - ${lib.concatMapStringsSep "\n---\n" renderVar vars} - ''; - in - '' - # Stackpanel Environment Variables Reference - - This document is auto-generated from `nix/stackpanel/core/lib/envvars.nix`. - - ${lib.concatStringsSep "\n" ( - lib.mapAttrsToList (name: _: renderCategory name (byCategory name)) categories - )} - ''; - - # Generate Go constants file content - mkGoConstants = '' - // Code generated by envvars.nix. DO NOT EDIT. - package envvars - - // Environment variable names used by Stackpanel - const ( - ${lib.concatMapStringsSep "\n" (v: '' - // ${v.description} - ${lib.toUpper (lib.replaceStrings [ "-" " " ] [ "_" "_" ] v.name)} = "${v.name}" - '') allVariables} - ) - - // Categories groups environment variables by their purpose - var Categories = map[string][]string{ - ${lib.concatMapStringsSep "\n" (cat: '' - "${cat}": { - ${lib.concatMapStringsSep ",\n" ( - v: "${lib.toUpper (lib.replaceStrings [ "-" " " ] [ "_" "_" ] v.name)}" - ) (byCategory cat)} - }, - '') (lib.attrValues categories)} - }; - ''; -in -{ - inherit - allVariables - requiredVariables - byCategory - bySource - mkValidationScript - mkDocumentation - mkGoConstants - ; -} diff --git a/nix/stackpanel/core/lib/envvars/helpers.nix b/nix/stackpanel/core/lib/envvars/helpers.nix deleted file mode 100644 index b826ae97..00000000 --- a/nix/stackpanel/core/lib/envvars/helpers.nix +++ /dev/null @@ -1,46 +0,0 @@ -_: -let - # Helper to create an env var definition - mkEnvVar = - { - name, - description, - category, - source ? "nix", - required ? false, - default ? null, - example ? null, - deprecated ? false, - deprecationMessage ? null, - goField ? null, # Corresponding Go struct field name - }: - { - inherit - name - description - category - source - required - default - example - deprecated - deprecationMessage - goField - ; - }; - - # Categories for organizing variables - categories = { - core = "Core Stackpanel"; - paths = "Paths & Directories"; - agent = "Stackpanel Agent"; - stepca = "Step CA (Certificates)"; - aws = "AWS & Roles Anywhere"; - minio = "MinIO (S3-Compatible Storage)"; - services = "Services Config"; - ide = "IDE Integration"; - }; -in -{ - inherit mkEnvVar categories; -} diff --git a/nix/stackpanel/core/lib/envvars/sections.nix b/nix/stackpanel/core/lib/envvars/sections.nix deleted file mode 100644 index 455302d9..00000000 --- a/nix/stackpanel/core/lib/envvars/sections.nix +++ /dev/null @@ -1,345 +0,0 @@ -{ - mkEnvVar, - categories, -}: -rec { - # Core Stackpanel Variables - core = { - STACKPANEL_ROOT = mkEnvVar { - name = "STACKPANEL_ROOT"; - description = "Absolute path to the project root directory"; - category = categories.core; - source = "nix"; - required = true; - example = "/home/user/my-project"; - goField = "ProjectRoot"; - }; - - STACKPANEL_ROOT_MARKER = mkEnvVar { - name = "STACKPANEL_ROOT_MARKER"; - description = "Filename used as a marker to identify project root"; - category = categories.core; - source = "nix"; - default = ".stackpanel-root"; - goField = "RootMarker"; - }; - - STACKPANEL_ROOT_DIR_NAME = mkEnvVar { - name = "STACKPANEL_ROOT_DIR_NAME"; - description = "Name of the .stackpanel directory within the project"; - category = categories.core; - source = "nix"; - default = ".stack"; - }; - - STACKPANEL_SHELL_ID = mkEnvVar { - name = "STACKPANEL_SHELL_ID"; - description = "Unique identifier for the current shell session"; - category = categories.core; - source = "nix"; - default = "1"; - }; - - STACKPANEL_NIX_CONFIG = mkEnvVar { - name = "STACKPANEL_NIX_CONFIG"; - description = "Path to the source Nix config file (.stack/config.nix)"; - category = categories.core; - source = "nix"; - example = "/home/user/my-project/.stack/config.nix"; - goField = "NixConfigPath"; - }; - - STACKPANEL_CONFIG_JSON = mkEnvVar { - name = "STACKPANEL_CONFIG_JSON"; - description = "Path to the Nix-generated config JSON in the Nix store (for Go CLI)"; - category = categories.core; - source = "nix"; - example = "/nix/store/xxx-stackpanel-config.json"; - goField = "ConfigJsonPath"; - }; - }; - - # Paths & Directories - paths = { - STACKPANEL_STATE_DIR = mkEnvVar { - name = "STACKPANEL_STATE_DIR"; - description = "Directory for runtime state (credentials, caches, etc.)"; - category = categories.paths; - source = "nix"; - required = true; - example = "/home/user/my-project/.stack/state"; - goField = "StateDir"; - }; - - STACKPANEL_STATE_FILE = mkEnvVar { - name = "STACKPANEL_STATE_FILE"; - description = "Full path to the stackpanel.json state file"; - category = categories.paths; - source = "nix"; - example = "/home/user/my-project/.stack/state/stackpanel.json"; - goField = "StateFile"; - }; - - STACKPANEL_SHELL_LOG = mkEnvVar { - name = "STACKPANEL_SHELL_LOG"; - description = "Log file capturing shell entry output"; - category = categories.paths; - source = "nix"; - example = "/home/user/my-project/.stack/state/shell.log"; - goField = "ShellLog"; - }; - - STACKPANEL_GEN_DIR = mkEnvVar { - name = "STACKPANEL_GEN_DIR"; - description = "Directory for generated files (configs, scripts)"; - category = categories.paths; - source = "nix"; - example = "/home/user/my-project/.stack/gen"; - goField = "GenDir"; - }; - - STACKPANEL_DATA_DIR = mkEnvVar { - name = "STACKPANEL_DATA_DIR"; - description = "Directory for persistent data (databases, etc.)"; - category = categories.paths; - source = "nix"; - example = "/home/user/my-project/.stack/data"; - goField = "DataDir"; - }; - }; - - # Stackpanel Agent Variables - agent = { - STACKPANEL_PROJECT_ROOT = mkEnvVar { - name = "STACKPANEL_PROJECT_ROOT"; - description = "Project root override for the agent (when spawned externally)"; - category = categories.agent; - source = "dynamic"; - goField = "ProjectRoot"; - }; - - STACKPANEL_AUTH_TOKEN = mkEnvVar { - name = "STACKPANEL_AUTH_TOKEN"; - description = "Authentication token for the agent API"; - category = categories.agent; - source = "dynamic"; - goField = "AuthToken"; - }; - - STACKPANEL_API_ENDPOINT = mkEnvVar { - name = "STACKPANEL_API_ENDPOINT"; - description = "API endpoint URL for the agent"; - category = categories.agent; - source = "dynamic"; - default = "http://localhost:6401"; - goField = "APIEndpoint"; - }; - - STACKPANEL_USER_CONFIG = mkEnvVar { - name = "STACKPANEL_USER_CONFIG"; - description = "Path to user-level config file (stores cross-repo data like projects list)"; - category = categories.agent; - source = "dynamic"; - default = "~/.config/stackpanel/stackpanel.yaml"; - example = "/home/user/.config/stackpanel/stackpanel.yaml"; - goField = "UserConfigPath"; - }; - }; - - # Step CA (Certificate Authority) - stepca = { - STEP_CA_URL = mkEnvVar { - name = "STEP_CA_URL"; - description = "URL of the Step CA server"; - category = categories.stepca; - source = "nix"; - example = "https://ca.internal:443"; - }; - - STEP_CA_FINGERPRINT = mkEnvVar { - name = "STEP_CA_FINGERPRINT"; - description = "SHA256 fingerprint of the Step CA root certificate"; - category = categories.stepca; - source = "nix"; - example = "3996f98e09f54bdfc705bb0f022d70dc3e15230c009add60508d0593ae805d5a"; - }; - }; - - # AWS & Roles Anywhere - aws = { - AWS_TRUST_ANCHOR_ARN = mkEnvVar { - name = "AWS_TRUST_ANCHOR_ARN"; - description = "ARN of the IAM Roles Anywhere trust anchor"; - category = categories.aws; - source = "nix"; - example = "arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/abc123"; - }; - - AWS_PROFILE_ARN = mkEnvVar { - name = "AWS_PROFILE_ARN"; - description = "ARN of the IAM Roles Anywhere profile"; - category = categories.aws; - source = "nix"; - example = "arn:aws:rolesanywhere:us-east-1:123456789012:profile/def456"; - }; - - AWS_ROLE_ARN = mkEnvVar { - name = "AWS_ROLE_ARN"; - description = "ARN of the IAM role to assume via Roles Anywhere"; - category = categories.aws; - source = "nix"; - example = "arn:aws:iam::123456789012:role/DeveloperRole"; - }; - - AWS_REGION = mkEnvVar { - name = "AWS_REGION"; - description = "Default AWS region for API calls"; - category = categories.aws; - source = "nix"; - default = "us-east-1"; - }; - - AWS_ACCESS_KEY_ID = mkEnvVar { - name = "AWS_ACCESS_KEY_ID"; - description = "AWS access key ID (set dynamically by credential scripts)"; - category = categories.aws; - source = "dynamic"; - }; - - AWS_SECRET_ACCESS_KEY = mkEnvVar { - name = "AWS_SECRET_ACCESS_KEY"; - description = "AWS secret access key (set dynamically by credential scripts)"; - category = categories.aws; - source = "dynamic"; - }; - - AWS_SESSION_TOKEN = mkEnvVar { - name = "AWS_SESSION_TOKEN"; - description = "AWS session token for temporary credentials"; - category = categories.aws; - source = "dynamic"; - }; - - AWS_SHARED_CREDENTIALS_FILE = mkEnvVar { - name = "AWS_SHARED_CREDENTIALS_FILE"; - description = "Path to AWS credentials file (set to /dev/null to force Roles Anywhere)"; - category = categories.aws; - source = "nix"; - default = "/dev/null"; - }; - - AWS_CERT_PATH = mkEnvVar { - name = "AWS_CERT_PATH"; - description = "Override path to device certificate for Roles Anywhere"; - category = categories.aws; - source = "dynamic"; - }; - - AWS_KEY_PATH = mkEnvVar { - name = "AWS_KEY_PATH"; - description = "Override path to device private key for Roles Anywhere"; - category = categories.aws; - source = "dynamic"; - }; - - AWS_SIGNING_HELPER = mkEnvVar { - name = "AWS_SIGNING_HELPER"; - description = "Override path to aws_signing_helper binary"; - category = categories.aws; - source = "dynamic"; - }; - }; - - # MinIO (S3-Compatible Storage) - minio = { - MINIO_ROOT_USER = mkEnvVar { - name = "MINIO_ROOT_USER"; - description = "MinIO admin username"; - category = categories.minio; - source = "nix"; - default = "minioadmin"; - }; - - MINIO_ROOT_PASSWORD = mkEnvVar { - name = "MINIO_ROOT_PASSWORD"; - description = "MinIO admin password"; - category = categories.minio; - source = "nix"; - default = "minioadmin"; - }; - - MINIO_ENDPOINT = mkEnvVar { - name = "MINIO_ENDPOINT"; - description = "MinIO S3 endpoint URL"; - category = categories.minio; - source = "nix"; - example = "http://localhost:9000"; - }; - - MINIO_ACCESS_KEY = mkEnvVar { - name = "MINIO_ACCESS_KEY"; - description = "MinIO access key (alias for MINIO_ROOT_USER)"; - category = categories.minio; - source = "nix"; - }; - - MINIO_SECRET_KEY = mkEnvVar { - name = "MINIO_SECRET_KEY"; - description = "MinIO secret key (alias for MINIO_ROOT_PASSWORD)"; - category = categories.minio; - source = "nix"; - }; - - MINIO_CONSOLE_ADDRESS = mkEnvVar { - name = "MINIO_CONSOLE_ADDRESS"; - description = "MinIO console bind address (e.g., :9001)"; - category = categories.minio; - source = "nix"; - }; - - S3_ENDPOINT = mkEnvVar { - name = "S3_ENDPOINT"; - description = "S3-compatible endpoint URL (points to MinIO when enabled)"; - category = categories.minio; - source = "nix"; - example = "http://localhost:9000"; - }; - }; - - # Services Config - services = { - STACKPANEL_STABLE_PORT = mkEnvVar { - name = "STACKPANEL_STABLE_PORT"; - description = "Base port for the project (index 0 in the port layout)"; - category = categories.services; - source = "nix"; - example = "6400"; - }; - - STACKPANEL_SERVICES_CONFIG = mkEnvVar { - name = "STACKPANEL_SERVICES_CONFIG"; - description = "JSON array of service definitions with ports"; - category = categories.services; - source = "nix"; - example = ''[{"key":"POSTGRES","name":"PostgreSQL","port":6410}]''; - }; - }; - # IDE Integration - ide = { - STACKPANEL_VSCODE_SHELL = mkEnvVar { - name = "STACKPANEL_VSCODE_SHELL"; - description = "Marker to prevent shell recursion in VS Code (1 = inside VS Code shell)"; - category = categories.ide; - source = "nix"; - }; - - EDITOR = mkEnvVar { - name = "EDITOR"; - description = "Default text editor"; - category = categories.ide; - source = "nix"; - default = "vim"; - }; - }; - -} diff --git a/nix/stackpanel/core/lib/evalconfig.nix b/nix/stackpanel/core/lib/evalconfig.nix deleted file mode 100644 index 9a31823e..00000000 --- a/nix/stackpanel/core/lib/evalconfig.nix +++ /dev/null @@ -1,146 +0,0 @@ -# ============================================================================== -# evalconfig.nix -# -# Stackpanel configuration evaluator for Go CLI/agent integration. -# -# This file reads the Stackpanel configuration snapshot produced by the shell -# hooks and outputs it as JSON for use by the Go CLI/agent. It eliminates state -# drift by allowing tools to read live configuration directly from Nix. -# -# Configuration sources (in priority order): -# 1. configJson arg - Nix store path to JSON config (passed by caller) -# 2. stateDir arg - directory containing stackpanel.json (passed by caller) -# 3. root arg - project root, state file derived as root/.stack/state/stackpanel.json -# 4. env fallbacks - STACKPANEL_CONFIG_JSON / STACKPANEL_STATE_DIR / STACKPANEL_ROOT / PWD -# (only used when args are null, requires --impure) -# -# Preferred usage (pure — no --impure needed): -# nix eval --json -f evalconfig.nix --argstr root /path/to/project -# -# Legacy usage (impure — reads env vars): -# nix eval --impure --json -f evalconfig.nix -# -# The Go agent passes --argstr root so evaluation is pure whenever -# the project root is known. -# ============================================================================== -{ - root ? null, - configJson ? null, - stateDir ? null, -}: -let - # =========================================================================== - # Resolve effective values — prefer explicit args, fall back to env vars - # =========================================================================== - - # Project root: arg > STACKPANEL_ROOT env > PWD-based search - # @impure — legacy fallback for `nix eval --impure -f evalconfig.nix`. - # When called via `--argstr root`, none of these env reads fire. - envRoot = builtins.getEnv "STACKPANEL_ROOT"; # @impure - envPwd = builtins.getEnv "PWD"; # @impure - - findProjectRoot = - dir: - if dir == "" then - null - else if builtins.pathExists (dir + "/.stack/config.nix") then - dir - else if builtins.pathExists (dir + "/flake.nix") then - dir - else if dir == "/" || dir == "" then - null - else - findProjectRoot (dirOf dir); - - dirOf = - path: - let - parts = builtins.filter (x: x != "") (builtins.split "/" path); - parent = builtins.concatStringsSep "/" ( - builtins.genList (i: builtins.elemAt parts i) (builtins.length parts - 1) - ); - in - if builtins.length parts <= 1 then "/" else "/" + parent; - - effectiveRoot = - if root != null then - root - else if envRoot != "" then - envRoot - else - findProjectRoot envPwd; - - # Config JSON path: arg > STACKPANEL_CONFIG_JSON env - envConfigJson = builtins.getEnv "STACKPANEL_CONFIG_JSON"; # @impure (legacy fallback) - effectiveConfigJson = - if configJson != null then - configJson - else if envConfigJson != "" then - envConfigJson - else - null; - - # State dir: arg > STACKPANEL_STATE_DIR env > derived from root - envStateDir = builtins.getEnv "STACKPANEL_STATE_DIR"; # @impure (legacy fallback) - effectiveStateDir = - if stateDir != null then - stateDir - else if envStateDir != "" then - envStateDir - else - null; - - # =========================================================================== - # Priority 1: Read from config JSON (Nix store path set by shell hooks) - # =========================================================================== - configFromJson = - if effectiveConfigJson != null && builtins.pathExists effectiveConfigJson then - let - raw = builtins.fromJSON (builtins.readFile effectiveConfigJson); - # Replace $STACKPANEL_ROOT placeholder with actual value - fixProjectRoot = - config: - if config.projectRoot == "$STACKPANEL_ROOT" && effectiveRoot != null then - config // { projectRoot = effectiveRoot; } - else - config; - in - fixProjectRoot raw - else - null; - - # =========================================================================== - # Priority 2: Read from state file - # Try explicit stateDir first, then derive from root - # =========================================================================== - stateFile = - if effectiveStateDir != null then - effectiveStateDir + "/stackpanel.json" - else if effectiveRoot != null then - effectiveRoot + "/.stack/state/stackpanel.json" - else - null; - - configFromState = - if stateFile != null && builtins.pathExists stateFile then - builtins.fromJSON (builtins.readFile stateFile) - else - null; - -in -if configFromJson != null then - configFromJson -else if configFromState != null then - configFromState -else - { - error = "No stackpanel configuration found"; - hint = - if root == null then - "Pass --argstr root /path/to/project, or run nix develop from the project root" - else - "No config at ${effectiveRoot}/.stack/state/stackpanel.json — run 'stack preflight' first"; - projectRoot = effectiveRoot; - inherit stateFile; - configJsonPath = effectiveConfigJson; - } diff --git a/nix/stackpanel/core/lib/services-default.nix b/nix/stackpanel/core/lib/services-default.nix deleted file mode 100644 index 1f12c3e3..00000000 --- a/nix/stackpanel/core/lib/services-default.nix +++ /dev/null @@ -1,41 +0,0 @@ -# ============================================================================== -# default.nix -# -# Core library functions for stackpanel services. -# -# Pure logic that works with any Nix module system (flake-parts, devenv, NixOS, -# etc.). No side effects - these are pure functions callable from both flake -# and devenv adapters. -# -# Exports: -# - ports: Port computation utilities (pure, no pkgs needed) -# - globalServices: Global singleton service configuration (requires pkgs + integrations) -# -# Service implementations live under nix/stackpanel/integrations/services/. -# ============================================================================== -{ - lib, - pkgs ? null, -}: -{ - # Port computation utilities (pure, no pkgs needed) - ports = import ../../lib/ports.nix { inherit lib; }; - - # Global services configuration (requires pkgs and integration libraries) - globalServices = - if pkgs != null then - let - servicesLib = import ../../integrations/services/lib.nix { inherit pkgs lib; }; - caddyLib = import ../../integrations/services/caddy { inherit pkgs lib; }; - in - import ./global-services.nix { - inherit - pkgs - lib - servicesLib - caddyLib - ; - } - else - throw "stackpanel.core.globalServices requires pkgs to be passed"; -} diff --git a/nix/stackpanel/core/options-core.nix b/nix/stackpanel/core/options-core.nix deleted file mode 100644 index 49409024..00000000 --- a/nix/stackpanel/core/options-core.nix +++ /dev/null @@ -1,441 +0,0 @@ -# ============================================================================== -# core.nix -# -# Core Stackpanel options - fundamental configuration for all projects. -# -# Defines the base options that control Stackpanel behavior: -# - enable: Master switch for Stackpanel functionality -# - root: Absolute project root path (optional override) -# - root-marker: Filename for the root marker file (.stackpanel-root) -# - dirs: Directory configuration (home, state, gen, config) -# - direnv: Direnv integration settings -# - gitignore: Whether to auto-add root marker to .gitignore -# -# The root marker system allows tools to find the project root from any -# subdirectory by walking up the tree looking for the marker file. -# -# Directory layout: -# .stack/ (dirs.home, .stackpanel fallback) -# ├── keys/ (dirs.keys - gitignored, persistent credentials) -# ├── profile/ (dirs.profile - gitignored, ephemeral runtime/cache) -# └── gen/ (dirs.gen - gitignored, generated files) -# ============================================================================== -{ - lib, - config, - ... -}: -{ - # Base stackpanel options for devenv - options.stackpanel = { - enable = lib.mkEnableOption "Enable Stackpanel" // { - default = true; - example = true; - }; - debug = lib.mkEnableOption "Enable Stackpanel Debug Mode" // { - default = false; - example = true; - }; - - name = lib.mkOption { - description = '' - Name of your project. Used for display, generated metadata, and defaults - that need a stable human-readable project identifier. - ''; - type = lib.types.str; - default = "my-project"; - example = "acme-storefront"; - }; - - # Module requirements - what variables each enabled module needs - # This is populated by modules and serialized to config JSON for agent/UI - moduleRequirements = lib.mkOption { - description = '' - Variable requirements declared by enabled modules. - Modules add entries here to declare what environment variables they need. - The agent/UI can query this to show what's missing. - - Format: { moduleName = { requires = [ ... ]; provides = [ ... ]; }; } - ''; - type = lib.types.attrsOf ( - lib.types.submodule { - options = { - requires = lib.mkOption { - type = lib.types.listOf ( - lib.types.submodule { - options = { - key = lib.mkOption { - type = lib.types.str; - description = "Environment variable name required by the module."; - example = "DATABASE_URL"; - }; - description = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Human-readable help text explaining why the variable is needed."; - example = "PostgreSQL connection string used by the API server."; - }; - sensitive = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether the value should be hidden in UI output and handled as a secret."; - example = true; - }; - action = lib.mkOption { - type = lib.types.nullOr ( - lib.types.submodule { - options = { - type = lib.mkOption { - type = lib.types.str; - description = "Action type shown by the UI when helping users resolve a missing variable."; - example = "add-secret"; - }; - label = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Button or link label displayed for the remediation action."; - example = "Add DATABASE_URL"; - }; - url = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "External URL where the user can create or retrieve the missing value."; - example = "https://console.neon.tech"; - }; - }; - } - ); - default = null; - description = "Action to resolve this variable if missing"; - example = lib.literalExpression '' - { - type = "add-secret"; - label = "Add DATABASE_URL secret"; - } - ''; - }; - }; - } - ); - default = [ ]; - description = "Environment variables required by this module before it can work correctly."; - example = lib.literalExpression '' - [ - { - key = "DATABASE_URL"; - description = "Postgres connection string for the API."; - sensitive = true; - action = { type = "add-secret"; label = "Add database secret"; }; - } - ] - ''; - }; - provides = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "Environment variable names this module provides after setup or codegen completes."; - example = [ "STACKPANEL_WEB_PORT" ]; - }; - }; - } - ); - default = { }; - example = lib.literalExpression '' - { - postgres = { - requires = [ - { key = "DATABASE_URL"; description = "Postgres connection string."; sensitive = true; } - ]; - provides = [ "PGHOST" "PGPORT" ]; - }; - } - ''; - }; - - project = lib.mkOption { - description = '' - Project metadata including type, owner, and repository information. - ''; - type = lib.types.submodule { - options = { - name = lib.mkOption { - description = "Project name used in generated metadata; defaults to stackpanel.name."; - type = lib.types.str; - default = config.stackpanel.name; - example = "stackpanel"; - }; - type = lib.mkOption { - description = "Project source type used for repository-aware integrations."; - type = lib.types.str; - default = "github"; - example = "github"; - }; - owner = lib.mkOption { - description = "Owner/organization of the project repository"; - type = lib.types.str; - default = ""; - example = "darkmatter"; - }; - repo = lib.mkOption { - description = "Repository name without the owner or hosting provider prefix."; - type = lib.types.str; - default = ""; - example = "stackpanel"; - }; - }; - }; - default = { }; - example = lib.literalExpression '' - { - name = "stackpanel"; - type = "github"; - owner = "darkmatter"; - repo = "stackpanel"; - } - ''; - }; - - github = lib.mkOption { - description = '' - DEPRECATED: Use stackpanel.project.owner and stackpanel.project.repo instead. - - GitHub repository in 'owner/repo' format for this project. This value is - used as a key for certain features like stable port calculation and - user sync. - ''; - type = lib.types.str; - default = - let - inherit (config.stackpanel.project) owner; - inherit (config.stackpanel.project) repo; - in - if owner != "" && repo != "" then "${owner}/${repo}" else ""; - example = "darkmatter/stackpanel"; - }; - # ---------------------------------------------------------------------------- - # Root Path - # ---------------------------------------------------------------------------- - root = lib.mkOption { - description = '' - Absolute path to the project root. If set, this overrides PWD-based detection. - - The stackpanel flake module sets this automatically to `toString self` - — the flake source as copied into the Nix store — which works for both - pure and impure evaluation. Override it only if you need a different - path (for example to point at the user's working tree to read - gitignored build artifacts). - ''; - type = lib.types.nullOr lib.types.str; - default = null; - example = lib.literalExpression '' - "/Users/alice/src/acme-storefront" - ''; - }; - root-marker = lib.mkOption { - description = '' - Filename for the root marker file written to the project root. - Contains the absolute path to the project root, allowing tools - to find the project from any subdirectory. Add to .dockerignore - and .gitignore so containers create their own marker. - ''; - type = lib.types.str; - default = ".stackpanel-root"; - example = ".stackpanel-root"; - }; - # Whether to append the marker to the project's .gitignore (off by default) - gitignore = lib.mkOption { - description = '' - Reserved options for managed `.gitignore` generation. - - The devenv adapter currently exposes the legacy project-marker toggle. - Adapter modules may extend this shape when they own `.gitignore` - generation. - ''; - type = lib.types.submodule { - options = { - addProjectMarker = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - DEPRECATED: Use `stackpanel.gitignore.defaults.projectMarker` in the - adapter-agnostic core schema where available. - - Backward-compatible toggle for adding `stackpanel.root-marker` to - the managed `.gitignore` entries. - ''; - example = true; - }; - }; - }; - default = { }; - example = { - addProjectMarker = true; - }; - }; - - # ---------------------------------------------------------------------------- - # Directories - # ---------------------------------------------------------------------------- - dirs = lib.mkOption { - type = lib.types.submodule ( - { config, ... }: - { - options = { - config = lib.mkOption { - description = "Path to the primary Stackpanel config file used for evaluation."; - type = lib.types.path; - default = ../../../.stack/config.nix; - example = lib.literalExpression "./.stack/config.nix"; - }; - home = lib.mkOption { - description = '' - Root directory for stackpanel files (relative to project root). - This is the ONLY configurable directory option. - - Subdirectories are automatically computed: - - keys/ (gitignored) - persistent credentials (AGE, AWS, step) - - profile/ (gitignored) - ephemeral runtime/cache (safe to rm) - - gen/ (gitignored) - generated IDE configs, schemas - - data/ (checked in) - nix-backed configuration db - - Example: ".stack" → keys at ".stack/keys", profile at ".stack/profile" - ''; - type = lib.types.str; - default = ".stack"; - example = ".config/stackpanel"; - }; - # ================================================================ - # COMPUTED PATHS (read-only, derived from home) - # These cannot be configured - change `home` instead. - # ================================================================ - keys = lib.mkOption { - description = '' - Full keys directory path (relative to project root). - Persistent credentials only. Computed as: dirs.home + "/keys" - ''; - type = lib.types.str; - default = "${config.home}/keys"; - readOnly = true; - example = ".stack/keys"; - }; - profile = lib.mkOption { - description = '' - Full profile directory path (relative to project root). - Ephemeral runtime/cache (safe to delete). Computed as: dirs.home + "/profile" - Replaces legacy state/ for stackpanel.json, shell.log, etc. - ''; - type = lib.types.str; - default = "${config.home}/profile"; - readOnly = true; - example = ".stack/profile"; - }; - state = lib.mkOption { - description = '' - DEPRECATED: Use dirs.profile. Alias for profile for backward compatibility. - ''; - type = lib.types.str; - default = "${config.home}/profile"; - readOnly = true; - example = ".stack/profile"; - }; - data = lib.mkOption { - description = '' - Full data directory path (relative to project root). - Computed as: dirs.home + "/data" - This is read-only - configure dirs.home instead. - ''; - type = lib.types.str; - default = "${config.home}/data"; - readOnly = true; - example = ".stack/data"; - }; - gen = lib.mkOption { - description = '' - Full generated files directory path (relative to project root). - Computed as: dirs.home + "/gen" - This is read-only - configure dirs.home instead. - ''; - type = lib.types.str; - default = "${config.home}/gen"; - readOnly = true; - example = ".stack/gen"; - }; - }; - } - ); - default = { }; - description = "Directory layout used by Stackpanel for config, keys, runtime profile, data, and generated files."; - example = { - home = ".stack"; - }; - }; - - # Direnv configuration - direnv = { - hide-env-diff = lib.mkOption { - description = "Hide direnv's environment diff log line when the user's direnv.toml supports it."; - type = lib.types.bool; - default = true; - example = false; - }; - }; - - git-hooks = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = { }; - description = "Git hooks configuration fragment (consumed by git-hooks.nix)."; - example = lib.literalExpression '' - { - pre-commit = { - enable = true; - stages = [ "pre-commit" ]; - }; - } - ''; - }; - - checks = lib.mkOption { - type = lib.types.attrsOf lib.types.package; - default = { }; - description = "Additional flake check derivations contributed by Stackpanel modules."; - example = lib.literalExpression '' - { - formatting = pkgs.runCommand "format-check" {} "touch $out"; - } - ''; - }; - - # Serializable configuration for the agent/CLI - # Modules can add their config here for JSON serialization - serializable = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = { }; - description = '' - Serializable configuration data for the agent and CLI. - Modules can contribute their JSON-safe config here. - This data is included in stackpanelConfig for external tools. - ''; - example = { - myModule = { - featureX = true; - count = 42; - }; - }; - }; - - util = lib.mkOption { - type = lib.types.anything; - internal = true; - visible = false; # Hide from documentation to avoid serialization issues - description = "Internal Stackpanel utility functions injected for modules; hidden from generated user docs."; - default = { - log = { - debug = _: ""; - info = _: ""; - error = _: ""; - log = _: ""; - }; - }; - }; - }; -} diff --git a/nix/stackpanel/devshell/profile.nix b/nix/stackpanel/devshell/profile.nix index 9405a1b2..4bce8010 100644 --- a/nix/stackpanel/devshell/profile.nix +++ b/nix/stackpanel/devshell/profile.nix @@ -37,10 +37,10 @@ # stackpanel.devshell.profile.pathsToLink = [ "/" ]; # # Integration: -# The flake-level shell builders (nix/flake/default.nix and -# nix/internal/flake/default.nix) check devshellOutputs.profile and, when -# enabled, pass `[ profile ] ++ devenvPackages` to mkShell instead of the -# full individual package list. +# The flake-level shell builder (nix/flake/per-system/shell.nix) checks +# devshellOutputs.profile and, when enabled, passes +# `[ profile ] ++ devenvPackages` to mkShell instead of the full individual +# package list. # ============================================================================== { config, diff --git a/nix/stackpanel/files/default.nix b/nix/stackpanel/files/default.nix index a2851c68..50600833 100644 --- a/nix/stackpanel/files/default.nix +++ b/nix/stackpanel/files/default.nix @@ -910,82 +910,6 @@ let echo "files: $PARTS" ''; }; - - # ── Drift check derivation ────────────────────────────────────────────── - # A derivation that verifies on-disk files match their expected store content. - # Used via `nix flake check` or exposed as a moduleCheck. `stack doctor` - # performs the same comparison in Go and reports drift as a finding. - # - # NOTE: This check requires IFD (import-from-derivation) or must be run - # against a checkout. We build it as a script that takes ROOT as an argument. - driftCheckScript = - let - # Only check files that have a store path (skip symlinks) - pureStorePathsByFile = lib.filterAttrs (path: _: builtins.hasAttr path pureFiles) storePathsByFile; - checkableFiles = lib.filterAttrs (_: v: v != null) pureStorePathsByFile; - - # Full-managed files: compare entire file hash - fullManagedFiles = lib.filterAttrs (path: _: pureFiles.${path}.writer == "full") checkableFiles; - fullCheckSnippets = lib.mapAttrsToList (path: storePath: '' - _dst="$ROOT/${path}" - if [[ ! -f "$_dst" ]]; then - echo "DRIFT: ${path} is missing (expected from store)" - DRIFT=1 - else - _expected=$(${pkgs.coreutils}/bin/sha256sum ${storePath} | cut -d' ' -f1) - _actual=$(${pkgs.coreutils}/bin/sha256sum "$_dst" | cut -d' ' -f1) - if [[ "$_expected" != "$_actual" ]]; then - echo "DRIFT: ${path} does not match generated content" - DRIFT=1 - fi - fi - '') fullManagedFiles; - - # Also check symlinks - symlinkFiles = lib.filterAttrs (_: e: e.format == "symlink") pureFiles; - symlinkSnippets = lib.mapAttrsToList (path: e: '' - _dst="$ROOT/${path}" - if [[ ! -L "$_dst" ]]; then - echo "DRIFT: ${path} is not a symlink (expected -> ${e.target})" - DRIFT=1 - elif [[ "$(readlink "$_dst")" != ${q e.target} ]]; then - echo "DRIFT: ${path} points to $(readlink "$_dst"), expected ${e.target}" - DRIFT=1 - fi - '') symlinkFiles; - in - pkgs.writeShellApplication { - name = "check-files-drift"; - runtimeInputs = [ - pkgs.coreutils - pkgs.gawk - ]; - text = '' - set -euo pipefail - - ROOT="''${1:-''${STACKPANEL_ROOT:-}}" - if [[ -z "$ROOT" ]]; then - echo "Usage: check-files-drift [ROOT]" >&2 - echo " or set STACKPANEL_ROOT" >&2 - exit 1 - fi - - cd "$ROOT" - DRIFT=0 - - ${lib.concatLines fullCheckSnippets} - ${lib.concatLines symlinkSnippets} - - if [[ "$DRIFT" == "1" ]]; then - echo "" - echo "Some generated files are out of date." - echo "Run 'write-files' to fix." - exit 1 - else - echo "All ${toString fileCount} generated files are up to date." - fi - ''; - }; in { options.stackpanel.files = { @@ -1053,7 +977,6 @@ in # Make the executable available in PATH stackpanel.devshell.packages = [ writerDrv - driftCheckScript ]; stackpanel.devshell.env = { diff --git a/nix/stackpanel/integrations/services/aws/default.nix b/nix/stackpanel/integrations/services/aws/default.nix index babb1c38..2781b8fd 100644 --- a/nix/stackpanel/integrations/services/aws/default.nix +++ b/nix/stackpanel/integrations/services/aws/default.nix @@ -26,9 +26,6 @@ # directory — it is the source of truth for the data structure and # is shared across multiple consumers. # -# NOTE: AWS env var definitions remain in core/lib/envvars.nix since -# that file is a shared registry across all feature areas. -# # Usage: # # In a module aggregator: # imports = [ ./services/aws ]; diff --git a/packages/ui/img/apps.png b/packages/ui/img/apps.png deleted file mode 100644 index 47339a98b945118a7c2c1a95dedb5fff9d92f6e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118549 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¥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

0+H6oj+A+LU4=X`miXb2!ly#2G(8G>P$NmxD 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

DiYaph(o?W)1YgHfTM?nS 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

87~f6lhpX$t95-TO`psljeddEx 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 Itcm~s0Vb#-S^xk5 diff --git a/packages/ui/img/binary-cache@2x.png b/packages/ui/img/binary-cache@2x.png deleted file mode 100644 index f6ab7826c8bddfdc0156d47cf23e357b3c419075..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189187 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

84iF4)BXFk?l6ZELHQI#w6FZ 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

Fh6D=^#yLQ#o(RN6R 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Ŭ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%0Al*f2=l$+oY^Zxssn;1LK3f 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ȑ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 diff --git a/packages/ui/img/ide-integration.png b/packages/ui/img/ide-integration.png deleted file mode 100644 index 2ab24d454eea0c0ff8ea036f3e61dfd542e9135e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 251238 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
    ; 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�#_Bi7}_GM`nv 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 Iefjpk0Vj_TCIA2c diff --git a/packages/ui/img/ide-integration@2x.png b/packages/ui/img/ide-integration@2x.png deleted file mode 100644 index 9274024fb434f550cdb4f3a65c6b9dbdda58dd6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 696839 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

    2J9XUO1qJacRRNk;Nlrk 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 C!gEWf zR*Y`m0ve98tHI45fC?CN-e+h+XCld?4KXZrUf?~0%G4e9c(`cge3O42(zDn414;_4 z7@58oo=^g?uL%m7;d9?rFTbX%oA|@n#56lhFJ47|Z=w>d5o{7h-LpP_Ly6?_ja8`w)_%vd?wan)H>ub@2 z4i#H>?Gb8>#QmPm1>6#ktO~;yc0VMo(oX6cAuJB}j0vs+X|}x-Wh{eiw6e(2o%5?z zcTsbG`<8yMeYODs+Su@;G><32x>Pxkv)zmBA3DOq^UtfKvJzDE{;9YuFAo`LiPkYv z7`db|@F8NCqi*Y5tJ%GjW*AcIp83f6O&(l_8_$Jgc~dg_eZ2C^v6w`GPJOO|@F- z7Th_$In~neW-4q2bUdroGAaSs@tvLfq8E!o6xWY?OURke*Sa+fCttsiTLz;}HyyT=2x{RJ#g z6{lTUnYIX zWV&#O1NQf)WILdTnfl*H$_7TE3eHImaVTNg`vO5Z)Bavp zs%tn?pXX4NA}#7sV}ny&&I*V>G}D&bo8i5Tn%gDwJK+}U=?Fh-ZOGd8%Aq1%{1NT8 zICv0JiHJ4M^TIt}`#FCPdlvbp>tveXi(kD4#a=Mcu7?YJ^48r?JI2Pbi(-*`zd$8o zuY$1nF8%Oeq%xbCWx2jJ z4&=&^V@SKpn_Db;(=`TN5UBpR*P3gMFAk=g5|R4L{P$pLnQh#TMR)sRDs25tY_-^% zJ3Ocb35>%dVB}qu96hpdz@@o2_Z;^lae-GXxF8|ihubfG<5F;dbuf zv-U2%Sv{B9?2Z#=+7L?^tA2SsxBBBUyJ_SWX*34mZUyw@Ri9~j+jEm|v|)2$1qM#a z4B|<5$pnNT@>L=Jv>t9{H+^gz8ZT;VD!+Kby0!u9{>YbUV=HfQXeg;Q8v5?|Lh@qd zYx7BPZC=$=_BOBYbUi$3i?#;OLi0OrRd9ajEBhP{5U`puaRaU0vpP!z)!;AafK1Pa z5Gp9t>-ny_?LqB{3)NwL$kP61(g?*)Dj(^10@*I(CVC{p3x1DX0)*R9n66go?e=NY zKZ&+jmG^6I%EdMiyk#=`Y>hJ8eV+ak`sZ9?HtIBz-~36O#IejyaHBcHW)j22)|7!E z5fG$4XDsW5QXZULf4?q)CXb9KVKJ-%Fz^s5TzVW6nXDWM>ezS|(zug0IMP?YI;dOYun8d$VED zOWfYlUH3wc4da&=j;E)e9iVm(Qnn)u#`~x+pwnOs!qg80;@F{kp31DP;5A6^Tzd3u z)J;eqt>Rd=<}xJdfFq#iOk4>rv*8_d*x18!SN6r|Y zDq+x`Z#jn|Qq2v)+m&KZ_}7aA(Q`0?pqLTQrK+z*R;b$TPyhy0q2QpPCjj>q>qL5q zI{J1TZ#rLi!YVK_>gj;qrEJzcKt09a4nVd)A81U0(VS0jHc!Y(-vdTuSX4OeKLc+a zmR)^h0%+tZmW==vouiRG{3J&M(Cjk@i|7GTOzD>LO4)3P+)dEY6m`q_79dS7Rg?ZP z*%s=6k5F-pb)E8y77c@!;i49y)yef6cFOHi1oBJubdix0LQ< z|IxUA?~RMy51!x|drQC{3nvR=b`I#cP0dG18(tafj{v>OP3&z3I#FIUtczQxkmeD)9@u zl?Hw)h7Jrhfg8EWrcBYeD&GYjV z(B<@(btb0WMt}W>SPw{M&(w{|KP7uN)#&FZ>9R7AGbn4uHsuQpe#wBzaG}#0a@sKw zSB{9X<`4ce);_{>*ZdUHG!5g=inxVc=WW8lv zlmGwrFA9R9Qp#wMaO40{ax@4?OvC^gEu}EJq)Sqe1{os-l#p%#snIPpLfGi;*uekE z=Xd?D@9*=y?(G&2&hvcd>vbH@F_)-?cVFf1qMMoO?=`Ynvjn_S zjrpUfsAvK>5@@Y~cn#|j%3{i*X^IE_!`q2`FCW#3_cYr{Y>8)6Y*QTjZoQw6ks?(X zZwFl#JCg79tN_>62}RPZ&oWdDe5xnzXK{|<;5$rUaWbbXOEr7htmmDv4vrkWlcJMe z(?o3_xMqVy9FXSjj_p1Fd2ULW*TcM6>$8VrbD3QGCZh${Y9A5Jv({hQf}hBzF{OrX zZhfH!mUBtX0z^i9-T>`8l$YMko8K+MgWDC<8#+oA}lBdQbyve3|fIX6J@DyUI-S&SLk+q0x3!3S+=GS8&akO!JdIM*w1;;(1 zToys!LycOI-jGaz4PpONcz{6$4tL(Ka@)O;8UO0CsH43VnaT4^7R6 zxGMD~P9;vvQDi?qLw}(kkY8fv06gg;uqeK#p`jUxKdi7v1R>TERARIHbsQLe&GCGJ z$KWja)R2W}+wOu$F|}@sFtnCc&EFbg4F~ESV)Y&x?*IG~3u;n4RYKn zuyBToK1E75>TXSOa2|7FdP{t+@2`;prDeSkU*ws4%x*pRstx=Ber0!UP=o8rZy-5G@s5@tY0PxFOYV=& z`Zw>L-EWGC!ObtjckmS&<*)CTq7H^mhADcr zx5~K{G>!P${-l9Z2&z7%XFYox)Gu8D^)b=LUGijsF4jD|+tB#<>(3=L&v2=I7AqSY z6^kYCUkEOmZiB@nivN@xfu?(?N1#xYYLvQKIJ@93J2-ZF^VMM1C3$t~5r80#qoAO` zxJ?uR%*Z6&FrYA2s#L$Vw${S)l~Wbk7!H&oCmO64D&vDjULLatr-uNd@R_IKoD={h zHJ>;9;dTt}E!EfOe&MWq@SMS8UQQWRq5rG)z3ljx%29UGBy=D*Ufra23%~?o)wDQI z0di*_FkXs#Vh+;_nzcI&7vC47#mMrnMS(msk2~kmtFCyAFVi&Amz)U!JOKCjn%(d1G zeP^UTjLojP_)7v)up`9-70qR4bLo(_v|h1Ck6r{9RU7wEsiEle|DOiQOSYBz z);Y4dMR%2%Gm%YHU3fg6{McSidzqx$3leWueHCD#zF*S**Vr@_g`P;R_4I9QKXr?0 zskZ*jx#cXiYdm}y@7l*YQ3N0WQjw{+`NE(B?$2UjoY9ZGy-_tIo3A!xqdAxDpg_9L z_WF83{9$Vd1x5;m<9k)eZD#&P;AMBOhJoq7M-@p-7O?TA^98loW(^0VE{- zVxy+nT)U}?u7fRr&22MTX8t9^aGwG&=?k>=@?b2@6Uyx78sC`%oK4#wFsJ!i$WWp|>rwMYzA|XbCRUqC(m5Xhu%d7TO-`WeajJ27 zx7jK*eFT+6-m{&lOR(Pum>$O6AMUpFO?a7_W<`=@6_0yeTqY!4dT~{ZRk(qOD*C)s zSS|6tXCXP)&BcT-X@#%&tu4DF^rAj}ns;6(*M;meW!MVA%Bk=ZXI3 z>xb%fw(Y*s1jOMLaR=6|MeP8fcya2jq$^*E$G!y~@XQl?1GJXLhYG!6oOVT&VNw9C z_WLSe94SqRsiCP)$QgY-sibqaR#W}{?nqh0|6clnC`B9yCk~GmpTb<-;yzbcJhFmo z0)0CeObvRO_VejUV5buwc=1og28s+&a+B)V1kG22)_i6$)B9XM6%6MyRR8@U_R$q@HyYx; zR%-DVT}35Cvs1cDnhk(EY`khV z&>5@;pur78k$!CRfF$Sg4O#xh2XefXE&a#|IZ-$ zKX*zcsprHVtLXsvKaobnK@I_S(D*<+PM&FoHHnbJpK`eQ3s^>ml%e@2vkb6+e4*^8J+T zDV_Xo0&^171EMAm>FX2nwK2!Qp0wwcYR)StSY1f%rlpyB<_NegM{c6y8UW-Cjn}KW zPf<}{X1W1-ldZ=KFQr2bwjW;I{}n4os`eW3J6=6~scH`B?AOkOlmAKhi~?`28;2O;-R{HiUceKsj@H%_oN8?$q1+|LgbE z`lJ^_xI0>4NGvO`W&v(R3E(|_m!VN>1Wxjw3HmG%oar}cqpo^1K36)}+nI08o#yzw zoUxGi%q&Wm(NA&r>*7mFfV1HavgQoG9c}?Md7re%J;g7egnNooTPfo68vWSxD zj`(-VgUu?wT2;d$E8rWD;Y86NZ!_9V)w!&1;DJsTGT zMIAtDBCpo>zqs(xndR{U@(nWU>pkC)#2rTnz&LBcTQ>4tQvaTS;p8|TfA9yVFuAQd zkp)*36;eQ^gUY7?5b8h<#+S?dqySaNfYvxwLvQpRkihqBASH|>>lwmiMGG+M&hz2C zff-maRU}8NhRCXkb^F%}sCy+bM?|kUnFUBbwLaSejs!7FBA1XOBmni$($KhjN%)Wl zpmf#x!2T5BnVS0ZdIqQm?!joZTff-B!; z3TH63+i5;^Kq;<1O!BF3X3l@HtjNDOQlrDT#W44dIl$piuI&nV3b6l|tWFdG81bpg zvjG^r1Y8PI#w%?2J3Nlt(r%2DF?y+nd&D&8@&_OTc3d()8`QhLWD!t`{c)L%qaa}@ zh?01evt1NWssRuYgBU<&p00T+bB~Z8rjw(e!ZOkC< z$@+s!=#C7HW|U9bQnklBo%EiotcmQw@yl%_5daf0pe^m^&!5WSP{A8Uq`dy7tg&e~ z?*R^QwcX<#caQ(hTROgP2hi1c(gG%?;R8 z7=L)EdL#~wOaQ`*8v&=6oKSCf_lXxC8?P>t5X#t1~>k-p|$b54{Q1!=cHXkA((Rm%J}-|VU86*>I{pxea4qYlh%(Z z0oCLKN8NyjoE88V>Xkphg-I!@2Z_(McQ1+Z^||i5&DJ^L?BY-FL8Ct3SM~=I&0ltBy4gFL#y+ycVN#nel4-0uyWHADKeodN4Ul70- z+}=j>d~HLttFhNkR-HVe>C%Bqu!eE}0H6|mgI4imi=ltWa1%u(4*`j&s1fk5d~NWY zsJFZieD3z!m`}DnJtP*_X@9`23=5yhcF6Kz~DI_@n~34O$@7 zp!Ici-4zP#UF6i4z*->_<1REzTbyzslY0Kd&j0P0zvY>a<6I8imt zq_+`3i;avRT!L)ifLV1O>C|89dQMoDrI;38Kn!;$@D+%z23X!ai`?vM91!(=ctCD@ zD2o4dH{{|^DO2NdD#XX;fZQ^-^w$2_?WB}HS5wEmg7e1C_+KrB_rE{>87=4|Ta($X zqspXju`YWbwSrt#eJ%Us*QEVMil9$90d$U8T0E=kPc%CbyxC8)&lW5~vYyKVzpI@D zkPcsnHT|mAulL^WayLA1&sy=h+~$$Q%8Bd40Jk@AdAxT(aG{pwaMU{(;b0<&ScYcw!YD8N@>36`S zmjE47k~=K2XG772zNq||-dPJo;-%f0Vj1O$r~$gZn@_@JrR80*m%g7WS)o}9RvEZ* zlt@3NtHhS{UMGizO1sQe$Y=4w5c3)nr`4}XQx4P?Yp;FF=KV=o$Yg;(wHdW+in#;1 zvP&1IqOl~#NJwMlJL|T4BE-7yR48AB7zyNYf{7ok0(R%HQgW(z?5S&x46aU?9=w*=B3cj=%ej-Ano29Mm9 z+YaIT(eM!NJfu7}cPU7k3q(t0O{Yixf$sqN!E#Bj&G$y-XZDJn$I_k3=U}YI5VqZB znY8Yo6(_V7prsGp+apHpB3p9tr)i{}KuZoa{u0b!m9`7b34P;M`hS?e79w|uBl2?6 zj@&qmJYEc_dv$4#a*%+-@8ag_!4f2G_dsM!rPQ?ET3=&L>I4`cdftWx-jR%ik62MhMQ?Vwcg0OJkAEtnFWpptOY9}+1?f_bsu#M#ix1?DNEc(@&EI| z>?0S%E@IUorE^+){q}JPVHQu?o3>c@UI)~i55mR~X;(QYX|S&Fc5xc3EchX2CRv5^ z7&{+F^0yEBU|-#EFLq~9&{qPW`bfW&g`%#O+tK*=UW&`6?1nDwFP=p5cL8V#Dvexd zmYx-|AVWV8!9rmnB|F&n&$iwh;y!DP!d_g@9u3xRaG0M?Q1&w8z z3H~EZ%8WF2FzS%*Qa8%Me;H|hKmodqtZ;nTqugPhWMDC5G3{~M0%|nYp6G3&)3P}? z4PD`ad_3Nmq>(x8f=$IydZGLMDdp-Bji(l#$J{gE@;=3_@qQ9=_Tueo*l0dW^xf>f z&`RAscNP*~;lxlDGaGyVXyeUULL>)!i$rIco&5+&=6P-^qAXPvZnYdeeT~$5taT1< zRc`0uA>p&z=a3kIZg&^%7Y;nKZ;+!r1Gp{-K@f(JCL?cbY?N*1&d}~{Fv;pY$N4V7 z%KHYzLRSYIKaAbQtigQkZ*e*%nq|ozI*4NKS4j2$W%tE2Xav0+Yj-`I^8?2fDa}$4 zWcKtua;LVMc%~2Lt+|TTS$t0BdF6tfP~!kThIaSjQkf$fZIlh2?2lj(1U`zDH#O{X z@6KWdpq_@P&XEC*!Sd{==V>JlT5FeCN;gI-{of1m>k`OiL25&|V9(gsGLTSQP+E3X z{el+lp!=RxFu(<;eX`1P$+_3rpn^V3u1;jgE>Bm!3c3Q%a1vGt;-h*S>Ivue0)$GX zsccGKSHKnU4a!f4;jbIm8vqmIBO(f*)ml3fRuu6ARXCV!XF<~f)ypRzXX(TGB+5uL z)_{8zUraQsf@9@W%H%5UP66?%qh0kM_VRtM`nD_ zb(8U3x66!P558Eci2CNmUxByPx5B)^JjU4F z!bH#A{XvfG-R;5P1dX;eTv?qC!6O7{z6AP-OwuYeS!)D?3uv;Q``@G&Z zH9Eyhf3bdttDI<)ItWLw-y$rc0c!rIjdM0)}ek#Cm;E$WBuE2m*W(@D_p>xH2!_WJTr zDy!ojqdKreLN_cb_R7N3rWPSuA{LMH>1CPmtcjBNe;_r{9qh*bKw9@6+5p5ZU9;i= z=;N3SFZPmBWjPiyjr@qr&@{{sZ~yv2^R&tS0DS9>GP6XAs?>6_$hC+YjPo299nO@XGOGCl)uP?(fdZ4 z8;X3oH`T10Iv{!=+}hYK)l=Hnd|aDCz4bvU%_x5f&xhfkn~mDGYlPt?XCzw$ozKgC zw?W88=GAd%_-}M`7Ypg0=&4MLSESe$n@%)=E~yK@X<_h zT|5)s?YJu}==NYf>#*AYVRF*L2F=X|YyEck9Hb)MCVWk49&pQq$>v z*jasON3v;M(v1sgmkggqqZ7$|yZX6eSN!wY=)C8?tYU?&$D|!7K~KpIxuubHq zu6^rbb8|>nZ0s)1Z0#+vuVAX=iCOBX|G3xM;`$*Y%f-QvSG3V=`X4OP{2U&%_r`9g z`Ut#fDLo2oR)$MmTM-^jC%Ollno`pAXI-3Y2R^;+y{;#^Y1Sx0hnY;(irdb<%vh_W zHR)U>WIQTPLhxP453}Mbh5A2Nj2^J!WwiFr5mjbye6}9b$QZ3dPn}uG8~>h;ygk*- zVgjXDO3GRljdw-q7#%q?CGD9Nvle^{ITqXBss8(5=UfB-PrFwur8}Bw`W;u}YcRi3 z>^{awi^T6WI9HtLN#p5<8O{LI4wML1aC0L!l0Uyj~ zj)%s}Q$OkMFuFGs>&hO9l@Dtt3A_4@F-vyg!Qmdw1~ABBDM;>erhP{pzs5{d!zGaT0zXVH4JLki$pT%vTn?7b`91T2rPCAG1kzc%ef1Q&z(&4GmuFM=+kLyWr zzGr0hI_xZ5lIC8Y>mLVZ1oTwg&W*#WYH#YdiD<(def$ryhWOR4w z{Fh^4;Q7a0G{tpL793ZaUdq3Xx*KR4kw%4$@<{1-oMy^slAwJigNN*w%B%&56@kUu z>(jjV^aeg8{=O&0coq?1*m159ea&ORfRzFjntWlb;eO}#k=O=e1=R6?_IadAuC{NJ zz3k)#(webyEDU5`1~p50xOy#09n$jQAm5HrR#eDmLQEo2+7NvmB$7{#eo`fEJO6|f zn|_f!tE5=InGasyI~SB~ER&vieV*KxoFSW%epK%_QG6cBJk{%(eXE1FXGGM{2g_xr z?_*<{TIO?>j%9E$)hOY2>2$h@Yf>NecX1+QZZfS)SnOBhSL>VACnNO|qRbdrWpI{gPH7tXU&flSk7TjrG9@e6ycs z{bZXM6XVUY&UN-#xG$p0;Z~NW$oY~Vd1a)8tdB5+C*p7~bwTVU?U~dc^AQ%=JH{%C z9tVtAlnFa@HjW+Ln9XD|99ea^4&*Gfe zVqs^Ey>!U!#$+r^LYP)B(YC*izI-&oc2(@!a^Rd&#+Hq2$1RXRElJYfj`B|)yZB$H zG)-bG9vdxCG&Cxj$y5B!kmUi_$clbcLVS-nR`JcuKO! zXDVFEfLHE~(OXbojUhAM;*$u!i{-GTR(F@e-DR4P?DJ{Rw%Vbf!T@!WH518*pTX+o-{HcMQO`*NDmw+ zd#CT@^sCVs7tPA*d)8_%3nbPl@|@CpooWs8R%mPjJYlEz@#q9UDA^z9nf z-}_U9S9Gz3N83GqyL{F;9lI!#4Nph~e}$|iCJ|C65=Fm!z{>s=Y=3C==!mC5PkJ() zoY!)1;I|DWxV~90#i~)vt8iZk`hz$OhKE=VQXU z;2CtUgCR?kWp{1UQflZ)2g9Z%Bc6sYQq!k(MK=5(7njBZsYDk2o><=Z1rJz|iH(vflqug- zuL!YCApc8V)D)p{AP~UPbN@-HY>CetD<$k(lHGbRlV@`JdFVw-!3@$ zX2NJvsc`xnlRed!xpy5>Vlxwl5crU6qss>htNTF?~~0XF@J1` ztceoygpoK4_I|%GV0O7E`RRF3o$gy!9R@|} zogqP(WEuOIX_E0^6sWSbTKK&8OagWS2oB%59CjWx7!j(|M4b>W07Q1Gj*v@)zTW{B z1M4L^EDZG!ASH7S8as@G2yC0gO>`^)Dh_h|d3$4gG;Xs?xFe6lzPxD;;FgH?13Q!C zIqZDmV{bZX_=(GvW%>=8i&4vUV5zc>bU6F&g6uy`oge1`mMc^G{$1(tbf-6KE3k_D zB~BOpXOq`$Sc@g5KSqDhkueLHQx^Q|jQpp7Y}%d1Nn*S&rGMg~adQ+BevsWG@vZ?; z7I$zWH51vi%rzd5jL3euhwpp*cC?|-F&M<)F5zNoXP!J0UR23{*&!1b`Tl_l#`fLF zeaQv3M;LmlC3h)>lXHVVL#XVQ+-Wx&g3n3OSUCcp|Q!UL0(aQ%d@}v*I zZ`sFm?!n0%?*9Sx!kZnkHd`1$%A@{gjth~4^J?_xS-~=y1TlahSam0HIVdeHG|n7i zQIl_MFdF07AnWIY<@{1MlFbYz%b5)!ds}h8$sZTSKAFdzdZD0-Q-}#=YNGlT6)ksA z0N9aT0X>j%1(8EA^SR3p&$NVnV5#kZyny;oz)zGPW>F5x!sYCV+N&~*YPems!)>wb zjF{H*%~kMPD@nEyw*%!u%f{gk-=6}l?bkqs@vTxI78@xDH=WLfctyn9&|3#mq>{6G z)vRtSpsAkZ?7ut%c0N!R5SNYAHU&h3UIl(qYpbRuGYhAC$K^ZiCOU1GC_1uD#yNWJ zSNw-(f*se+qvU#`$w2jxsB(TxuyAxuNNBim$3=%OZh=BR{=!19jpB$y$zn==a^roZ z<`MbPy|JDKuL03)FJcB_(r%ZY2UBReJez9b-73KY@n(wD=zC}Jt|A{VL5*)DSXO)O zXv9dqiG+oprg~$P8CYTb<)1H{O|oW=9MhkVb@`~J6h_OuZ8FzelS`fWL3M}H?6e>K zD+MZgf+ZX1FWaoEC|t5_qNFxp6`vfITse!-52{G9VMl=P!Q{L!)rHOL zVgGrDEH+N~mmSE~#8cZ15m3rZKgX{!8Ohfnc&_zV2X?Wv#P5PDkC56R71>meUma?> zARbm&-)`zT0A<~6xxb$0wbtQ^bNf{4zq1@OvQH&?F5clPFPDXIs!Xlh@r?C+E0Ttl z!UyX=t-$wnMVI=BM1t__M|Nb+7j1OdShH*13#K6NLEg6mqDwrCLIMgS_Iot{n)$v z*anOs8xi#IEt4$~JhdakQ=_$xBxJRQbAvY`E~MJ&x}wcTQ_QJuOG~EO{&I-KMjkKP zoQy~jeB5({mvbVsp|Ppn$#XP5`jgexV0=vV%s@6&0lCOCQltt^;q4htXOi`rGm}KS zM=_4JI|=zml#xpdzB|TjjXjM7ANeAT*?M(YT;APFI5jZw6iK8T=U<*(IUrfqi3BCK zyUn11#>*nquPgWs>)%M4wf^0#w3lJB%IG~Ne6rx*G@xBkOy9$_PtOhhl-r0UC0uZi$#M5X z%mMkEj+=o)n~$4+N>WLM+24O2NPY)IBO$EpXXyda6{pYh%F+w%GzGg~!Xm+&NFm>& z^7NvHTRfQT!;=kH0Z7m@F(o!FWbKZrp4O7L$6AowFReC`tKb8Vifm86=T$~+6@f>! zz6i*Z5|%Zk8J-ALVTg^mgqz^=Ccj*i*F_sx#^fBy!U{0kXk5>dxdo4OAU7>&b{)9` zuaSBGx=ptB2lR=BsBPEpLS2uJGCaOmhvbA(dx2~KqI7ABfT?Evq{#_I$ zf_$_uRXg2?Rx&EJo!gCan^1;eqR-no#8*8VEq{_R*hIs;*K`s-w|LpWjsyCRL2JG4 zhMf-0tbE7bDc*GQpO2`D!mL)pLMPp*LTmh%6jH25e+47sR$)hhDeR<9d~A>}){NR4 zI7j>Yi8Asw?ikFh#rmS@GzZ)1mIgB;oNV!JfT{tpdk|$67pC1-jt(5J*2W5E9_=ZT zBG4^2-Hrqy`0e9*tHWf+U<>-)J4*8xnEB0PE4ium+l)%QvKHE6Q36WRb>99e*nMf! zn+n3~IJX<}T_je-_ZrO#X%f212&fbHz1?5#Z)*X^cGKfu?b!@GA;Mub=$Z#*F~Ta0 zUhw-Xr=@Y5II)CKXOy*(DSw%*0nX6sT>30|x8EVI4UBY={m~{CCWOFvTWTToY3}Bt2qxXPW@A-`JaDYP0KLJ`a;iI3^c@ ztb#i3?w{%0+m|RlyJ#uf_`3nySe8LeqBhhws%<>0>2`&;Y3cb>S(_#X=v0c9g)fvC z@C@f&cK2c1LbQY@Npt-3C#@HuLOtR+HXzbf#xc#Nk#y+Lui9`GMV;!nNsx4zn6GKX z1?P|yK8w{dpB1Ju2$}lRg0;bRab!$3MpB#wn^o&;l@{u^vW?YM(kzNxK%1{k^!sEZ zvE0kwE|h}7UrBrfOX%LR97#tNO&1#9@_bbG_f`6ahju+U5;#4Xfnf0^0)#NFIjvf& zKAE25_;x}(1=%>kwOnB0A`%uYbnIh{EHQFY48Aa)+u7Z2oPr>$3&1r=^{nrirqFeC zcpHiif8d7!C2^c|!%+^zi9xDot^qb51x2laq0dH*s1ILvrDozl{yDWlSf3!;ftM0J-)t4bO zc1OyPD?81(sDp&o{s9sqOaPg9lb@+)FZkhP9FK!`zjBzJ>^2N?x5jvW;p2sh&;gk@ zvE14dda^iO0JQXH{Vk*L42NW`JJvL|Q} zj}#;M6lTOkJG+f^yDCGI7KpFSGE102J5FsCC2Poz0slbHP7bWr=v-rSN4PJTcY!$T zCg=?HqMK&l!GFcD)5k1ivn?4N;4eI&%p34yJn*cJOg-I6c%VIBrFJhZseO*{#~qg{ z8@qkA#y5TZJrJJY=EO`Y{!s|x#m^S+Zct0#O7aMs;LXXkSh7g}1Ab8U!@<9g(CFQq z{TMWyUd}A$&BXq+3}!FiR~mHDiT`qF??mO0?j6A>vt{jD7j>Jgj9fzWM3WA(M-PsC zA9OS8^||=EnC2?^*)4fe;a~WX?6c51MJ0<=Hj6s$m6A{NJr$`AVY7gV1P0l{3Faw|ee2 z71rnr=5o4Ya1q*}it#XO(}}qWzH%7) z&WC0)WI~L78*%yv&C0sY-y=ckvRhaGlM0(Or{Cc}-es3>A840}XKxy47G(=SVRPo} zcfE(ANe=2|Up`DH{v|^Tt-j)7CLOsTRL=# znMxPROgEVLY9~Ekz&hfM6g+6?j0>s?Y&1)|ctnPNov0#ywEvs1#ETYL>#B106s#g| zasn1XJj=HYY|!DpRI|95rvu@utoQ>Q2K%C4?_BL(6yHI_w&&+q@9qdI;2Os4;06;`a> zm=06>CyQ7sEpgiYD>;sZGljiNv$~(QTy+nQZVf?t)I=?brKSG)rs4EDbK}Vw<#VOl zUo)<>auf4!>2^4mgUH2GOzLDWtqCmhU-F>_Z7J=r;hL45&=i7jZ*Z*V3AMx`xDPq# z6pK{Zr}l0k0fDQPOzmS=)s~wD@8~94v()0W$B}TS;qDuJ;PgmoU!_S~TIj=Z){)~? z0j$HD8Hujkt%DmbSYk0)$%@NzK=L-_&=QBFATelU!dhZ-Ip~WhXF3;tHtx4WY`>ZZ zBtW7fj+_<(v7!O5z-{m(qc$~eLYqo{u7VD0i`u#evY=vk^RQhgD- zE+08wcT1HBUq@-wcZsSjp5|;gw(Us$d_)?#Bz)B`l{^OoE z*RElVa|PegCqi7kwza`W35vnh-yVLh`I3Kp*&lNm4$3SJeLv*^r@@Iur?jhmKsxCk zpq_(OezBW8ymk8HK)=bRj5bEq5XBh(*>14bAKNru>wgGYn$8H-y{?)Mo+8=|Iw~?_ z#B~icvy0J6TCTjwZY!7ums}K2WNupemcl|mTA+qIB3$lgl?v)cRi)2~i*fWj$QWas z5zk{KCRy)acO$X*b%7I`in_l3Xor25T07$PBnwF(TWH0*^sI`YEY0VyH}tRnB}RMw zFjIpKuT0k^@2w-dyvXcFDVEKrOq#I%`+|YDT9IOp`%J6#J#My{0u_i+$JyAgG`1*j z%}M&fwciklC95Q|wN}nafuWxyE~?(V)GuLIx*n>HJoWp1TJYL)71eY=e7932&66@$ zfMc$Q%zaJg)Hokz8`odiie25I6CPoX1O_S&^nMd&n-ajW6`r^xjRgVXIO9kKmc)s!#lCM8?`2xz$&EX_ikFYR z>a;Hyy<+kVoOJ5a@NSM_P^`42NAp?W@ zD9!RQ1@Uc}XekaYaWzb-Xzv@SQhhDmEX6$juwmkCRBkE*s}7^*u6xFUL9#whZa$53 z)tpQ~zKCDG&egf@hR86iFv6!XJ_;3CnSLhY&{o>+XjNTC!~27tdhn6EpKhjE+#EL} zUQJYrX~4#lC!brGH@`?Y8X7#QmBqt#k%W0EQ`AywRCQxk;&sW-y^Qh4>icRZ88&;9 z5M>+YfkLBe@-x`Py+!nisT9t)@Vp=|FJFBTPB&^;VXH3C)Zf?JQ^nTV-JdzW+bofO zo+lq>qFcOa*!wuI-bfGJ!oHb+eBj@CQ@zilm2=Hm z){qZHbkU{7n?o{u10*)GO;UxgYr zJMArpmkjXa^@p89CqO-mRXS@%*COj)K@J<*_73CUAFELrjC}J<6Y2TIYasH-U6G#u zGb1ondK9(VX1JfdI+oV;URGPk>Zyc#v#TEe-u)R6Rs(y96L&6pmE#oc+87xjeZhZr zr{G=~ZrX2{UeG&%hj{;fejej*Q4`BoM?Gz6OE)w7lO?Ug;rhLrNb2jn51*!q&8{}9 z{#nn`;$vn|;w3IBn}5eL`PkVGT8WscFx=9tmCWLmVs=4%@C=K416s_zRyUop)?+J} ztEs+0X8%=@c!NgrC%;iNtY<0U4%dFUAm!LM3gsB1Zz^x}@~8u~Nf$@R zsq=Ycqve6pz3Ng`jLX>)FpN^J~cf{ClWM( zi*u7)8_knEBNStQjMus+J6#KexdVc_+RTbH<2QOY5?&E6o}C&5k}ZBX)eIE0;cJ{f zF+1jCL_Z$+hM-)0NxR6l;-azQHCn`4EExWQ^Qjyy<>Et5q33@`KiR}|A7!o9X(zuE zfvZnywJ|F@IZd-oTd^s}BnCRZia(@ac%)}@s%Pf`?d;E(+hr?=H1T#ya;gvcxfH+r z^yvL0ME#eBYX0zJ&rErh%shk8=gK)9UqVHDYW?tx{nwD!#6c?`UZAdZ?1kir_W{rQakY0-X3dif6vOh zVnAy)5XzxTa>VXJzk|xkv|$b&K7%w{srMnP2cnY(F3k|NXgWaDg3p zrgr-{Y86$db<9w7o1AF=ptMB47D)AZ7wfjhU}VX+r!BzgOYimp@Je)Kf7KFuQ&{m+ zQCmr8&F@iRMFS6-+n~GNtBY#|cl9)C{$?hlm}CO=qZPlQ)jxkt5bmc2Ep=HO_u9Fd ze);r#zLL*)VaOaVzrG#*wnyIj#_+cGi?FLtm42&Vf!KxPhqkNB;(i-M&Hlch;6C|j zRB&0bE!*sC=fL7x+dC6S9P&AOq_pXK!0fXv5AwQqj35nEj5oT12NeQ>6#J?Kg+~$-@{Gx@v`zUg8ohRhCG0!rQ?Q5Qr<7{Z+VOBqI zoH93EFjQr;^zkxDNsnrX%>_0K_fr&6iO(yo0&m10{9ae6&%)0^v!$6uYi8=lPZqc`Sc?5=JtXOyXv-q0go;R~>iJtW#5@S8@qqfA}ats$3yp!=^wJ=st z-O0VZAk{As=XJ}5H87YVU#oknj_{W(mzlKn+5Ju zz|x*2`mC?Go&TIRD{XkqzzKQR#D6_e9W&1mUBl<=(62Kpz5ED8sc`UBWAqx;vc@&) zVfyCJ%s59Sv7|-)mFm4|k=`ldY!M0>(+Fa+v8V!;Q$_v#J>TqqVpjZWF#sEKsLyHaDl4K_&4Ow zf*h*@4b8RTjM^!D;gS;JA33`N(06J)m^iQFtab_{iKZ`cLMrEp@Ncubxrh%?ve>1oWw~c{g z{H)|Bd9I}ixoD|Bl^q#YYyy$rzPjq3HET^yR;@H8>@IhHpP_R*KIrqP)QysS_1n)& zwpW6%ucr_1-lKMW>v6yI?N_~x(k!zSr*Ia}ZLqFTQPkp|dlEO)#(VF@l@`q(TF_1~ zy#s*<=J7Pui)Py=$YCtI?Gq=ObIq2HV$QIn2+25+E}3*Z1#3kyzDWPD5W2Z(J}~T^ zl|wKHpfCB!nbP`3?b`dF-47f>unl*GXh&$fo#GPxVZx2G47XM*f4*BTqg*}AQ3(sO zR8l_@T)uKWz6L^V-MgEW|0XMdN|#=haY-_hXz^=&x*5{?MY`nj9mzcINqZ3`9M%zW z{Jj77;^UBq$8}3q8S-k~A^SH`#Z1{kUcYn`WT-Md|)B@{Jo=H40u9!9WufxPzir_(TQ`%vshJj2vIG!;H| zbLoTP%=^DdEn+S$Rf!V5Pd`+8lL*Bhs{}^52$(%CKAbs(9Dcd~*S>JLxXq*Ma#bx* z@Qmpf&Dy7SbA)|9EBUnLzp_uWW8mU!we?jh2DiI5%h;t=Tf1*Np7t6SF<>V}YX zjDOPFM-qc0#38<%{?rER8CI9Q;gL4$O;ElVbl-N=`NkPO9Xj0n;g(~YOadt&^MSdy z&?s!o?><9Vtt#EmA2gX?`suk}q$)N`B}SFAx}z3ZL#-M)oZVWs`B^}NM@g0Q^>cBT zu~7#eDYiA^B-O$ZvxSJTpd{nxXBoU%ACf&1dTnYYBNxm3qsmxDEa!&(x59tFqd>Ce zoAr}V{hg9&v#+|5Nx$pwrsOCoPTiHlqD>vclqtv3;Qa6~dDGDias4;rI#$;-HB2-o zJIa8Rxh$Ba{pYkJ@9@(wp>O0-mWE34>?NOjqsf-Q(yMCoMFpYuYkJv*8Zb|W>w3-c z+Lh;$Q?1TzNi;{n{fZT=Ty1>mHlIT~T?g+>+Ra#L-S)e!la5rhj-J^M?rwGDCbtc& z>Sb#$s-9(7Mn0t0X~|RSHqmDMQ#1JZHF`*B|LFfC>pcVE?7H^v6w!hxLB#02gb2~Q z(L0G=f(W98(MC%_LbMp81VeO*-aAQ%-ih7~qxatOZrAg?&wb_ofAhu6oaf$qm1C{F zequs74Z;QFF{c?F#OQv+!IHKg_a1Ghr|qLBIyh$&mw9tHC=?3g;v^{v2B06 z;}^+V%9V?Xj}0E=LLJGJY&Q)Q933^zR;>+!>)TgKROgVkGbJ zCBm-Vd{$(f{87oN9+H^|T6TX;A9<8%#{b5jBS0+`bR!$LR{feoxyHHd@pYf&K4P2T>qDMowQBOqakou7}oC8Tj)b7nP!{xNn8S}r? zC0d~??(9Fp9WxdbabSExORH-A92wCnHuzBnj6^h4W+mU}_)!lOT8VM>j50E;RM-MN zni?_(Rs3Q!@s^99F?!~hV-YpWHxtQcbB;06SZ1X*7`{iis1Rq+#z)wVny=q!W`c(K zQ%I_&POx?czYG~fhQeqX%Ij?RQcj1m)4DFOBsU_1%x=qwjAF`g0?7j(4s*;!*JKt1 zyP^%UAM_oL@&uYkNMx~y8ksG8yoMxZdTnw@jq!8mRJF*M%mQIF&D^eEwf4m9X)`>e zW_E{z<=asD3Ms@c+oacx=gA4BaY``pMIb$49tR0CMiE;JsfAp8DUI;#Ayli4UwDf7 z93w1LL@+Ki(Xpclv|#C;Uf%j}5;xA4)KG-TTIH`Bfe6%a52#I__1v0!%laRgCS7PJG zA#7?L<2Y@~v-hCl=)?b#$ve{ z>(duf0F5 zY`jcAO*t$$ySBMNA!bmpHe^}Y*!dPwY)%u-{*9B7!OfxGX zH3OVljwv(MEyZ*(gSX-%1iyRRIZytHYmMl^J8wBLIjARf+q#1c^X1q;?yln1cS_J* z<&ZBnm>jD=!{ok(Hlt*OT-a`atGbH+llF)gK-T-4$SJ!ED?Z zBvg%d8XB)va?)V9y}q}}leygalEt&{95O);)eaT7E9yNNJlIYz(<284*$eK_qsYxj zZ@|kE?3U?Co}TQ0+05i9V&M>P%hya*dsR%fA}YrUcSUKlf_c;mgV(v9-jO~D*2{|S z6_f-N={qFKD9dr4KhfgBo-7%h7Gflg@yRt$Zj?VlL=h3nlA=<6xzQesifcQJa`ee8 zNoUFZI^u3ryW1}KsV=fry_O~?=FQ?DRjRLpP`|sIj_vK2a&F_qk7$poTIE~ga>TOY z?m;h#2;F=?(?%gYvTz$7AyiOS{@Dd%njbl-Tedv;kO2y$hL62|dXG_z|Kd;H30u~h z*X%4#J)C}Ud(q}>zbyRFv{0Y-wM}XUJn6V`W1=D7`wA)QLF3TK{RnF#yo7x|#a$OW z8EdX;(TiC$A;QLxHTLasKzSR$o~Iqg#|D3SOBZXdP#(8&qe?O-J^x2V%>BFR`C5OH zCK^@}8#uv#Gcgt&C+(%~uJ zDSzdpH6R}GwApW56r&I!0_D=O)UPuq>Hp2sAu5BOGwD4l<4*&lzj7Ur0ShNrU&spgtb|j;p)W|KP(p@fr4Bvjntr0i* zN3_HGY3d6}7^*S!w)Zg8JpYj( z3m9Ekr8^w7LYpIlqjof^t}zMUQgPk(I?MCgWd0qP%7|6ZlGIzZg*)0Km@f7+Uaxg{Tj7!l!aEkhwd^2M{roOrLm#z ziq5$`AtqrLqrqv$mlug$n>YBvQ@xXA)#E|xqVgeJbe=u~HnB#s7}U}2ImE&!?%xFQ zaf_%LYvqkuY_Fw=y00A_A))-M!}ii*)ji&>qhyu;;GJd->D>WZNpO+0V*WacBv4@W zjmI9~2hyhnq4~cawz)17Ykx=@8kJ;OcrwI&@LiCf5Ce$~7*#c1#q^7GdALVCz(Mhj zU&ruteD{R#2IsjwWN|8~-vUE~$J+{>+;V8;Fa|nzpYZlt(P%r-U%cAE)rVOzoL&N z((jX0m}CB%Z_(7jZ&iFiUh~Z>BI&O5{8tWIQs=zd$vGZ@>OJ-6f=X^%2dExG(aYGi z$?t-Ebo1R{CheuLlDkFNE7&xuzDvF*T{e<1?Zs?|B)Tdd*v$LoU+y8^ zQO{cTBfXlL(i~kI`VV~L*D#Atm9dWj^eJ%=ZRI6(MlaV zO_CYm`(!CN`6(OKNmfyhUh$;&tQhn;jTSqGj(T&Xgr!P6I{r~p zEwym2GfcghIbDsJhji?h*%$n-$L?f#6!$9M%6pvah>SaOjjR>5t{m%XF2J1->t3=Y zqB!`%ZpI`f=R?V4Nk00#vpn={olSBlWaAkt#yiVK>ea=eS$tRF=B^I& zIua!mB4$(F8%2S0bC%lVXL8ur<(-qhR5=~Y zf|yeen#9DNv9**9PNhBV-GW@p>mp}6`=tmK7RnE9niPP^eWQLVJvzwr8EQ9R(W>+A zPfU?UKi%<0iHWEdKzq*6(5S}Br=zowiBAH}Vr8-wmQJN7WF~bK>+9eT=w!0FalJkg zb7XVH^ux_^$;xY=(vS(iH~W7=nt8LcG2U>ie2IV{H(K*Z$#%WZSH zi?f=Qx0zK`$ZfUb(GBY_$t>8Oo%zAqq8i8dU$=VtD&pz|9|b>kdlOH`7wGsrg)w2J zc+!7f+OUwotSF_!)jd1YLB%U)*~EP^IBm%XOMmr^2L8dXl9<5I!|ejkdM;77k3=5^ zQldqz#E#Vx=l=}c{Op(cJR~Xfo`Z$hhm|tqL|Vm=Bk#CILpoE-AQ>*>lm^4#c3MEE z%UiGShbunn*&9}-$T)AU#I0C;iU;c$&xSn|L-V1EL8gal?eqF|vWRL7xZ=2x-cbg_ zQXS5fAy|Dfp;GV4?aJLZ<~v-POirUBzsUvX>}0f{zu`^Q-}@(tcX^i71yu94xT7x= zvQumyMr2bv*YQE=L<=3_4(Z&@i5!YdMc*G>6k)E1)FkN(H*QOzELLw>7|04)TlpB= zX?3*=Ek&L}+1l|F!{wA z7X7yEi>+KB<*K^otv(9jLKnzDgm7;hqX_S6$Xo40a@mye+eakc`zSw+#1lZ8MiI-E zX;O~+U-Xu^=MT(gLm7J1%V0g~!79OQLn0Gz*qLjIR&Vv%nVwO+jQm)E@op0&Ha!E^ zv)HgTt2WyaI1CY@=o%d!)f3)R8dU-}Ezh!)U$H6f?3{6HkM@|b7+*26uGV!4BsIpT z>~fXBK5e^KPt+srHS9!winWr@)3x4q|M@uT)C!fCXr+3nL?FJ6sMl_E9DMU&Ej+uqnMxIJ<{)NG?LKY(bq@jV zC`n6vRgY<0xyL(^6wm~Mf%wIn+DS(PvL&s4Mm_;)-)G#qovZZIBI);=9asMdtiY~_ z8ND6>*M@1ty&m3$O_SgsZq1lCE`_s7Hv*mvbk9j zo3q?4GVL&yX8WXMA9zdNFRovI_dQZ6VEKdBp!!|*V)rM?mA67_af=d=r7eDJ3m5oTe2)=2tmC&Yjmx>Q}KROsVDr2g6 z{V7Nd-qlaU!R7uFjH2Jr?8s@2`)&5QasR#kJAsI&V~_i;Tbqdvl|7Ij+@t=(^%W$r z$T%Vn$2SWhp2J^>SSETriZxf7OH3iTAF*^Xl1S)v03k?5<6<{$~v2S0!5ih`W;wE8OK;_-IOw)5ZL`8 z^PGCOUUSH8>(92?SA7-3*|1v^Etk(;-61hO(lEk#c=6>laMN$Rs5K4yr6KK}me9IS zLFzZyQf7ER(Nl%->yCJ-sJ+35+KY?2+1x4a+9QzLj}y}`KiN+ zv|rt{UK+JBtvQ-C={h~`lDM&9&jGB&{vGt1MslSA#JuL4t*YvbL%LlUiJ zJu&P2hcucYI)A#%iC8=1k`_xHN&O5d-J32K%xFeGAQiPI)l|jO3HMN&=^x5#2CD-O zN(weFuCEQ0BW6B0W|st+v;!Lvy_)FJRDW|Xb|YhKPSK>to2y{LRTmrf%A9_~ zGJiSwE5VkU^Xo$2xRvDhBMvpZ4ql7?81BYQJDWlG2IV6n!U|5Llxb6<8{G{3N7$ck zvZdt@DaXxh_it!bX+n=wy}d>Yzl4sID7@X5_gzx#2;ha+kl^vv`DRl0y00jtYH%@= z`q30ViFf}>pL27^wC2YgKcjbD_B%&&9=iqd?yJ}*p*wsuARr#Q~e)CM>H{2p!^larv>{)%&6hHv{C1!A$}q~Qjv zLO;Fl!c(o6>+7g6p70+XO!vx?c3lW7QtX0j7#;;hwTr5sw{mcBoFw z|B|%y?|BMgb1?UOrYd`L;;r=eYour4-r&wND1hnI4mZSRv;W}|4Ye+!HS|T&sKj=Q zVW>n4k?J>bMD~g&%k(?i%AhZIt3*+|l??~^`F_a5Ez30+@%@#o zE=Xw|`_0bu!Jg{3jzxTORI|#y!NhmUVI6{bvoF7f0QF z$~;Hn<%BS;fz(7xb)@T{-e}{x4a*De-V0G1Lebdgq3PEPEIQ3MjVnw$F*V%gux1)O zYtqk#nuf0f3|2k3YCNk!QXjoBFJBv5)pBZxZLM2PQKN@Ef=W|yNV#Y25gz1QE5CtM z)Vmd$*BrW4e(+PPew@Q|$4TPtPf@pxXz4~Rzt}T%?SW7ciq1zm9`e7}&XrJuFHY(q z!Tl$Ge45@Xk{=!Wq-{iS3l@vJk*I?sqezE*ZIQCmUX8 zhHs7ei&{52b%~Ptnn*JO72?TysXqFs@o~!ns_|@4@Z+3d-TUU~k-buZhevRASdA1e zTv(2llHd6RJZ45n37OhB7cM0!zZjHYqu?;e|Fr&`Vj^b0$EKk%zQTglw9{tbFoW}X(lKU4Qo6p%J|+ z3)dM}90-z3`#GqtRHxI*9k}I279cH&z~@VFQXpLOax^@Iq*_^KsBZ<=zV3P+rB|f84yP)0Tc~)Oo&^d~;SMnc9sg&a1h)cz=!Oh*0l3vzNSs z_X73Iz4UESA$*qfA^#Cc{RytMryF*u8zXKGrK_d2;q^W0-^?E*{^|TtYf-40P~pHo zUa?!3{eyQoSXfzTnqBp#RmaB4CYPbm{&=#*YUy*nR!h$mq@(urJEBsn-l=O<vu z^%XcZXANrDQ)^XQJh|c@_G={fOhtmdsvX^k?`OpN5n(h%Go8H$6B!XEnri#$bslS9 zX-sQBRBObn@%Bp_S37LT^$orXtxrZSPxjYKv2O_-`*Uo#vGl4=o%NcXow<$&f|M4< zQhgt~>*gnsP>{>umOY|&>ZFh|=*<5$h3*~v^wP3sW=ROTOWZbC(d=6bfSM9uN-DTc z`zoz<;pt?eK>CRsY9@pc(~>U(3f`G_!|(l~#BB{jW`E4Pt+_kwEIPw3@bOXpx^e?+ zfB3`Yr6)qEp(5-C8csNlWx=A`MjC@56}Of+R;IOLNdhjiRw@bVJ(;Ac$1=mbjIPw- z$Srh8jz-LIC}kn($c`6VS^}-#gOSw4b2C|@ea_eV!6wTujADhYY2r}v7O~nI*{v_J zyK^rLK2FvjrdP-xa!e5oe{q#wDIGWca5-{#C&4Mzo$#eT$xcY9pO6H(7NT9YH8^kw+glQquXTT?T&XNEKfnaMc9W*kDwxI03> zM3n_ujVi_;_V?7W(+#@p@TQ}Avn9{^C0?ZFS^b84p6IVm(qDhSN0WR$nnOSNtT$C{ z&WNbr`}jGGc5%nQ*=@Ok?zjEXcpuHWN!Q_#SPO-yP|OI*Y{PeS4biYx?l|R5=N4hQ zY+&SYQfqsXP+2z}*GXH3>a3pXbnosz$rUfCS>rvLvy7N9YjtR}y4qUasL0w@rNRxW zEJ;-g8290L@}_%3qL@|cnwJy>Y*JT0VAUtp+P#-X^iTy1mvl@IsL&lGagTg0IDItQ z@~l%oHl5CTV#I<QIFmUWBP*r30pX2*>h&wov`1l#hBTQqbZm z;|cnq!ty%I^E9`kJbo)wc0f-W|GHcKJvzF`?B7-9(i2R)Os7XIeq}Y`oGJXGjXN#W z?v(EgN5j*;0nhIb!@J%~3h*di?;wKWJ}G|;oB%6;pUwvzLS zJyemOBw#xO*~oH(q+OWqd=^I$2RkulfUA}=mv6ixcC88`{V=z6ap*+nXiPS}Gnel) zZy|n{Y1ow;hLRmxB(dj7GQryM_1jOUqdBC9w0cFyj(p?koO-Xn7GFPkfc$w|h!So2 zy)>NdTif+7%bb0qK_4KJGdaVX%_RY(FbEzTBjQ%k393b#y0O@5hk=a}WP`8Sv7_zV06JRZoHtzc~(X zYr7&Ze{?wVzUwiX;nxx$qnMl*Oo&SuJOk+@@a7<))D)WnH_@Z=$fA-KOe@IzA*VX% z$7*Qdku$eD#2$i^H(xC@f5Dm;K2Y+zTuHgKOX8)p@3#))8^PcGy_D2Z9_)F8x6P&L zTKX;5tD->s^z++W!G}FEhU!Engly4y{g1KS`^^_uBsL50Do3d2(6gyk7=13){Y=)h zV~|(ssLIdzCzc0_W=XB1rK2m19;dxcUjy;MAIrzZZ(`aQYc#nPkyG+BdeZati|#Ma zWy&cYfP?&&oTUEmk;X+IubE#dO}NIsN;j{V5*gpXazl z!e7u&DgFxAUEQjr@jm@FshN3c$E4|swWXV_QNCy4qj>FJMJjr>4)o={An7jEn%1C< zLR-rw2H@OzBo~b4`?1U}Wm!JZ0}V_jJ$kO`8raMl8mw|Sc3Sg1PbvPf+~-NW{xCsR zW1grPra>ivM=f~|I-veFtQO%<8STmC#m)@O?y)dfeR`WQH6=P&UF2SodV%(BC;Y}Qr`Qv>aqg(znDhdV!=Rp6S4-PyRsotupFQ0 z^wXE_$$Qu$YCrRoh3Rce1L|5He4{Eoj0*o~D9Ig2ZPm5-LxPz`K-Bqa*YmybDhc<@ zW%G2ajT-us5~D>Vo*xI@HKJX>MN2bRo9N);&s4+MIvvo81#D+X3RC=NsAd=6)-{N- z*%kozm|ZV6?(a~VWWjabD(5q#|K})ODbV04?^#hW-$pyW^6beTduFcBnD|`S=g^^- zeI&zq1Wdhkj{d%V6NgAZ>cJfGeYLg$$B5p zUTZpWuUE-*BG>1ber*+NL3c0Cjz;WK)pbUTjlX7PXXjeAM*P}>B5v22FP^*`q212$ zNnM^u|M|bq@Xz;jS>l=+#&TsEE8CTimzURq!5c4GHr2cj>t;&okL%pq_^g?I3cEgk zw7C<@jOg73oF%G13(5uWNR-X2WdIlp>D|`hXX^E1D4{2o>+eUnZbJ$N($I{MUNQ(% zUsUfi=@Scy2mt!|_@^qeU}iQKOn~&wI#4x&y ztioYzfZfGLS!436tk5{_|GfKuem7WOgS+x(MsTajcZ)*z1$tOV-*5#}U(HxQ<1UC| zgEJs1YmS4{+G-(^zEwZ_lA2M2gM;ci(}^@<9)VN%{G~eUdh{R4TV07ec0SAnNjpjX zV>JHn-83QG7L|rR&(xO9q^fcu#0Otal^BoJdOFv2I^BvgiQrV3rHVqE){?(ax&c2A zU^^KdHIU%vA1pZU5!(v$T-RfVYD^k--g&R&`oyn+i?Mw})9HVI`9E*z1^u5{y7QZG zMCFWief977kwWvr-h*lw=BE?zKM(TRz;s3cu&T;x{$r5;P>N9`g41^1JF*#N$nLzM zw@VP1cSN8$QwN8g8P2T-R>t|GkDKmNH~BPbdH(g9vQ$UmofMgzu9hmb8mDf(vkvkI zu)nig_!gT($h;6E%FK}~dyg=ro-K&dmqhFkuuh;ZPEzk6t$*iFFWV=}S{~a{& zT?ESclsqiUBUsjKQzD>&KtI13^=1lx&}pnB6EXFXv7G^LS$&{3gz--O>G!ucVYFzK zNh%md$yGl3wmbsBWb|u2HWKvP9=X_X5@=)GBMCX`tsE+W1^dr-1*@_3=R(RsuP>I@ zg{|F#Yq{TXUW2h6z|`S=!YH7Fwefoc~vV>>@#6uRT{Sg+HqQ1X?@(J(6^ zGtqC~Z7sCSJYRR+`U;=S;?VQVUruM&I z&}~6LB(iZ7(V|uR&IvKC$+e5SKBudz>#i|5hQAn$c74(=i0)R>o3sDAqlN|W2a$j9 z6nj0m4kBp8=7qARs$8s0*^dF}NEP^ozjjY3wz>!&I+geJPN?~in6vsoYGP7VDMPSv zG6;&uRH)rGwvPa6JcZQ0V5Dpib;nRn&0 zXFY1l?5(Bp9$e&6=vPZ!pX0rn-V^4(zTLkhJGTYD`Enxv|BS}fic?`5=5P0eesO4E zdzk1OUq4!Ar)xWqt%3RmI+Mo~zAPYR?^b7td2K&~MyuXYq@!!6Lo3oY0#pt&JSN7w z&NzZjYPz!TUd9?sW1MQwLvHS`#g3Oz(qBSR%6beW851Vh?jEd-eDHXnxm#+}D`?Y` zWLIR|t~bxT$xTT;NGJS?embfd4uBQkiVQ7osAH8FGjN*@I!;vNkJrKZJA7;R0Z32m z+>9Mk2sxeRynv^#EB|e!WJ)SbjJ$+OOjZ=4fvGjC2M&xAGn=QNppXC%Myt#H>8Yw@ z_sqyi&|{`7yUr2Au#}7hMFIGT)2*vnhTwnLL>NW*Q>0!?`ZFQK*|r=cEP`3%$W>dC z(f>Li_pIJu&qly+@F$cuJNkKo`C*!L{W)u5n^jg6wJ|FzD@9L#{9_o^2kJ1Su%44h z)!G8y5azfX)Khmr%gZR!r{p!uD>k+OM{IABFd1DrtHDVnTv2fi1fC*qv#pzp;M1Xwy+6S*49!dws5^^6W^UFS6}TjT1A z*{c7&iQ^e$^b4X3Ofa^tq@F#it;|R!UnaXsnaSnfyZ2h176EbTH*iOO=(Z@qBi1N% zagn#bxR3{zyMKH1If67iGE&G3u_po$BXK%j4FWnlA@pnsbqRan{bdo%e+4@HpCDdS z+dY3ke9#kO$c2FTB{ZXKdXot+PWD#@kH&MtBHpu!q+HD;XZca5@TZ4lF+WEVksV28 zom%3#W%eMaCDQlhOU}zNfQqS`fn!kWMs}6!j#agc?N5&f#Qxd=VJnJriqqI$HcNJ0 zUI-?)6fw_&i~KTUnGuaNZ{J9^lT^76mXeGesy0aCc9>wV$1V01_BC$d2f&r za!8~hLev~I{>-g2Z$f|dsz@fI9hAK4+QhOR{q|l0|Sbf>{A<&M$!Q_+$^$ z|E&oqC=C|AzT(qtY-|f>At52hQWFvS9U~&&xRZC873Eu_A>|+|dz_38EC8;h^+u5~ zuxwvn*kHJy;o2xZ)1TK+x>W!Zfa9*m{Cmmn@ndE3`Y{r+r3)_V=zt-kLnD}#lp(1WuROabHiH)$kV5U50 zrU-rRxEjT_t(VVk})3Uvg<0Hn&CC6B2Fkv(f~o++OWb;u>7Xm%)FN|w@&K8 zkB{>#^(HOx-(S`)d80R)KdT%u^--@30A#$_^6G0}1vOG${`+%5qA9I%=2W)0ZbZ5T zjpBt46kC}sjzck$fLzHUGhJd#)h_vyr^T+%{Kem4 zm>#tZrqo^uA-no*~1|U#s0E z+lI>cP!NVqBU?r4*%5udK2|!XY&M=B79Jj?AOR9xo>>q%0N*9H>lV>wDStymiaAPL z+$^K5(r8=RK`)?VOrILTV*16^ z^LgWOukb%Mw__5NcFmqux)!Q-nt=7j)H-CDo!0)n+&9~QdQrbIh5A{MnNdbpqzE~V z-xb{l|G)4WV9TZE%Cg4rXSp9Rb^VXMdai6MStr0ZjA!48PXn8NHHfz>?I%N{K(P;ne zA?dNqv-MFyyTo0kyztN~3FZ1eV6G*Lx^rH}u~mO!6Vc$r%IrkkQrN#E1pzRY+O)$z zU_%=j`toY-c8NzxaGa@wT&Qm5uO*154Wv<-Q;hIwXE}BC5XRGVb=!P|03=tLc@2iz z9Zu#Q5l3IG!V2z50pGbybx@DwPQd7Du-b-;8B%I6SVDVo-C;* zv;DxRPj5R=QB%vo@6^Ts`QcYsU}1Ds^a%&S+{W+v1~f~=->EYH-5`Etp&4tC-@hG~ zW`U~S;He>$~;Q!1Pk?sQ0-z&IYYfB3*R$+gsi^laEzd|RIV9DrT@0Q^2?OBTf zeT2PhWiXqI>I;_(o5e<6*gK9P8m5E1-HFi2e;ihcBXc?omEoRU*4dL5!bPX;CpO<@^I?f+4NCEs`XVYz?ee~P*Zx*8PUBUGq#=l z4LDOT_c!D1B94O%Bzv9YcdsG@qaWhkkS##N8_kCbfL8k(L9_th?c?A1e&sqK)GY*g zhSmR9{uG4eI!)5H_zdB;eE)0m?!74SXOIvl9Fp$2jEGrat8^G z#nn-D^REICOHQG84s(z!;q&~}^!qZf)?7Fsf}1?3#kc=!6}aE~_b@B7-yu9r_Sv*6 z`a>ats4K@GIf5c0BO-G^pBiJI^qGPuf|TLzS4~PU8Lm>Bhk|s})VZbo zaP|2f%SCsK%7OhNW4tD}|4uw8(pnGRJ+fnqV5Q_Su`Ov*es#ER$g-KIktEFLY$%-d z?1!wRhMD1=JKBE9Q!1BfqF&px;$&`&?X>_nnt9;GkVK=C+HrzZFXMRh>Hc5N?Iy#q zD3004*nrrR4ITEYh-pn3Fr~1k@vsXkoo6DZRNMi$+5%nt1v>#sE}^Mr5T`_I;*)3Ug}_NrDgAZQ;t}t^ubC0C98JPBzw&=*^SeAh zc`=x!9-apnq`f`ts|k0qDblP((I3b9p0B>{lF$ezJjy=^DM{~7!Sj=jznf;qyq@( zP4Ce@ETEo2QpKCM0H#)GCG*QB?sm`H1sKV+ zyP4dIVuWHR8vWcCkOzg`SD&DmnsXl^>g2xe_MY|p+hBnhJ%ZW!Puz$w(qjh8ITWOw z6@#+~MDy;_{IAf1>3W|X#>nR6r>WYi^dE|h`=v{#fW%Dy~ytyfOl1yV$wpT7rGR!T@%p2E(;G@fq_%>?elw zHgX-yEi#h+dAEqi$tl-= zol(PUX>C68h&@i*eMT3y6aKlOueuvpqxsA-60tJO@Z^RAA-4FWk>K-R82`kxc<8Pu z9(w0ah#1%u^LxNyzFgPatlR6++ty<~0YRp>qnfTkzA)nN<$qWvIF04pIh`mq=9<|f zIIA)la#JN>3O_-phaHHZY7@i+fF=zbqxvTR*t4A*XlinjH{VTWYEJ~ouO2QP9i8(- zQIP4>l7D&xVibo;aD&ke;rX{o@M}+Sjh zl2|)9aQmwodDdOQJj)@9w|i@jlh~^7!s_WnTrY;u+rLtlBx9ZjO zB@p$d;`|q{e=&K3su0<`O1$}&Xp<|QZ%Mv4IXO9^7v#Y5?1iOMDi8EyI43H@kZFR% z^2w}DrG6hlT-vR?rmUqHwbhqr-Y7Zbga1`6{oEWEefVF=^CvBhxFyCZ!&=*=mslkqg6o*RE z&Q`Llwnr~R$^X5f`Q)M<>ry#PRX)zHbJl)o69omu`5wLK3Wa? zsnYOn=-B>@V!qEs@Ed6Ut~ z)sgJGk7{klgIhxWOV(B;ZJ7u}W&Zvs9nh1E{sA6-Vg}(qg^vMe6p9j33!RqWUua_fFaydOl{QnrVA0ww>4QfPcl5y~d z)_)%KBuh~Lm;!9uT+Jk5l9@T>WGwf*pH^`6JhKwSdG|#GE*8iHNag*vXMd2W6EJUf zwJgmkN3ch*Rll&felH!GW%V%Z?1}gOwFlzO+CuC4;^gQXL6Wq({+u*T zsHW^n_go&SD>5blBPko~U4gp!K!5364~P)!dh@NIK-S#sg#ZS$u=~bGw(9TKB=XA+ zm<#c**KubswsYP7TTY|6{$qruGlmt%ZbSqWgkY^SoT{``b&+s zBnvWL#@P9m-jQXCS)VmY%#&xj(qW8rYcwC|E+Ec(3DG_Fx3s+BSKH&k<=CSRb>&#f zzo)S*qOjyu4t&!4KH%4DTuc5YstUgeE)NYabM!B8A)yNU+L-1A1^Y) z=y?N*qAwu{uodNGx7GVsx!%S^WxBH>b9I>`M7OWbnm>6%Z=T=#$o4VYb1o80-Or5A zi(>=$(_V-xQLQ^mJ@KKOGay2;v$V8>rkhau-jR>fY{lowB# zXP6NtHCL4jqkQn9AiAK$c9tYlV&&eFukgtP6rr}p9q3%IKdr?U`tIp zPl{RBd!-k3;dQQs9mO9-(A9n#0`xL1&h5ZLxu~jAP;b;27b$%9*JpPTx4i%ZNLbma zbAdSXMLwD-x)3jF=-OGU(mrOku-De6bjIoD^Dj4G7^*aq zfI^{;Zmb#GQ`fb_w`yYOUjZn&S%OMjBW{nD zrZvBk^cqxe=aRG;?f^ZZ)_S3JV-Xrv z^Zf!PXs=LIYXdn*q4Qb8?LW4iRbM!k9Y4BHzzF(csqq%?C4%TgNSWPv;OH?Ew>M-C zsEG{mkEF(+aNeL@Gis}0LDE)|A8^?>7pg;53RCyCXMeFLDva+ZDo*@o5xb0JyknrQ zX3HkFG0W!5W_5l1?bTj$KZ8`p*%}kMI?kWRiDbhlKHSyld&)mg{^u;izzkS&V+&DyO9*@MwHthX0w-x)60ehDPI#fGEM zPu#&01}mA3kyydh|K~@98FnghV}hY0W_!-dS|?wX;YCR+l<8+b^Dqk^ixw@wwM; z`KG~0_;@yMgDwMn+|p%KU7uY3Yg3U7#Zt{<6MXcWQe|)ZQD*|DPjY0rwxH_nDCmXY z?*+XSJIpDRD~U&T`PX&@5p|q_>sPt)qUZDL-O1<;fxIUkT!Cd=b@lrJE2)QxkKZe( z3zIlZ*NGg`v4wyKwk%bea#wfw|`Jjdq z1cihH4Y)M5Ln&0~XN};{fRdZt48n>(iw(D~W~d>P(``p33hh`ivaULP)q|lynEE*> zDQqTsmo}OABRfyNzRm*PZAx-2Wij2Gpc&}C!dc?`<{!QO^UYt#-aI-pg8ZuzW&V;~ z#RN`)2a$isCLB+ha8J9PqC=nVFo3b`Z6N9?(X8Hlk3doX!yIjdKuQp`mRjNprrm z<6_%7>n8NPB^m-M`9@sZV2|jNk&*3@dMt^f4uUUN^uK)l`t^mIn}B$yX0kZd0~zdP z8b(Cjg5vaDYU^QK(J$7;jBiay(9c}{mDQPWA)W9Iye5IK&)4F)=wdusMWnZ{nH=RC ze3f>5W_~>xZJUS{&|9jlTyqGL@P$9jNkY&l&>&RAlS~l2Lat29Z>Pwg{6tCu1 zp6X5MHrd{0$)ug3m1fVrPI6-=gx-_Oh^Z)rs%`cf2Gs&(vV1M#{qLWUt3)Zc24z>| zm1A%9w+7~Kk2|NkgP}I&rG_pMgP2{9d&NlsNynI?N)`S(ll=R=KftutfkcnlU&>`3 zs?H3A@Z#B@vWj5p(H+j3bv<0E)<|E_VG)?wx+UhZ9(?pOnPpnnLgi_>w#C6~|H|DO z>}TdMY1k{S-IOQ_@i^{?f31W)N{5}T=&l9i0jPw*;}c^*(ME(9-hRP2k>5MRIB{| zadp*ERc7s8N;(eRaX6HeboU{SN-H5LjnYbYA4)o;1yPVjLK+pMI~0)al17@lnLBg8 z`R;$SmTQ*C`@VZW&o4u9)DMn6KA8v9q#=mMA|tRPaPmp9tNfQ*q_0^8uQL$k3hz%S zn1rU>ER(wkSZcPLuzS?X0jKajWd`Tsea~NHhBSq!pq&d z8u_778w5OoImsNb{$h7eC?~dtQIovjx>mXtE|_)cLZ}x=v^RnHdEF6=Kd!4}|HU}; zHRqslaL89i6uk3$*|C9m@f&#|tHdvMGFOXI1*{@kmJY7Zx3T@C|ZuH~n zS)|J|hI;{F#c`4LheZZxH{ut9NVFCR6Ef4%3L#zQt76T9AqP6JwO&|UIHWH9gcTsJcSr0e{48oTxvegf5wpSX=hz-7~J@!ByY*% z$$k6f&w2oWFoiRK>UrT$@VjhS9Hle*IS=4WlJ{VpR4V|V0#XGjKTzPErt@VHht`3) z`&f~@XPQdyjbyjxtmgDt^bF3VJVs=5-oSpC=1*^es2>&%Lb_Or@h|Uua`b?qXOJoX zd)^hGJQCn493Hh0lM)wy9`A#}6xlSCtjK9W{EOR~BvWki`fo z@;FP@0orv;-OaS(<-^Ddej90ZZreIQDnTZ2%Zn6$tTkR0Bu)9S`)NErDyg0gxfl|AUOcNhTKPL3^?}y z)#UaVd+5^|%WsQtKwZ2^`b_dfI-Xu8j+IC*;aM3CH)Mm5yomuZ18eR!YK0$8%Q7SGWfETHJ z%B0LJCJO01li~H&xo~(KM#Sh5>Txk^91`kKIe=!x+p ztr6X&$=qXifx%n{60zL0C0qMnpV^|uosV#ri-dB#&@#i`B6RoYcM{5>`3juMtf*p1 zVuA}`RO|=j3@FO}qr%P>*~G+V9iJ~C9ZgEVSV|E^?4DPFo8&&Lo+E9P1=bL&0#7E@ z0qD~gp$B1=qAhMifl;ypz5h2-f2S0nC4RCp6fh7r6RA%HUYseH48l+0FA;G7hyh?z zj4goK>E{4y(@4$0psb-xTfTyLtwN8>^=jYKyNdR#OJLmMro9nkC5smc(_I+Ik%`qR zk6U(I-6jlPWB|gzaW>d&3D?vI+UlP67xDjoojRE9@k{%mXFYO9pN-!TiIHU*X^V9D$p2_LDoVzTB3i{i8at>uPKU z!g88=1?k=zqKZA+PnPN~XUAFwf8T$JE6KxO2^r(4jQ_DxEL?dUboHRt0Q>5V?npkf zuGQ;X6U%t+K8(WqWIJLPps#}k(vf=7j-Q!nQgqW1U;i8(3A%BPgZFLj9mNEhA zI9Y1(wrN2qk5@K+nI-Mbp&#H7PEE3xeORN7zOQfnC?~{Vh^4^EFm|ov^LbVLU;Ec5 z#>uy^YFlDAw^E&<{O;rkX;srdKs;OwbW8bQ1ZYeK5?rt~Zv&Jgt9%dE3gVv_HqCzK zcSm}rSF$RwZ=V1BNvX8f50nxs2zsz02rcaBe@Tt-5;2@y=+&iE{wsy}pEGYlylOXv zt-gKxox~5Dp(;Q$+va__WzK;j_w@`;ghXIw2Ee`+GEqMlSp+u4bMZ7L3s=5Je`%F; z`={1cRscT+4=u@)*!{U*lD7(moxtmZ5BGK5VgLT60Y3?n-NiFutGS0-SYFKQ2lN)o@#xC^{e6e_Gt1%1?H~z2fvamBSeN^gF>DM_JG53?we! ze^}+G@SdsE&f9#Xg}=u1Oh;ip412R%zm}a2_*cUaXg6+SvYyHbEY`Ndu)BqC-}pbH z>+P3?3t(KZh*a&fl)D5756|7QVx))`@cp}-+*Nl;OHT&_NA(7{6g!$c60Dt@MykN0 zkoseug?^9*#a7!I>__h8&?Sg7r0}ae{ zUm1UVFaVHQxjDrK)u6^Lur?$`MP_C+vGq%A>m+^FKBHky$du6<*f&Rz2!Y(()h7iQ z)nA3(IMXBCsov^>mX1(kppFh*k z4~8n7HRNEtzTM0ThHvCDxU!gAt=~53d{_TEQcT|YNejZ8ki@G4L6#5kL zg7;dMyN>_OAAgf9Y2OC!x%1;Kh5IHiAiGwoBNj3T9)k!81$P~&TA;L%%jQ^-4lE}< z9mhArbdQ8hZR}?$BN*=zvYtSlB;g0!0%aCi`}*=?sABQU0N~a9Y<9j0_GbLhdDC|G ztlebO^ZHM#|8b4UMa{a``01%fjrX75M7_hyFbm$) z`~5CN4Z}AcF#vs}zc|HN52Zr8e>ca#nkV2B7@U`@$wCdA;VrE_bmi8b1@6B9KnvKC#k8_|Z%2#}e*k zC$4|07XXp@P(wO#?-M5hy2dVT_V#7m`PZ)YoeMvr2N`7J2xDuyKHnQd5cpM{rktN?Ca@d9pfL%3f@p z{<&M=^`08clR7XGFE9Er;O3|-n{`G8?fs<3XlVGB<}Zgq0TpIcJYPYh3ovU8bPFa{(kNDfQ2aDJTFbcp5X*_#QK-Bcwqw?sn>DhO^ zfA7Us*j>hD6Ps{;Q8f;>=AT;IJ!`Y(jVd%=C%F~mESGgq*Y4{Ux&V#B;QjNJM?WnTyOJH1< zq(N{rNu0crB3)cN%vj1jEP`O_TV6i1?WgcR_~UoVPi`C-E=NTlq;_n_V=ud6AKw-Q z7muSa?Emn;`cZt_%Q=1VBa~T~e z@Ly%d`;oejWiCF+82Ew^Gdvm!gYq;~XqWRn?79TlYmrE3_&YfftT~_6Q%3eQ+H~X< zc>PQ-f+w1RWlN7WNbEO9z5P91>(MY0&Pc->AaS{6-T`V%Nb(ApF(3)-0eHhqY=7)x z@#N?AvxVPndbI!h*76Ge%;jy~)Z~Ap?Eh*U<3Qc-AaPv%=15()+;``NL7-v$_iwOY zV}h2i_=8q@L4EyZs{dIkGxXZpZ`Jzi>(KQhzX|Tu^mq2AlMXD>Y;Ps0wZ(bly(a`G z9ST@`7?7{S0i04nnv8=O@u-Z-ypO`Nv<<)|L6NO54+<_ut2zWm)JF7+oeX1mJRVC$TT%ki@mcs$i@}wmvixJ zO()iCEZ;1^lp{i}Kb1W6z2RNAPV1HSz5!ODGyVuzd7h`xEqY+qFw^K>Ct#(1@PMR< z?!jY*jY|#Y1U9}VIsHzD&^e&QF+(|U5qr7%s069vf`g(s=E+6eNanPt_LBRcKzOnH zWW7d5lmD4aitCETm3R9;GgS%!DxYsLrqsIJIKId)jc#+#nl`Hy5x*sokKkF~IM!$- zT`48FB@7)hfYZ@X3WPkCUN6X8hBLixaI~#bKxnT6|8|_lEX&43KM=0cyt2D%LWu<9 zvQ6Iy+Dsr;wL|;9QpKNs)wRR`R%ZYL@@aGUB>tr;70MTD@_?BweUFJjha`E=_M$#V zJ7|>Z?hh&qyr-`q*6@>1mH9;urzc&@NVL*F7L4)J->Ln^ThL9wYYPLvU*f9x>oy7OKzFu z6mK;o>t@nqJ2?4y-QN@7VF8{m=Xo{!N$PNUamVfa5Qfq7Kzoz@G#kV>suN6;N&C;E zF(6x71hHQ3Xl`~-CN^E=WTw%w*+6b3g{)vTRvdf4{}3kDsYTqg1#~2L*OG2WG}uAX z0LzS7^&YI80A$?)X#A?R+8_{ojyhnHc(J3CFhn-ma}kPqV73s-=Be!kR$(lmN7VPx zcyS}jSTZHcEl4YS2KbzSC8Yf6^EaaF`gf;WM&j$6Hw`R#>e%q(LhDF6#nCKsZN-@O z;Sunc?ZbX(Xe+h&xR%ePu4Lz}J)Ef zu{0s4H`$(>c5H3-Jy?HQ%KP_wtdHaMAYFPw=4S1tX{yTdVNE&^fHa)%h0D-J2Qk6q4mi1^-y3;nPs^XtnJt|te);yWAK^>~a?9_I z;4OIclSp;8Pw1(kx}*2Fqb% z9Bim=uzD);yN^Xt{APam8XXT_Ps&8qb|9SNR7!^P=Wed%%CFaBui4@CE5ynaXqNNy zur0wiZzAljo98Z?!4#>An~X5qmAU$GQ|$%(_eMKlvpGKmmmy^{C=k7xCQ3^+aN^p! zplRKwS^9Z(^g)J>f)%dTe%&d#$~O*XiT2IggRr>uqOr9Y43oUoL89`4d7fujxH)Gr zAfCik8f0w#iEVf`ed;jeXX*WB^>$D?4);F|cOKYKN*Waw9hBS)BrE}Ed}>GG;cX!9 zoiF$Xt!e;B%YR#5u^$UBup;#(oRFdPeB2 zrzdlBY;xmzq;UMMv0hk{RBU&>|6JM@vi~MP4Wl@P-NVt#tAXq*hT1(Jci;xl`9#T` zbfVE40h4*Pt^tPmI9>f;tj=u1@8GulkwBZDM;4lUzuwdpf|$h*2Me@5QM*d}@>Y3k z%lTV|^S2Z3#iYuOi?CKc27Fp)-4ljleEY1WK;__;-HR69*RicP7p;VW?vxA0@&~%U z8WJ*{QJY(`zuRb=947iNHD>V_97L z;{#%bDa!aiW<-&#D9N&TWduh4X}!4`zqy{fksxMeOsm4Pr=d8bXg%#14+e1!X}ZGP zLo?-<-y3ExS4EUPJsyYcKKPQn!rw1*#&!eS%z8;0Oj|jv@Nd;?T!RdT3`0@<4gW_j*Wux{u*++ZYA!_|L3v9 zSTI$bZ-V*R6M?CVcD{0Z<`a<>kLQh{I#n~CzP4XOI7=JATHWFWBG3<>tL2wrr}Uk_ zj42B>4ps|BLw!mMRG8V{nsCm4TN*uvzZGFV%h9u?N(DMKgMuuitTXW9c_`n>Cn9~H zPSQ1AwIoH>z7TAM7^;9Q6WP~`-p^A}(5?D;(DCJqH`R^=d(R&iXh68WLoF;a5GG~M z*C(D#nYa`Ig6tIGbI+P;ctt+Gsd50ft{T4=4C5!k^^c;!D3as;Hp&b1z$D-H2+a5- zl||i_gXDb=QZ(8~>4{oCZpcQJyWH~cI+Mwql5kxaM=!;7=BZgQT{4;ZgA@e9Mnb)H zdGW&8W$qhrLR6pE=UmiNN}gWt-dz6_NwST@=v%2u*WAP{jj}KJopZHqtqe^zIZHMv zx{akSnz^CwJ6&dz(f7SR%!!R6RF~O9&dRIH7;+Au{(W)_aN)3DDBr9zgtAto57j9! zvd!;4ko~GLp^B=0K0EKXRD;{#5?>)8n<%dxw7p)Dd(s#15c|u57%PY0rh@BimJP>jyTVvkL9$l#LNFT%}6HJy({flRR;cAFsw9sMt+zio!O?jND4 z(;fG4YB(^j=&gH<99hzY^G}u#|C*ZU_}>BeWlg>x@fFYc<6>-IiE|g-2c6*Q#1UZP zis7ccpcY(dAnmoyh=gI5w7%SP!`;CYK|R7%j0fgyHe&Y!su`Lqi5D;?7?COu1M1iA zV!(Fm0Mw?^pc@xklQY8j09Jl$%eFO-p#6-4P%i9S#G_fVhV~`I>4nE&PT)&g33N3C zZm^)Iq;O(cA0(#~KD@BVRUYoI!X_qWfOy!hRog;KEq8(lMdtpb>*hT4rkv<(qLoOB zIjdw|knnUTRM;5r;4dP4t(X!lI>>)vKPt~9$US`#q3)9hQjccGq_0^tQ&0qa#=uPJ ze;?Wd>8^~H!|L?Ikke)p+|j+e*rgMrh}(MnN%5K2&a|<{-Jan4QSRyOmNSo%Qh2`A zhGyc|lPw%4p}Mx++>1TQK)1wyUO}79e&+<6_x&>y_p3f3-j$IyqQe8amAv1lWu(i! zCt4FR?XU13RnSUBy!}F68%ojoA~$jFvD#U8@05~`+($Hga@*#oA|e6|XI;uu_KVOb zDXE?|`AM<-&fmVd94D!?E)_NUf;K!&+QjV_t7`@Jn*@e`?<;-d=)Tn16(;sc?wb#o zN`s|yL)8E5lo{dh9w?@F62+xU1RT~b>$iOuQ^!T949H{AWbe7jF1Y?qu8_#F&+h*y(DE8tLC z-iREm8bbfmZwjBkg)QCU4U7H>fg{;sPDp81Dh&BDyI`1f6|GudwD>}f>Q{tMk-jjh z$Z-Eb&pobi9*)!!dHSPV%+Ott*~!dpF^^6(F(hHoUleh&?Hy}CtU zJO=l_-U~h0_n^Tx;u!tm)BgkRd6vEVN^((Va30CAAUPO@<5gSfuadPA=-E_w2X`;n)2ee zd#i+Gr~iqyzp$DLK$^_u;}mIsPsBo7Jgr+#qDUDYvz4TZ%fbA?g~f1z6aVLf|M>@n z`xrGC*z>SEXJC5iZn0mG+VxcDIs`oxKGjrr@jCw6VR?Nw9-13SUoH@Kx6oBivP66nn4Z^dVCi!GY&BcaoS9CA+lAkN2XsdFkCON zf4kksiJ~E^m2wN-6K7!}O-vKL!v1USe%96aJ<3@=091_hI#0bhFwwb%Rm|gL)s@#b z(C_Nbo(;H%sS@P8x$gAa?)=yZV*hvi-Vb;PMyzPJFkB5Y#C$xPK-S`FPPKev|J($}=YC)L0-Y_qAmjX9#;NfFn4C*Aq`7ST|#>D_&#o-jetG>?(2 z6ef1PSJ3GYE^bQnoKM~6pR?zw0(o|EL^fB8);h=Y&&1P?6SSpq@7l9My%CplFIZ8J z)Uw3KuUTi5a&4FGvI=P6qh1MXkFu)_BCz*h${uoz@DLR<n#Q4Q z_2W37*kMk?enitS_7l{~nrd6d5mG)vxqXS2B?)L%N|$!Zw+Qn6*sif#0C9(z3K@_5 z5t*dO$P$?rg>EV9u_S9DZLIFuqb+}$ZwPuUT3;gGq!9*Y20H@q!Wj=D6ty1dfjHug zA4G;?dBfY>a=sD~^RWa#8<LKsmZaRm_{4o8G44PEJ=4Y!dFL zv3u6O*-==ACDgxA$iFH=UKm#p>N~U+nkwdhx|yVS;GktZHcpNlpnuOzM5T5ld=aVH zInog7miqyZl0Cjve653`d0ml-@)H9)$=Am?six9)8S>+ME-vk0MZ6%!ApvfCoOmNNey$w*T;ILMoj-(lbJP_7+u-|Cwx$65_0wdUyfg z>amHf%Rhd*fAai9>3epVwdg7{zfkn3oSkLciqiY7hx@TlM5GP4m0C3ZjhLq;2l*uh zy*`)HZze93`2m6I;-81uP?cOfm}*I@LD<$Opc(N(B1Ike@$KtzpOSs~cx!{TIpFv3 z5sX?&ph{LoRH1U?E@oW?@1kcVa(?()8rn+0adT2QRT%i4pIyp zu&0Tj<&acc3b^tpxeW;y#WWrI(^a-~nlGsfPQTRQW;ex_}0BQ39PcQqvlxyi13{wM0Nd24nZq&`_LXWA?6O5Jio=`Ss zo0r?TQv+ZXbqp`SotB*;avr>fKFBx$%?0T#x47%|c@^BgZO*wIqw?hkhfyI`X#wQd{eWb=ZWGA<u^#m>V~ihCAgAmu+Xp!(=7SYuDME z+5**oNV?VV<=EwsvP~*xiay~#LxphjV)8t-1IZ8$@n5;Hd2p)fLio1H zi9&l}(E*I%_rr6;#xe5@_?$>aT}+AeBQdZSXNguQp}Czm@NVeGrI{G^t~+-iTUh@0 z{N1Sna-&6K^?I=9g~~luk2iH&#d!=5c9oh|1ZwF{9hjFt@uU>PqdBpAl^Iz!^(~giN)re@d0_RTJA2A%&X{J)iW_Rm){Cb6W62s z5bH?-xZ!*M@nT~#)M~Y^YSog5>@sM~cDv!yM{jk|*Zc=Q$6(4y!^@SnZT2nOrh zF5$3la3afd=F0(X{M!YD$Ue|qp%Y?HdgBDQ%1-yVO*S=NdrFRmYPZyS{o_X5;Xy_B zLRwy?WO|53xhr38yD(PB-tU%SSqwBpwShyG4O;5~XDhLNs&HPR^Y&8@@gOCTgf;wnp5D9UrdE?oS+{e$FZ-+A5m&e)af+UB1lubwSmizEKyI@7@}7F zj@c14Q{cY(SZxpyYx`A?YGI8w8f%%B7Rx73W66BVZcU>&04wTjIQsoBMsxDu_Q!Aw z<6v1kxL$}B46}s|N*-^NC(dR7cakirOv{ZSkt@b}mS@3|FnZDpPYz(g94>A1K}J4x z$42FVN(?s`u8T^G8WY_WJL}b^rNQ<|huAf4jWDYrXW>w^mVT_|73c1T{cNR>c1N1G zL?{e=>E`-=Bb@kK&wQyTm*54E7A=_aV^%YNBKS{jHe}TdN4=BdoL)y zV3R*fxx%_R^uHe5bS?LuX?jVY6S4UopQOM4nzbR*Ye7P7EWk$nK>LK-wY{oHA#WhU z*xH0RW)@d*;Si33Dz!r(SkV?0c$e#OT8jPer&i8<&sr-TYBStoDpYB-)$V+`aYpGz zh*!Vammrplaw#A@L)+UxG1d zs(hfVeBS_hoTV5ij?IeCsR66c>xPJHOkR) zN(fHhThM74lfQ_wmo2e0dG&{L@t&pS5bFNtKBwl#!^mZcn*ZogO zTn9}IQ3(H}UHQ7(<{d(3#Wr&^ z1*$hDooI|!OK5|_W{qF>WoK;ar=(xApu5?l?S#0s3)<0K1ni1ti|K4Xv;JT8TK!a9 zGKVi4YG>D*+VmmyW$d|IO52{nrGP!Z3mXsC6)Da46vqqSbs_h|i_~A8mc(imp+emqAz_}*)vov~ z`EdnN4~@NwzKwa9q8VNG)Ge*1K%4KIY?cMLwxjZp^F%Eug;w>ZybYNn9p~~U)Ib+p zWTRjvfTAnHC8k=@HInml3ebAj-n|yp$0_!)qQlz1BzUko`NA{uo#hNMZsoehV^8<2 zq%7M&{@yJwp|}-dX4iv3rDaQo^>_JRt7bR1z%ZY@dO)R-%7v5-34adUT zia5q{-*&&m+<_Lbm7Qg)e4We!rPO!dW5P|_ov(km!|j&z%k6*o_~^W9x%;T0_s7jt ziwjj-N64-%u4GNyx@0Z?X|pE5l<@|xf4wkEgHlER#~c?8I2x8X{b`@y#L}z2&X_kn zc{};s&c4GZMVX*6Am@a;_p!IjKV;eJ^WbYdYD{$pXMWax;BT+)@`)4{ z&6Mf_09ME>$H74~OISjO0ghGRtT5XVNSSP+}>*ukIQ znA6?0CGT2aQ5DVBY*C@FN!Szi4l$~gFIQoWQ-|uBjcV;#$sp!`KapNFVMlpUelp0f zFZ@+mAuGNs$ScYC9`_T|rmqXYYY zX4hhjb1ihr+dSWxHvVlnT@sgCsWq{|2x$q(4rf0Ifj!*>+_jYt>ma#!VD^2HA3XZn zuLq=dP>kL^`XxqzZmc`G@Xl48L#i8lae2eXbdROEf;!eRp<1B)9%_$)`oYIN*Y3Nk z{PMAXZqr_{2n%kwVi%d=UGlk{4@g^@4pSWa?iWf8??H^IF!HjFB@ibhK^GB&GaKW^ z47Z?&tXj3O>2P4UXg4|)*CJ}5(}jS{p#W0?;l0>xEc#r5!>(xA)aGf!cTRsD*W}` z-TgN`81szLWPEgSR^?B4uI>`|>~jC(WG~Y>dbPb%H%IUJrc0uBYIymGa9EB2gf9Am zcFSwedfM}jqbHs{RGQZ_yMFGHmd&*4Ng2g7=m-2>k`gInINOP8VZXx$;swLggg4lL z1u2gyL6S~eXn1+XHK}&FQ0wZcd*awJgo|w?C>>;^u7ThYHqMj7Rc>L{q?;Z$&Q*Sm ze4}oXckp_^fk@MHqLvt|xT$E#5=f&fqf63HuCxB!{)!QU7LQBSO~GQtS=AT~SI%9A z4!xk!e_diA_*7|R=uh*mFHr7~M1lSDF^tq1G?eUDZ9;8&1HFEhGW;^}fADyQ=sljx2jiq;UeNl5 zcfcEpo1!zN0at+=pI&%fNnt8 zRc|A(5~alfHC45wMH+vZC5BbNmTrRx06L>90?9rF|t zq&CK2h6}|CMJzt^d^bT%JBg&Fc1-@pg_Ct@`w;%9SDU&x$GF^B-<~44sgI6c42*%%#S}E<)X}mFImxT%;iYMFu0I zsOA7AwHs@%?VGe~M{@iYx#V||{1VUAhb3gBjIk6w>-=>UUzOUldB)@%p+_6ql!}e< zN8Tyi;a_@Z6?6J`FG!!!sSsTxeUM~%Z%#MHy^}?g|3Uh=Xgr_cYW;a-p}mW9oVt__ z@2$uQnviw=qdvXRkE@onzj;VCmU!qn-OLs9u5Ybow?4yhp|WD$(^bo94^4MkN4uOd zv9Y4fjyM1K*?&NfWFRQmuN5Sfa`f8I&>f3tj=9#RO}?loN66PC%e_jGMwHD z>X_>&npU_SBiL3q2uBHpmDJw@!KG5{fXsMLM8v8V@b)?0MwQSG$*4?3tU812&{KlI zru5N|1yMh!Q;LKVUM|Ime2>k!xme*6C1I@uN6u8Kno`X{+a|j4#DtdiFs_X%W$*L5dQhhckYibQ=B>W zw*wDa?8^AFE{3-CgIgHmd*!Ljb>~f?@nQOpkR9ylmtyjt^5!7?ViOz`E7nrCbmKRL z=pFeuU?jResnfk(Q8EjEmW_y`i~8SZ$Ue**YRGRFXREZ|p7!&b;fz4{k$M`IDuPD1 z&B+pZ*`+K^%%)$P!|rug!tYquzvldQlo0NQ#UDq%W==T#JXmae{BiTR+~4<6a55o6 zljsBFTM_T(YZ|5#s;~Sa_`hVi$ZJ?mWO&`G^Et-P4jqTfT9A1Nj$Q4)!|2&~W<69+T6r6yK#gz9UZ_dQysdfCgha*EUBG zW#PT_hyQKaKWyeh_Vmna^7JpfS0lWNPNTQQyU&Hb5Uk8yHTqL{eYN8xxRwWa#JZ1=ou{jhn7Znx0*YExGiDAfGvuH-QvQpNjr+rmFh^3X0y z;jY-Dalwx~b4}uYl4ND5xCDK3Y&lz91+jFHtn8~kkk9Kz^NQFRNgB(WD640kc&!(o z2Lbt)$e@5Ef?o}W1WKEibNehJR_vm-E=;D?>yTIv`0|V#`@annckt0@H0OvaEaePu>YwTKZzmV3oj*hw{0wIy0R>WmY1jjjX#21z*<54|Mrz0V$vQmV~G* z-SroSOF7qG?m9SWGkpOSy!EOw&R)5EPERI#25Vy5n@gQtK!gP^lkuA0<4bLo3{{h9 zK+qSm6V6~^WSd?V(WJV*O()h0S4KlyHpX!J7t$&I;Zt>wuOw#FB?vv9-Y;RHE81(# z#KEA3k4MKK?{^oGniwU*lIEw09h!)KwdMpONmi_UUUHyiOqP8oplTtuQ&xpKzIplc zXf9?epN(vzWk7@l7hWx6Hgn!+HmQ-i@8zoFwG|$5Ql~=e4o&rq-SFR+#FE%IcYm*m{-x-#&9xZg4gl5aNQFOyOM(?9Ut_bSI5N95v$?T!MJ-*{>X6KNst~wRX zhg?F$rgfxB)pHoOsD+~np3R%t**CrY`?zOk19$yIMhaMjCaa-?e=vSgzh z9tvZ=Gh4o?gR$II`I?fU_zWx}mPC&@eT&S#!@ ze+TIFjpO)IpX+#Srp(Kp>%kgeZ`PhC2ewf8@?_t6&l|xiFOzkBj~d&z*Z7LJN9_jB z9z!#;h5xCNTM<%L8M+*?S5R)9K{oo9!*xaL+g-pOXJE&}&((Q{UR9anKvwYGI6D%F z_N@hPwB2>XG=elYVJQe6z>)?zGtndY_cKCDd$O0Uut?7$?;~J5 ziKnU{?RIe5-GqB`Em2e z!Y!E}J&xtxRmt^eLJ2BFJ;!>Q0;t?;z$PQoxBA?c3 zy0Iq(?}`RMIwpmCirdlq{zU3FLE%qBlb5;Ffz^%d9d}<%)@*vl868`t}5wjuMi;EtrDS%De`<`4(?(4ot2e zO1^39rv$GL=IG0BVVx9PAMp8P^J>3F)grF$OC5jHrrzef`tAn(D$vz&t(7n>*Qsz8 zf|jFyxAkR76`{U=`Tq%`bxClx^U4)(j_Ulj>hfeF$vD-~Im^c$Fl8zFa^`h=j!>(H z_(8vz+OSBX#2B;tqA}4n2VKkf$_C*Tln%vqG=zra+OQt&hxNx=meEx_U1(OB)qhwl z7v3%YDj35<%*3`)o`^%eYzbj}>?UTL=&Mxiv4NYEbd>%Br0ysY5hXGTCQy51p~tx* z#wZcc9u?}Hg&Lj+&MhrwJ(LWdM~<&Lpt%0vMcb&u4j?=Ht4!Te zii1}u-tX|I*-y0fxQC{h6NSY-m9-NWicLW_@l9Bhen$Y*I_Dg8k@UVRdDQFTS!vkQ zp{NvJhdVCPyCgs%6rwTF-ISE&tEsfR2}Kxj*s>owMcE{!c>ERg$K$MTMZ6z{@Ot(ub8?FjmWcYST^lBZb4Nz56y1c`40Z>OKuBeTqY^55F$&@3o!q@wQeP*S_&g2E@Cj zcb>5xCRAeU*^Nu4lU=16K2}jP_bbUl{7<{=B*!1COt2Ze|4~3<_cR$bnPWz3sQPha z8=BZwHZdI8a*%wnK4RF&-#|DB2JkneO4F(99Fp<{YOS*zJtUL!6T(^^nH)k4xx5QC zEcIf#vYArjZC-wm(6jezjNYNsf#r&(e5`Dbr8Vs+VsgyEDq(;xacZFBXa=tw^oGFb zEW>OQYxxsv9fj?G*b#SIC)VDNlET#lE?UqeP}kipks*mS#x7u^y|SG3WMI1Xi^p1uwr6oFeLb#(2{PS#$#Ow9*FI&gq_GN??vNNm3c`rjC9iLI^z@8zYUe6UJU{=kf- zW%>`%$h&n@i&bGJQH<61IG))aR_Kv>azFDHI|)qTh~IbVu?`Dvm^gboC%x>x^TP9` z`=2N-i@nqD#Tgbkr(sJhv1}r(0y3REqw`|QM?%ayGN-{ZDy*wSR}XPho;|C_x_%y2 zKqS3=N$sK6Bj%fS{`bsJIZAV`epE!-)H&KEx&QDExlS_t%^@kTeAzaat^#txi=AST zPaU5bY0lR}(kB`E$Rw5DsPuXX{Ir-6%ZS))rXWdQMbyE`F*kWy_0~bw{_{L&_o(S3 zpBzEM_JhfHa^Wg1z$h{a3|{^3m&}AWW@yoOisLcQew_%%f7Tve22?LC(^w`f)B;Tr z#*3@x)o>@T7|KpX)iCJ$TR=UKm*j-%Wla?+q>_TTGBq(-?>$u?qq7g+D0@&=1}2Zn zudz7v(DoF7!*zZWiK4+|X|SNa`+3ifIq`I5+k3S+^IcD$zgTDc{o{YM`|m|?L>Fzn zq~uhIRWF-?qkvGkf(@0&w`Y9Y8v*-J-b?c3$Q=?_sgH90Hp!Fng^_u}`(H^@Rj;Bi z;Xe7l(gLjHy|oRP=kH?{3ImP!tudRHMz15mo2TwaEz|1r=gN z>*{h@B<@N~L|ut4b6y##vDYTAiNFtI@{#>5D?$Jk`aZlx6a=9*sq9zZDbb}w`I_4F_KxIcD`;VPzoqH3 z@D+p9XIp=&^QK_ifL#QH4mgdh{^}|`=dSyub?cZYroZ^v`MrxZf*YaJ&Atv&NL_&< zY}1_KLeHbl`O`OyHs;-be3lQ^%}++=bw2IdaH@VXd{;~BDRAh`(!2LgM442Ky4ucr zNL^6x70|UJVY~cdV)~#LEQcL*M*jJ8pnr8+ZlYI4gct~6F5Cw0Xt`Wl5E~#AB-;X_ zf5Z(+#-h>Vw|UsgzBNj6>B5p;Fat88PdT>J`Fh!wR&4^ga2mK#DN3KQMBAJH@`HNL z>l;iKKB0epv@Fo=hc%qUQ4G}++-B4U6Hg<=H>Qulb-#F< zicj5`{GPew=oqG~$rfmxU8a`zC@p=?xs$Fxh1*~gHYJD@0?i8_OBR1*#n`550$;A} z@L5`7J-V}!WTTV9H!&UNcmK*9KV(q@)dehTzC!_oC}AKjL49FKD%&%b;W`bVfWAQRD3GX$QH+91U4%%-{K0U8??z4hwY z32A!?*bdbW20TscpcMv_AhWbD4W1EJsBFbtlGUD74_aPQDH5dqosR&@ufkX{pVgha z689LO7DgaAv z&Vm5Qw|gN97x5;R>b}x#Ll(m$BRh1>yjV` zUqCl|Ie@wx^|^uzsi%44|4Wb@@d^hxK| zZ!%Q(NZ<@=%D_^KUo-=`v^$X+%b``_dB0L|2kH50g#XJ37Ojs2fzdB5@Wva)g$WI4co+o}92!mtN9zC4oMTOY%TvLE|%|6>;V z-@i&fj@GAi-a#xC9cx82g9yR%fg}zMCwsYyPEL?462=Ys0->#e!E7H|8 zqeVz4TUlAH`ljLxGbWKB1}=fe#ZHAF#`+8S|4SJMjbb&k*zE%m5(@q)GY>A)N_8p- zm0_teV&FwuR09bqf|!|)--Uv#d#|s3x3+1H6~rtH_VQuNhX5Sk^3@RM|CgBENE=tGn!QRCJb zEovBs_j2FQ`qulrpMU&kEpzRC&EDtvJCEbL0ym$Bh2CHENW@}s@%x#u?)}A0EJeKZ3=2Hte%3kuZ2EKHfdzD5Yjt4y41Ku;EoTl}q0VmHvL zYh~MOk-6c;>09lN5FcFc65!|8URBmO_t9CkE&w1h-piq1az#8?AJzfgJEVydBdmb8 zUMl*3qyL7b$C6SSliuOM^bJEszBevtY(#F(e{*pm+g$`c)fW(-yUp(B-g~YaEHvznQzl zcLDD8QrqQC?caTE?_#fvo8Kv)_B}W0NL4=R5JnV`J*0H{%SA={d-PibFy4Tr+i;Lw_YP#*KX-I*6eKda&`&pe_+pCmIm;0A><_|9~^{q$nT>8 zw#WZsfL2HYsD%Mz2}79~_h$)w7f#<6DQ;XAn_t`_P!``zGViN3H{g8lW+|@C5ba3a z)w5~R83{^g+SU8#XR+#@=Y|BJ(6h^aEl1WgfQI9*`-K0mfqlUD#v-e!K$Dj8j$%O0 zdPLMyo%w>CkabU#U}f*d;kRBIE11oj3)q8SzwB1v6lQPz@eaz`69il&_D8^>&YO@EbQbezg(pU7yj1@RVOv#!6z}FO}An{x<_wE*|d)76eQ{D}=WL5(WRh#{m94 zxcS}w0@dAR<$eSZt@`zKqBVM0NJ4_vo2#*uM$|Ln0+2{oE=DE33vM<1ot^*aDGbrRJddD>X#O3A}CkRjdJB;daf$U|>s4Lq;m03K#&(vS2V((w#l zPOhvGL=c#!A0xZ+zn)Y&xxt2SV$!=lyXW?ye+2q%&zKK3CL9p}rJw!}b9j+on#l7s zTa}%s+PF=R3`hnd0VGS9l6!mi8yFnk-nEbw3vj%d^s0dak{0^Q5P_&QEob%r{VJxc zRWM@7>U0G97>upJ=g>lat4I7|{AAZuKv1yzT`bKjfl32O9rm64_EM_1kA^M1y&C}M z8quuux^jDUwbm);GuADGuSMTq{=a`%MHaumxM-}p`zW*11*f&PTUm3g7NGKf4*?z! zPA34{>yQ4Hu*vjLeN(;>K&-r;^q};O?YbydO|xPYA1ofI4KA8gwA9W2WbD#bKbXoj zbkz@QxvkckxSZ#!b{JZo_WEt}>%m`c7`fJ-mCLLS|Ib&<*HnRDFn#0H^}icGG6Bqn z<9q{;iIzr=c#~RYPIk7FllYB0I8e>SUrc`7K##d$4oz}lfvdgnpH=5PzT))v z^q$%k78X*&Z>FTYuK)3P@ZahFC9lfNEIB~h3NKRR6Uzr~;%!ra8t)CC8bDFsv`wje z4M-W0;@eBH5Y%{!q96GOwOjyI;SX4~0Q`JJip|XtV0K|n1|Z9|q=cq_gwe zo(Ni5(^sb+;3*F{W7qitn?!+g(v>D1QKE)NX~x+-1RLoM^I1lHw_CbsK*gk;B%{%k z=N@=druCd?<=fUFZ#?l+J7URmhB#hl{AHi914Ey$)u78w73}!Gk1RbCFZ)~R-{u(3 z2M%MxnHJx4a^wyGOoRx4+yr!uHP-#9l}UI0ppgo+-+e}|n@pHTQ@LvH$cmsZ0d8IF zisZ|eeZ4)%FH)iR^PYu%+bLiMd|BKEpS}cLD}}P%yKAW`DZ6w?yVRBRh@U9VKgXS+ zY1bS(WghmYSK1AF@JDNO(#;LL&krJc&y;Z9Pn2mVzDmVNdbl-mOB@juIJ_@y>?$r@ zag{)Sk$`65^HDy{Ub@I0kLx(AuX1A(fwulTxc3+6&AlcT|08{B2c+(&08r5C%+au& zq#%M1pgp9?ZWRWwm1L2G>jbWZJ0ff4b-0F~v#zC5?S>!rXOtQ!uheW~n>?!>R8lv+6MpRl}9qUR$p zmy-#5u2Z4^R*OupzfgsmI7t*d3RE4I}fe1;Dkn$r>y^`fF|H!?_gn$kAUz zNf^#uV?j!8^%WQeW{s3$T32|lSoCVm#?q6HzRpeKj4*%Ua{|>T9frV_C%Fbd%Y5`p z4lp37F!>bAyA%;gsLbo!aeZ2)5A<3Cbpn84h)2sb-0qbzeFxK(#$N#S-1$CWAOnH6 zF*|Wk0rVao-+LM8D;*d7VA7Yvn1m>obMgo&vSx@X)oa(Lb+9v7rt6J)Rxgo$U|`54nGU#q4uBI|_e^A;az+ zz7Z6(&vNtlkf|+%%+|+9Of>!*Qb)u1{0tXnirT74TE{7ZxK0)SzmK>mJFhfVY~tR? z<-`l4t*NaAK>O}&2Sh9mF8~R})bHmmN2k)@J)80k@Oh(cb&47c1`1j@~J*$!{&oVsG2QpZVa2(b`KC5B-?cjI}1POGTOV+!PQ6{$(zj0iC^71xvOA@f|^9yj-9~Zc|S;tV!iT+KSvkV*-^ifs@!ESJKV{C6Q(h#80$Z+nW(g&v*Z zB$Yl}SI((?P5u1SM5*SlVx=5UrxSE9uq-javH!(mPW&|h6=CH4m8T^|)VOleP@UM^ zoi(qH>Den0QW`hXw|5~ww6`u0gpYZjiGmf`u}BjKPQ}DbM(rYDobx#BOk8g-?@IS2 zF#S9Y35vG-ryx=3`1XJP566;Hh*2so;f7$OxMbMtR~FAbeVZ97xxYI5E!b=7UpXZS(OR?>Q^ zD6=^Pac>bmda2^Y0&?bYdTyWnxx*Qq>mW*}yw za2QSK8@}(^Cw7oEU!8qBkR8jGZa|M!(I#r^b1-a}VvgNKqzLG={W_4V!f?mUexZHa zjSz$@Hh0Ls>9>sI!0Ik1yK{yd->K*(ctvEQ!LO`T*%>*>HMm{Y}5p|eY)Yl^C~ z^YI-aZ6SqM;l9W!{R>asS#O(vSAZ>#CrFB)1eH5*A6?ip~Cs%4J6W~vqo zpa;KhEcpWmG8r(o(!$v{cz&(FHXS1vpZH zdZ#;9c4$<>nynI9?8i-xmA}BD?(?mvlF|9_oF8o2Ok!t-;T+e)Cj%nJJF?uHpf?4V zTVI9=P0|mgu<0<+Nb;Rz!VQ^0Db>T*+WQr0*Tmx5`8Kv8t>=cRdv6V=2fb6E@kgF` zAC3yd*al;<3=a`|%B@@b^gBAw+d@qwYh!Q)OlOdA0^DTtrEM#{c{A0E%lv4E$`*a* z+@3U<LvQi)R6bzGt;;#^06~r#MLsR8s1j2c^5siQ zJI(Ife$zi!y;@=JSC*R$qdzhJvNulr%EeLNiLs_ZU*rKcCq)xe_O*^^Z~mE6?VP00 zBP^y6WBg97^7PyL%KkXBj!4lsE2;XWxdz0Ot18J+&*=l^o&VOV1FD%f3QOt_jrT^^W-fSjWh9lC+wQZgZiYR~Vljj=Gz2)SPB`lMQs8X^=tVL`= z;}WuniI1C_nv~G9vA!z^(l~2tCg>!^5Z48paz8T(+A8rnI-3x*bDJfy1`=GqlHHNw zXT0L>BVypVMM5w`nWf$*LpQM_GVUp<8rc8 za*CB2_bl?r?ORL;&gICy2W5OPcw*^aO+s77Hh3Z;G&^&lrK%5DzOB zh%^kUQ_44S9L0v9Z!rcv$U2c@aUcN_eNeUpl(s#z588U35{Q#B`aom{B{-yVexWzH zH3NI*!5sk`>wcKp#PQEk95@C6X_BgKRYVAPJD`bj!tnJdXVMh<)V`k`M#d}IN)-A34w(f&S^q4`6e}X z`Cxg!tN-RL-WkFnh**Gj+lKl2-{yj_6kPY_+G7f6CiOGIMGKa6ol&F2&%ES)sF;eU zpd2we_v@($s>|)p;sT0^1#D5iXhjZRoXaj>pHjNAdEhDtoiAV7y5ChVim0KZm7~%; zp_lwJ%FoM;{(3(}dxy?eJh@=`&76`@#TJt|hhlcMB!*EE9pg4TP!oXAUia)1SdFq~ z>c^W7ngZ`Bbrd+2zeR^*U@bIB!Xs|nt74cJPQ?LMe(RDct7M=gOkO1i@!qiLEQNJ) zCmu?gdTXX1!=i%}@ApB6UiUvoeZCGKSQ<5YTfZ8!3ff7cRL?G#Fi;4Hsp%J6*QMtQ zG@mg)iw!7}8e^U0KZX7@ovVwfm_~A8bC#(`+rRjeF3hscr8regz6`<8%-dYU`@_ua z9nW^+&@NZbTedN0?`Av*PRr}*q{t}c#Kv;}xR)My?EcexL~|)3mH%VwULCP@I;@Nu zrB1)R5_)th+Wy~FYogedji5Bqc2F$KWAKxQ@ksq~Qeo`ZyI;7oJT!)VUTh9z)s}jU z-_K6)Fs6A0)C^5=T{Y47RO8Kr@;(v|5AUBMsxIg_3yWz& zZ@P?Y*FY~hc+I5xXGThluC}i!GlTnGg%jZ$CQ62CF8*9dX`?-pB^V+Ku7gdIgFpV; z0lQ^}#SuzlDtT8T->^01(uMck|0r`ms?XX-9D3-$!}m-EWFFyJ^}d&DPO>xrfSxjrw<|oe6tAkQGG8-IMQ?7E$j-T z9Z^uKiJ3SrUE*QFijY&}L3eUs{8p*r&|yO{kJ6w1GxkE>4MQm$TpQ+vzjFxYb<<`XLgqrx$8~uOTP-|Fov?@=snQEP&g^j2KR`j7Se(8&U3A4 zF4b>jaAjgTZS!&mIO!dsEZiU7LvWTuB?|^zq#qS5obX}36^LELbP7mFm|}rp*Bh%$ zWR?unlByzns{=S6NYPxfcgaXSNA0gKae!SOeDodKGOK@!55GOmq3ZVxl|x00DP=e?AxtNk)-7_`GYj0NmK?ZHeRlFZJNF5(p}r+IS_jQ{ zyF|ApZYQ#>xsNV8j~p{s3wxYUs)2T`ZdVh!bTQP3dV}6-r_`NwzqUEIzJnJ#R&;YA z?r(F4Bvls2`_C(j)h5H7Ewo%{{4)Bklj1`6klcF%hiZe`ug!Q`fK$?UF+RgDe`F0x zW3C#!<~MG!H-+%B2|(31)zKb57rHP}*5BSn@{wFF%(v!wTV z6PGEqOTDQ})87dH=Us|N!fh}#&O^lTuo@0Gh64ypslKq0=;@=i$bTz6P38FGEt6>z zO*Xo?g>~Rl6Z-AIk=L+qI3{5H;BL_1gwK)H&+n_DbpV&2(=Tz8NXTL14@-C!wpIfy zMVZXkzxp;f&Z+M3)p_{Y7QY`Z3onYqwDAw!nqpn1l;vfQd&8j7)jP)l%CQNz&uRz@50^xwKMk-~PbGN^je%uTi%*d+bpHdmp znv{Nho9}K2!CTq2nY-_95T&s2lBgbc5~9EHSQ2BqWMKk>Ozai}M>2AJsO2-(~uJkp<{_?gk}p zReFUATTw6dCiBa=UkZx}B=NrcDIfH`Gy`EvHh6tU!*K@P$U3H6s@mbvmkxf0t&e2A z?@tP@%QF|bXpiFSO_I;Argw^71SuGB1|Tx}kZPL--7_nVvQoC<0=D);mj@kEB}6Kz zU!LGNM^$U!nmD&=b&!g1aP9Sb_TfY5hfIICscA}7(}lL&Ug7y!XB5#nnBrlYdnEg1 zfr}*ij{-mx*?dbdk|IuLFB;IgokVM~lA&Ok_r^f@gk!5YsR z;j^VW$u!To$5W<>4m#{~i@~lomM;m)6D6g8I&uw3^+F4UoH0NAV)P8w3Bza0b-#ir(Ou^qs6?eHlc74aHyi?W97jd-IP zWY&t?w_M&(%b!=BHRff8nNHgMyI)7UCJ&P4xeV5X_Zt(HL zs3+#h-Ut!Zw0zqiDJB)%*_-tZ zmDCzalVwB<@hPl6VX>Mk^VhEnL3*?x5> zmEV>m>S+CYipHos4LA>*sF!kG(AtQ<5o~Z>eKZ7%XV_(UOqvk;zCWxb$KT?Pn`Pu3 zs3!%u1d2wF+sGe5`UE5!BYS#_uS%L4q0+2Ri7e&flDHsGgTlQ$ELEN}C%iIAN7>$8 zz7s^95V-}F7eqSyppe`E zSOFh+CON~wWPoYf^-yJzA(1&x05gHo_@hSn=J(^e06WH8G z-;^|=^$jshFq%A*2~&Vh@zSk25o)(=`QFL) zF@LI7@2~IXP#d&Fr5?$Tkc5GV@SQ$UCadWW|J;i0I_B zOfqrPE>RGHSC+|;8$cAi9LuHTt0NHNx0jlIJ7zb_%7=!jdY-P2*=&Bm|od7%r**{g!saPNt9-8N;5;#}qP=_gN3 zYYa7c_|NWRb84I=w!6dvA}>TnTxp80uyY?JOQ~Z|z-N~A0bhTe>n$E;w(A7i%pQG9 zZ!QIrnG{Sq_pCkZ`-$f?G~bnvdcgY*s5X@_rUh5Y(Qax>T1c<j@3x;Z{~~a0(2Zn2 zVz!;!{;o_QyyMwrz5@x5b<>G?*6(h@fF9rC1`-~UJ1^u4BVeET_fhQEP;c$ik``ybM&uAO1eCz`Yr9WCK0 z%HEvN?`~acO_55vIK0v{3QEOvBqSsO2C2>Ko!`U0;^J(il$JSCP3yozgW%d$&0BW# zAp7+%DlI#~v@-FSA~*N7qqLoa2`2Z7#jOLl*@6BXGr|%fbkQgEPU1R{N(*EohP?a9nEcZC8p%&phNyeYI zG*w~kmlV+R>b9vLWL{s7k-z`_nRPVv+sQP{M@*X|PoqQ7GnOTP#NA0Ir${A539jvD zJDXxCXxrGxQx|Of-d$OSOO;F=AyT3Yg>0sWC^qVRI>yf?2XEr60d7g=DdR%{zCFaB zxI3ja(wEydp@-rFq9xEX!H~Xt>wAa?u_5(!pnhA_7u?p?TA~f$Hs~TE$*B93TVHzObDt7RmJjtHmL(U zDYsQ)k0QCxf~;v*<>)T3yV4fvZuj31JI9X;u?zLzlVDWHMlR9R&M0D@bnsm14Y=c{ zr-_yZe*T@T^m_2MUo81Xp&AxGTIXC(G+*c+GN-VLKjVUMLOL0#=Oz(0P^J_w}k}TAGh^gT?e9h;VK))Qk zNrO}<*xR;4jdt>Xgnyy_^gP3f20qEXD2~ zAHj@_KtnjVF1*@F9LO(F!A*vDO;0#+Oelzo>+iE4k!S}+bMvOZhzs@JSpJ?FEsS9w zS2sy^yQ>7H35mK;38KCG(ZS>&#v|9R{$7RwoX|2sLwyEbrLvMGrdJzxDRGKJMXuS5 z;-Y0B@9H&7X}+}L<3L?WE&K5}Po+h?w11qh2J5dV`K*3e&*;PBfoY#q(zQL9E-sZ~ zr(ge7$Vp@Jirzn!qzkFCFXVV}7+QRVblf6m(Nnl)qH`UuQd|O6&%9h=lzj-{h7g0V zRNBkSj9KadeqWpqhgd%k?&E7kwLHGE5%;(TTF)CY8U6S> zX-_@h)hK`bOD6YtaKDngs|o|(ljHLJ8N1MXbDDcyfAOiKuJw}hc8Sl`L_kkwU8XC` z90&q;g+S}TQH*t-3`&M8Kwu6lcooZtA-L3#T2@qmfUQG(VJ73hC%N{x??z!PC4e>8 z(>(J4og)bqp_DqZ&abRm6#&kFADlx*A9nG{-Bz})sDMa=i^CG z!D>4xw!owsC;%6OTa4(K0Yh;5Sv(IeR?}(phc+)bq;giR`bL4?{4?hz^=j5|WHu_s<$zOh|Laxh(1c%wp1yWQ2B|vPd6! z+sN~)>ZXkGxBk^UEba=SC6X_Ax7cVhC)w`TPpPb`6}+4W*0Io)-WZ*<)Z`Bn4D00& zbrl55u;zs@;)Og8>@35(aG2YC-@L>37tyd;haKtmP{Z=K2bsw!a~}bB`)YdQ^eWE$ z4$%K?{}a%0U4k*&ap8v*{}#EfSU=ZJO=w>ENWaO1PEv{bN?k_-KHr1+i7MEOp}b_x z6P&Dt#=8FNlESyjuRo8a1194iO>C|?k#yZ6!ka(0#asIrSxjrEq1hh1**$L14rO)On;>(a2b}k>p(2(ABrOAo6ye-Szs_dujb-aRt>c(fUY?9+$ z2b(#lhxmob^ErrZE?k(N*JK|Rn8)#RHBo?9V)z@_sEPMO2OoSh(F3Q&h_RrdMHa@* z5wD9_F(Fa1cSQC_+@o5X{xH=F^H9;_An zCzhe-2cc8u=y3O4673LdX-q_7D-$kS+vAjlfIq4Xnw795f|E^Jq;hUeH)lpL^}KSo zs&z|$;U9*mtc&eHU#onw0djdw8+I5IN{en|_ui=*ebS5Ix$51wj>KJB6kMccbuPo* zp3YYciMlW6px*RI+xYr~2AMWNJ|X7!`bFOwx?&0EUYj+I?L{MB#N&@3(LySzL+7%8 zv~i}9^y3HXgK8NWcJY)xmA&M&a%JF9nph4@39f8@rU9us2j38bnfBiWc4?c{5`trf ztDhY+ni%c_pdOxs*()u0cS`nX-pY_5$kDuj$uo{z6frLJQ3O%rTp51Y?1Bh?RuA|wkoJX&q)o@5sX_v25kYauH$Bb4_HTr43ww_X3 zU*nNVvXJgn--!&uX_Bphvx3+y309_xDq3wodseQhtvuW7iVj#29&ynVD*Z4Qgwz&V zc)x0(`>>7FZ z$sfUBS#|VE(gJiQULW{Ags#(^-7opC03pHw47OC&O8#t4EAp7cAKUn(c=s)!#PN*r zyf$Ln4k7XPQ(sz9*P)Zcw*a_>Q zt|cwTFS?soc60<$0h^jsY>&IhpSpFG+L)_6>XS|w3$GqI*=HIxk^<+M)NyrLTFR46 ztk=%Boll59cM(wsZZIFLn1Yt<^huHiq$k1adJtjW6}h%ug)ig*%zl2VclVG@St z98q!Lo4-*b2VrNh%5^(9v2r6`h)lxuB)Jju^TCY}uk8(m*;J$%$9$8q;t}r#|9dpJCf8mf)TRe#+VG*4 zXy+4|n15sge)7!*LcpLnlE!4RxDfyCy^2iMWaGiLW~CVm|E&ZjluV$NslS42e(sog z**i=N*tBR`r6J9J3%*pKF~nyr{d+!;$A=Z{pxpZQ`RtgDRG zIMq8%avh$ZN)u>uxKuT3VbOgtb8(X0lKg$;qh{m%pK>#KVut7lVx40KooSx@n#sERGG1goX1{7gVscrv9%J?YytG?(jo>>1unK?zt&6p&0 zpTsB1?SL}Dh+-Kx{GOFhJ((cg-L>W;qYZiln+VFc`9oyN@Q)w$4IIk2Z`Ni9!c0o0 zk6@GL6rw}WoW2uzuqA$eb0_Oh->caE$4Sbc*DO;YY=pruUmx*=I@S96L5rB;F+EGnysilK3R} zg2?MJ-}=ClVmM&I!1tdeIYEPAEoYyZB1e)5wLb;0eUGzuM2*xSPbO14zyE#T@RPTW z$|)o-$Udl)?-B)H#;RbGS3+@hBWK#?Lcp@&r0wNIAcH@X#Z?2x*}hg!Pt_%mW^;wc z^xczht3-p5^oPIq6pNKqarreVlXHIUL}CJb7PRV|-bqUDb~gkyyh;=;Ii$rV10#;u zE9qk>u9qsU2NND4dxfhn3pda0^pR`OgpH|isEY-t@(B~86N#tEIiw{l}0<0#w4mgAy z&vQldmd0s*&r2r;{@ULs{BnINo-`)aMTU|Q8`nc{B5kx60*LdgJ zBznmTrP{%84tvobw|`*fL-!MCQp;YRI*a{x{hJs+zZY? zb;scwsj9hTMl3Nq8h`!u8IZceBTh3=)%oQ!xGKL@GbE6B^#+nO{DT0jkIxMy_BdqC zhYpr`-tFt93T6g-6*qQXA^pZ^St3JQBmVTpQTd)~nm#(#xJf0^MShBws*Hw3tU)ZP z;#!A>>cy(ej{cf%YkO?S$4L8kqeG(2p(1Cx;7t`HX_3aD4+a7;N@fa(%Hk_W-C$X% zf0>QJ+Lv2~!^GvJO|Jgq{~x3V-lO`XD*{gYNA37VMTBhx=Yd@AH{- zdfJcILMWpphou?5#pkljez&w!2>yJp6wP=7Can?=^f5UEzx$_-b-M3VE6lS39fj1@ zWJVP_`<@b~n0i0>g>tuiVQ(@nWiJMYQt2K&+AmNF{9UeC|6<3Yl|gTOWMLuy_l0S3 zY*T_P{e2eU-EVMoh&Ll6Hvw@?;`gMdcXQlijJGykXc`xZt#e`2h(k6eCPdM?b?SU& zOc>h^YSlJ_z4r`0&-$4b7dYo5kf(D8&eW&P9Nv|NPPsfTWV#w7wKKV9vQ6!Uk~Rm$ znCS!BQ`u0-{>Oa+0kQbj=p?WZ>ipXDz*qmheVP!ZzOge(j-F#r8MA2cst&p9Z{S>< z`T5tUdK%5auI80mHt0uO#qyAE|PVJzryTqaejF%BNE4$a8!acooTSiGaUs z<4WkI_QZaS7Sbrm97?_f%_>}mc2iG)dDpg10F6_QI)uKz4;` zmj3==U16aBp{h!mUnX-FOHYXFK)`^{EdE9l${Qe0fJL1?GsQxk#2wq-P)*f@1~|)Z z^^q5d52XsV`Nqm%6n}l)@YOY%+Dez3cYXBJQ#(puR*wo+#Ep%@DyGZE^n4715C0$& z{}8#G7Uy#@8Ki}yy6^cUOhd|NQ*4PUp_BC0>yH9mtM$QN6X7%Tb2+7cB-brIw9*7O zdQ9J|yv1GpHx)Qna-5|rOJY%>g;>bu(E0=Qj?}V)8uPj@&J=GRr3h6d3kLJ&jP}kQ zrRYJp<;*uqxU93GjSK93U$U@h`-u1U9G zxvMbyoH4sp>a6`G-2Ps&QNXGQnpXZ$Ar{9MUY#Q~pS18OmKWNTu86<X+IHlX%F9Nb3;oYxDR^%2A>aan1VqF?Q6y<>e3zX3*?VKt5pD~hwey~4_RI=H zl2iM)(`+}#I<4#SA`UL8*2asWpTPH1?o<&7c60_1xsDT5fP4LK!amxDP0 zeokyu^KxVUORWz5x{Abk9W%6gK+2g+@=AYWR+?`S@sr-cA5Z4wh2CHYYDk}g%F3A# zvCLB?Rn-!--Ra@p<4-8oiNfn4^TddIsC%6?9}cE|u?u29G+PH!b+kGEKC$9im5HPi zv2_*ns(0#n*M_KEcTwfj`vk9N&*1m^(XI`?I;$DI8X4?^YG3UV_wy~cMTrVeyD_Ddil(z_qU-n55U0pbZqNY4p7TFipWvh+_6k{&{ZH+5*WL6e zC)Y792}-c8{I+bU^i0r8)a5IIBudjcsZMZL50$YGC?&Z@L=-(v-aBpqqr{<^{k`wD zm2)&kGV5dudKI4sk_tSHl-&+(9cTJ(g1=_p^@rn|w6k8wE>}c4=9af@MI-wp;UAMu zH@ANI1LC7Z}D&)l16hI+w zD#0_&WeV{OD@g&24-*MZ>Bd=`>&Oy0sK@&wLd8|oao1I>DpM6(t*#h7A!Q$~$~dHU zpmk6!j>5jWgQun3eEEjpa;W3i;wxx55tFcB_L=+#d0|GQ)YXWdCz0i(9iX_z_y-%S zVspx81iL~)$|QG%5!$x$e>im#16XB~&J?z!J7L%G!HxTjmbEcgrD_I63@Rkcan-Vc zMSScJuWlo)j?`kQx4<8#fX^;m5JT;OV(C(LUr2n@U8uPw-KhxVXjZ7l_RP=OnRi;1 zRsfzJ4wuDh|81RQBjKXD7a?`>1({n-2d7suP_*F-xcfE+X-1{^uenOUb%k8}U{`E~ z%KD);V%;9xN59vDBz-_tb-vtwn`v-|->S=kJSCW@*RiSrR`PNkeH38<`IYQxnCF+# zBnln+o{}p}r!m4G*B|#k-R0gghb>vx{I(4H3@A)7+Yl&Y_X#YO1- zHtjH4%hkB_m^ivk4xfm%b3G}31{WD&7)}ZJOphK~JnWpIdQNxJ^T2A(Xm0D@+yLj$ zFLR+t8*IKe<#VjjU*(hxYkDCqc0R8k=gS=AK!Honu#u`uxajLp7A+@r|9wQjzrk&Q zW6m~hNLMA2U~^R)c)}&&RG%8~GaWdF>=n~C$@E3q&!%!Rb@V33?g>f-C~Kye5OrVW znEn8;b576Ha#txA_B=ug*1dN3r_EFYY7C9{jGESx7@}G6igc=i$kI$6d#l{cu6xfmRO=d`PuC9p&~xQ ze~~aMs0guj5TVvCxd9)z!3BPtxe7^9%&8}W(x7@hL&Gfj&@2y!KFi=RfiohKIvZw$ zc;V~6cu{4B=;STA>HL1|#ba#?p7qTaF;k?+)xdi&!ViUV3(F*nBKZY(Uyf;viXXX4 z5`?q-0b5MOGX0Exh>4*_vgbJl&+4w#ogp3i^G2p;W20MwZvz%OTI2O|<;dh2R=%6d z{<`h*ml-!Gw=r=d)zMGyW?=sb?0E=vcD~Obt6NH%{tCP+v?pC88vG!Df$dAg9>*-> z^^#hWBL8YQ{={wiTAS6GVSAjVfY=ZLs3*);!dfr3**@!se!XoOR8HZMbPY6v>?t#$ z6GNFfPRq+f*(T8U=!h+gxg_@*IIeH1yp0^$+GR=jCdPzJEl4~8g*itX9;#?|a zd$d=7lKiGK``}b@`iHRLkV{R^-?v^@2IV<=V#HoO2NXgx=jtWdzkw&Iko`6J*MH-u z;_Xy<9C1a*e_+~%S&AN&L|zRPX?ikW)qjsa2BDJnW2fHBt&Gq(l3F#9<&*SlkG0^f z$Udqux!LSrqGs72CMbkt+9xI9Do>3v9u6U+p9(%|{%7NRPP7gC$8E0aA^c!A< zb=?s>@?eI@rIS}71@TQEpk#_YOc&I@8%%m2+=6^7BH%QWc;sVVIx36icw1_d7!@cx zeP(I|8@)4%xyn<%<;mgcZAw#{f*1O)UZElOS>L;e#EP3PPf&FFkbD)ccB)tm3rybL8GL#E^h3jirTF2(pss7 zL(v-V@Fdh~PV7mL(SVRTZT96(byTyL^&9-NRw+%j0jn%v+zO8`bNn+^-`|*#~!)wF8@U| z6tqU{IwsvTjd$tNIc~n`%V?6XDM**el>q5yky{;-}R{Br62N zU~}Ad^NDwayowvf-{-4|g2$4aPvqLo)pMymCk(V#u^6U+pwo)=Zw7mHsjK0X8|R;) zGot3(Q{Kac>^@jME=&H7aq+O8>pv|lVj|IN|K3K0__jrxI0^^WopoT9hF$Dg6C0fg z(>yHMihc6;e@SioCk=R17(N}TITqLH=gXa-lk{&~=>=!)G9|MWw`s5qy2hV(zQdyK?xkzW9{MRtTb>A+CgbKc=P z-f1~gSH@Ci50gCpSVD8?J>D`e%RX-shY!*EELz-r3+K5`?v^n#VRsl3jK&8Y)1vL= z)g9&|2zDQU4bV6 z&5E`o;N>30YopU%hEO+-*-G89w0N;j|B_QXZeU2^!x2M?j_p~gS~c5@CYZ(;6>8!DXUsQTT3m&**fP1jOw1z) z#hy}Ttwd^qlH%+Z+ldDX>Qkd{NsS&##=O(MHG(7p_mTs-Tn%QT$3x zHBvx}0waP2fi(04BC2TYlBJQkDUV#QERDMn4}frSLjp=J8yyMZk(+mmvB};Q z9P%u!bvc1SqP)~#Vl-(pfql#e{+i~guynL^z$2Lyq^qO8DaXM*Cl#^rv~mv=>Lo~k z%KGZ}c*z30wOQY1$YnX<#enL2y$c89MSP1^ZQBNsVIP?ew-QN|s&eYAT|?;-#H^05 z-&>-s+nv;Ob~6R-#4;o4G`;gT=B4$5HoSf|uR+_HD90?%>BOen=yD-1V9dp&FjCCb(0~(6?KYP4!N?L+p+#FI$teKpG!zhm{ zl3N#UJ9Qfhc{}XeI&ofYTFM(D=4%Tcg}aKJ-Pm$}HV|s!!V1^YL*whH$QGIuM|ed< zSz;%vI!WKCL3y*RhMTYAyGRFfXR?QOohx^LBx}|2VNekQ(P`%ujW&Pip((S~WrD61jX^{Fg{a7uPretV*w7VfxX~4|sB3uSM zOWJSGhU)jL@-6XqEYz1sv-g?H5YS~K%`F3?Dyi{|!f{FEf2w4movn*6(7VLkhvo!= zAT{>d#PKlT@tLku@3o4_&-9{ejt4&cTOqmp_Ad|28LPmsYNTu2oky%gIkFqXo9Fkkp8l;ImB#YRw!~5*<*2I*pVoV_Cx)we7lj< zSl;~eEIqe9zo*1eaW&}7Hqd}>h#1lf0_HOQ{wDEANq-d}f2!PqLWHjS48Czl_XxH` z#KQcloiOaDC9BQOi5#QBXUYL?#lFS_rxQT;x{m6STid7JsSxg8AK*;Fb=rSaa#Y>X zJNNC22z)Ct62AAfq3H3sndIL#=kNT)&*C6~=_LaJsKG;R>%rUgZ(EqZqvUyi#8UXL zANNj}Xm0X#Z4mV6Tk&yElc!k(m_d6vdfVTVKOm4Jn~AT)L$!c_uh;|iUh~qGBU{SM z%6G0x)xn%%Lgc_|uFBx0!$LGPaddci;FQ_y!pMf>vv~B{VI{Zzds<=F4-rS8@8g>r z8<3~s*}^U=Y*m#?IU24)#j>H4IT{}ob?-72i1P@3(nu8oY3E!oL3^6ykEpw?4Vn4I z@j6J|IXG2inJ(G=Qc^5vqe_0Qj;gp0%A4tbNML7NdTZ*L8l1{&(&RcBRvx9Xx$_&G@1J?@^UPe{H|IU)?7i1sd#!<<_(l)E%((dM zZKr+p>EhSiHc->thBBTQb!YCpguaA_cjnDVZ%X}qzr8=ER}NoG57{* z-C5WEi>va}Z8k>vyGv|CR|HePQx~f`Xa2ee=b*QUE)P3$tCUoYWT<7%I9vxAf)GTX z@z~8H`Gl?8siWEHyP-2P-}F80NNFYXV4OZZpyDQG?O& zQ%Z&KI9tfGOA4f_RfBNipI_{c@V?;zo1`{8n%}s3VQ*Z(Bgh{|5^wWYHw?$K-MQ-%=9*NT#otL4?o}Q<4P*LzJQUN| zRqrJ{U?0PwyyG7$>vQMCuqK(QEQYc~`}0pNWke! z0qF4Ev3lb*&vg=)V_h`wuBEt~+xZgm6RKD+5c|0^$8BxcTRSzaWcgK3nr)nCIai8R z{^grd%||5~HNtOn$GqF$INr;56L*PQEB`gP`!;4QV_HPFTJQR_bA?~TMx6%xtI=t@ zlb7WTmD+{Kq+D)_pYn~K)njw|jdPcswT@_uZioF`10>9I`Q@QCb283%8GjO~Z&Vh2 zX6Q(>xf(GO47=U1G4k?3h_8gUgCzByUl$2qdq8>Jwm;SM>~ieEJY z5X99K!nYEB8?MPo6}n98)_iQ7nr~#Z*}Zjj{7JArw}jKMTh6}W0Cq5c4ycIYYU>OO zTN?_?xrdQI=^ZBZ`W0Wc^t#DwEAKuQgFf^aDovq|yaBxxtt2|jfw+$dYwx#%E;omJ z+~p0VAJo*g6h7Mj;pQmOGRqHe`7kcwql4a%Ju9xiI*U6aKFq=SoLp%cp_v>65=}z> zu#B_95&r)EM3D?3GUq)7dvKJSkUmw$&zeam}3Sz3D4xGYU!QHPv_`(As+hpq3yrA18D9DM~-qgXUEZtS$Pln+%J=JF#we?RGL<+)2c2p z$?&m%Ex}6Y!3+49z>=d&cG5=PTzS#xO#s+&<#g4VAE z^n4(#c!>o6lTG}TwQGB8M!Y~c)3qLPPT%s$03ccS-TPP32{lAyF69NGJo5Molk1Gn z_uoG^!iOh--iF0Lx>4b@C501%(J@Z<4}TI*6&5@3Wb64)ncRRQGfl2ZA*X^Md@0@G z8;T*`kcY4nCUMsfcwChc?#&i^mN!B#F=62B4R8(6H%kQ!>kx z_zwL1e7eIh)-b7qnU}Vh&x7o*1Y2y3aR~JfXJH=rQO(ccNo@}E_wH`X5l~0J=tErj zpGZr%FtV>(Xxbu-IQ)f;Z&~TQ%G)|oi{FX2+T0&KBB-x<^X83Ak12Z{ZMA438Mea; z*`N+V$I#ep2Px8d#enW5~ogwIu>Ql3Xn zp)hpZub=yssee0T$NVzlRl@AQ^e@#n4PNR3L(vJDb>Yvy=|WDB)FMA4_q!CIg6J&* z_1$9CnsO}@dD1yYYv;=nON#&>^RJ6`A)5tAYqUdCWV<<%6}|&)s1}dbaHeoRAV;!6 z$p99fMTFrBlVm>s_Ce$AQd{BMckcL0Gdco6CccJRaQQwGm3#xFu<;J=pJ1gx(YVg_ zxRN`Q^-qPO%W_vy6bjLmKJxln8LF_*8aQg5!OVZ%a~p>^Ze?|_L}*OVMdFqRaWKcZ z+Q{`+@ykGQuY6Vafrm$(sLQH;z6a1a8w|T47E8_V-q^hDYIW1W@v|74y2{xx)g zgxg6U1;w%CZCPThcJ@JaAsxfZU4W2CTC=tB1F+k!BJ1)NZMXAO}2^s)&vF?r>df?TofQ(PMk=Cob ztGfJ(J^oJ_(YVM1(@{k+Q`~=KiApI*3D7@S?Hl_qOsHWtCs7a4nDDT$mRlD5B1gh0 zI31eV!@@XaM#@cS>wpIU{#SK3pUW@+=EA(!c!h0&e%Ssc|C+raBPHZRcZEP}NY z(w9;!!69Ud+d50#uu2Pvq8UG~L;|@)eG%I_s)j?g+tq3VT1U@dYkyC_{O>`6+*a)d z46qDw8$Fzo1{GRkimv*WedDhm2aL5bV7I-3!6R|Fk)-(e2UA~5imxr21c?czTZC`3 za4k6hcoam8)b%U6#PudI@tK4n5aep`bWip})|p1b%i8q zesYbh72Pw21nu@rso_zeptOBGl0kEutU79_KpAP>XEiC0nL~72W?*}Zc8&#l+WHo= zTLQAWS?1ZSKe>S|$L_G0BH{j>0X;?5aD|HCOGCTMO{%*=Vx_Z1o38Hv_`dwJ>pa@T zQg9iAZ=!e$db&nSE$4O$O=^K&nq_Uw*McBuYC^)(xESd$A<2x0ZOB_oehCRr{|8>P z%|Wha`ufB=jJ4Ek>-k`SeJqq_BXr<*`(H8ipMTIv4@DWpQGj0A@~B-Fv}`fcs8tI1 zTi7YM%*tcH)yTil5hNBzlfUvregRiJxUgMug+YYu#ENZaXD1#D%3(ROyucDB2h0l_OH_PQe~Bdix4S`F4FR?QH9OL zhsn;5ed;+c@n|B~^eyZ-Bqp5xckTT3;3sl2I9CcRxghPK)U&R-0D6)d=M-n$k8yjm zPCFPOD&Wk`eJTic9yrmf`=LAapH*aeB(sj1*Xd}IW3>jNYNjHp9KptKmydpoBZ##oYtz|_zCXwf97>Jap=oo{TOFmFE1b2|mo2t_9@_~Xp)D>k0 z2CH{c4Wy7|-Z7;WEqP@_e{a9PA9y*uOz4CAXJ^)%`e5Hos`wA8{D6ipx4#K8^*q{L zwg`=+L{X4Uc_~G>x^YH}H_#7C-Oj$;QEbF=jwATExmms-W^J@Irv_YHwM{#VJ$jYX zUha&hOW<{fQXI27B zN~h&aO{Ln8;0Apz|Lr89IeBhwP9*F0NI@VsCcrkN;Zme{Ys^k0D8JB{*2{0-lFGs% zA_Q<3E}P3GFmz`nh_Dsr9IGY<%E?tbE$LU9KhDR9VM`KkoLb)a7)}MGDR^*{%dBbx z)Q}GyP+23`i@Whi5^23YRSyK!2)BuBYln~ij$a;EG`H&_Z(+n~CV#cqWG_@_x%%mxsxhP&-raZ7=F?E4UI){g-()t8&~)T*gVK}cH3nJ@0*ylIda zcqBE%wbd?bFH}JI%Ge7b4V301{+|wbHQaxGpKd@|c?d1vtGjkm+bid^a5}9~3`i*2 zBYZNL`(vfutGcFUK$i#^!n9(tBw`lCgD)y~2l{0NXu0~F z#IZi1S)@SAOAj8x;eS&<)?gq7Cw%2FT<|$sAP_wocj~T=|Hk6W5SfJXGb$5u{H5${ zywi75Ie}CSe4`JXY-2<(*i#}HHwaPYm9h=$;#jbMFiFkH!1vl}nCFIZho5rTQh{IC zc>z`~QLqvoEq3QMw^z@W2EyPwY+oehw-`7E7nHYGE_mVGw5G!)kF1h`z^o1B)c0X3G;{CZ(co(2E0_fOF04;^j&n;kwBZSfgICi?drB;HZ>?gB#B- zg-lc5$pZp`hIeIeLg1H@B-|@>XEl^(pBCxbZ$cN(`OAKx^_Nncv)ZN)8H*MA_T@o# z!K(V{`cKcxT z39f(;kFyO_-tMN|3Y%=l8y_o3Zh(ADNL)xrw=iuAoF$+TnKy1%TfoDR>_^bi_gM=^ ze@-FFF(-r;)%XF+lEBb-T6D1!`L+M6h=uU4uX0xvUVJ#ey%IKokmYC%H>P~vrd%{+ z=P&dgZ1tM-;wrqBt{Pa=BNqiIJOwB6ZYC2mUNl&sgzVGs&#uf;zC?Dz)U3|6nCHyS zH$(#v3AShw?#14Wc99L)gegTEaW{SfZa%OxR+Q^6wlFVFE&gi*-;ehQ)bPmxv53Ms zwu1pJ+=}8P=||n$-nl0VV+2;IH!nD+N_vW3|7(>ABXI`I_GH*n8pFyUv6!-`SMx?s zC_p1a{ree#x#Q!yV-o`Ky|N^y5!OIt*M#HeW2OS6ew}+aY4&S&C5}Zcaz=%SxH|vS zOTjyy&-^jNlfu;vh=US~&%2lM-Yzlft+iSaw4T|i5^dQ|186pgZ?vs?; z6@NY>J=tM*KDvrBU3^AW+gaRDo~At!XpWSn;5c@qm9QwS@5Wyh*?-W5%+V7Q37|fa zVXcM7f-9n>s!u=NQIOU1vP06D`vjWgZPe8^Ljir^45UT`=nA8tA8kT2S!o7QRri5# zFneaeQekmzQxbpU~t2Eb>IfNw8!+f}uu?w9BA)g*`#pzn~&u*{VG3U2QFRrN* znT|zt`^~olS;X(;VNT-nxfOq^P=Z9YKaL*Q?8nB4O1Y*!|Vfx720yoh?f|CF^N38e`Q0y$=+b z7vG-|r9Rh4)t=nh|4%X_&@#cD7M>B1p7<@vN8{C1I04)zo4A~&ZTRYZ755sDTy6a> z)Y2kB)uUB-T!^0Uvk#Kh=VnoY&6rwy}j#*u1n zA8W4E0*Q{v4|j)U6(Vu(9dFx|CB-hpQB}8?VZr(W5P04-NcC zRjliM?{?2zSWoDEUtu#!K_eQ>9rwVl_f&8ZkB!J?^SspP;Kb+W;KWa;=cjM|*h5jQ zw5y#oy}-#%dji?PQ*e#5b|cfWVmjRdB*n4H+ZLffNg0L_mqe9!^;#I2ZbR*X^z|bP zK|#SSNl8i5uAZLO?U4d-Ur8aPE!^ZTJo<8PGf286${0Axu-_})YI|e+_LuS!_Uwx= z1u+V(ZdBlsN6+H+gGa@u-p%GQ)QOTX7qzR>JakJW+Ul`spNMTSQqG#SEsKXWnkym6 zB~J{+S2Ze=82M5QJWLeRePe}HT3jh=N8?_Le(Sr8Pmc0vcT2y`9Z)n2TW9S74B`&B zBTZ_Y-nYahDP1iqF6#``Rs7c8nc?@DlE&-}Y4{bI_o1vtd^f^c)i#rISi#F#W;@Qk zMAdUg)Bj&K0D8^f$iTyfMGR?E&pI$%dStqkU}a+1J>iQG0{z`FY& zSheF;jQ07io8HOw?h~bq=bnE|gHzwpAgo@`B{$#=&&F)0J^b|7YSEuH8`y1Ke1=a{ zs_DKSKG>g7I(e%H3GR@P@kt9~oaW~iOYaF1`#k7(Vl6h`k0ZKJc#S}F8;EZ&go&*a zO9(C}bai%K*20m3d1y1_`3Rus0}rlS2Z{G2$*BDP`Xqf5C;7Z(PfDmY#~#OzcJ|=- z1c~l(4N%{-($surFHU+%WZQY8VpV_%HF;nb{{G+Neyy~Z!?UgT4m#c0BDA!eO1G?f z^xES93eT{lI7E(dX9minhcpa?D%&?JB;<2evCwtsQmyf#Uc+`wBs9Y8X5;E=nuS6anY`GjFEr81bXM??jdAqIdf`9^N>Q|iY zANOm`FKq$SNu50hX*B=;Wvk%%@hdW6>~X}1zV4y$IzqAM5NsvTU{?De4&anK0s_5& zhVFl^-RIneb}U7k9A6x`^%hkK(is$zt&o?yu)l*EF9l0TO4NbK|LhAy6WQ!QjoI9X zM#r5m%>}U1zrXYeGpg!^VbGJqVAEcQlsUM4YoBVGznd}z;?I&vI7nY z*X?C!L@O!~r$hd=>lW*n)$#ik6@=JT29Xao3f5mNplB4UZD1_~8?rYvFLZigTH?=^ z>2eXzQ#4X*@je%+{`F(r{CYQx&@ZaW5uEhF3`<|t;uw`fP^N4EY3Au!(K7Y z@yD;&v8gL%pkHXV}Lr4XaXss4#b`2MQb{;3olKYKxs* zek%TYLjhwI@0S0-14O=s#@hgl9BTJ@zaeBl#Sn?RDzSU_XEFtf{YEVwp7T(`XM^&X zs1V9pY6VP4AT&;^ddi}j07xOHkIW%>0i5V@Llc?CPs?j;^}!HP#PQrMvaEC>{y#ur zz8|m!Axzn3)jV>P`%oc{#ixU=N=59+tXZ7(7U93uPL%la%Lr|}}xq4=y4{%vIwq~h=S!3hta$J9*c{xZ1=d^}D)B%79` z4j_I_aY=#5`R0i_wByp(rGDsov?=LG!34l;B!)eK$fjY%oV4_TvVeqcu(H?8M<3CG zG6aeHNCAU{8`ayLcnxSZ$C@tgtlT^I^ri8DQ^6M?ginc0;7ZYYSw;x!a@oR83|_$% zx=zTpPG!NUVf(h+2%Cc23h!)>Qf_Fgz1#&dwNHW2qLMTaG*l!W&zm;j5*CA#`Ty9c zM_Rb*%VPG%ss{lxHoJ?mFYR7dVbiNY#r~=}SsDty94M_4G9AtN7^_8qlz&FKQ4r4p zzas|qgTn}aVH|ugZ@sdrUl{Mxl#W}J6(0MWd;8DYJkh68Ff~M*SNhHiU%%bWKJtPq z5TFha?9cqdNEn(0Q(1Emn9)|Nrm3lSEg`CEXQ}tC`n{<{pi=KQNph)FYgLyypuE+M ztbHrso4>T3)alybwZLbL)4cM)o9WuJ>2y^tjClXk$r7VCNlC9!qXo>E4lRX?;;aBU zu52PeFo%0@LYbJDiug=w2JDVI-w1+Yjl{n#NbK3+C-9MTdr%6{>R*3A8ZDP`_Q#=( zESlRtYV(CFukQf4^Sx?G)VeY7-x?`?y9egdEI=b-IQoDok2+Qf+NyP4-FPFiO7PNr z!K8}mi-^6HZ_9We1u3qiN;H# zKuYm3NFx1YHKq}J%9r2JH*g31ZB}{6a zQ8AwHf2|Syo7;jvAJ1|TM&(GUF?UWb7Nu-_m@6!LuokbioMc_je=F4HR(u_m8td8by4XN24)@TM}SHqvE80WB}v z(lDU~Y!Z%f$<`}qPJS^-va=2r2{RcC!LnRjQtzt`f?TaX`kF4M%7{~$pi^B`Mk+*I z0+)z{TiJDbdL!MoTMm{tFKaD2hu`3=H@B($$6orcCp}@to5hB9k&JCquR}-e&P&v& zPavR&e;;Ks!j$a6{{H@gF4uhKpOHJ&dr&^w$4_XI-n>~Z{7dJ&&b$&MWNKHX1spI^ zSlh<;|Aci7fX|SUQEWV^JjU`m1REn*nmS2B!_`-fcaEuZ()Q_XN~>Gf0JPGURzMrF zOo;?&`49kD?=;}JRar2PioWg=qk3fF>6`CRIGemxsbH~JEFm#d4Ygz!0R`-_rifdG(R)zKQTE59R)z|k(`UGX z>$oHT>osxx_B=EqpaI{U`kq|LzWh}k)P`iuJeu22u@|#KMPUa&hm7oPOJMLZR1~FC zi_`MBEwmbgB5>itH>$20-GIsxQ+xdKNp<3RoTML&3tKMB;hpp%1 zqpca)nUvktr%p~~Z<3QkH-IUWoZ!v!x?Q_ZACvqscf%}0R&{Seun!+TWO*`GVxhV+ zEn8N7_rQJ1XY4=0`IU?=SKSVTBXQ$mCp*&M3rBSvAA3y8nnwVJnYAVNIH|wzsgnR=4U5SelqvBj z!qM)IRR&fVgy%8ft<{qvG0x4+4FKN9Tn?Z{&I>&RlVDUE%CmJ42TPrieEyeD2u2IY zVhay}la-F`bi)mc29uwkuSzj^@ll28UGcP!`CWJa2lS7pl|l@RczNpR4GNv!0;o}@ zXsX)LGRE<_E-Nc*7Xae!2n*BJ-1hmVLoSmtsRKD#u&Uoh+og>BeKJPc%yqgHMCkHO8WvpEOmRW?N6x&$Y@G*6YrA++1N*H;hr@ zdd1DZuS7(`{=G-tY$p!Z!+X?`IEwPpTn#8XD*DIw*)Jgg4(67IqD^Z)MTyg9uq1LY z;k{sV8aG){(7y+i{&%Uw`?Muzs!eg$!yDRqXRk^fKmQ4=Me2wA8$jEGQs&$Kgrnw5 zShbDr?QjZ|5Lz~wa9e0L(O&^HE&Fdv%^VUW3fo_ayb&o*UG+&V*_XEAzs)?sh`y*g z!{8sL`6Ugnf4@$=Sjf&+asi~=H&7Ggk&B>NV*Ry{Wf9km^cXF9tJ6KsIZi#Lf)_C! z_>oI_v0L#{ZH#VxKoV%lY_Zw@*PTWAYs^X}*DyQ*0KXv#@qS1@d+3@_Abq_dBgl#H zy^HH05P@WKd?FFJqql#b-F z6?a7c+cZSZIDZy9iL+ya!=8Xn+O9PP3P1uvyNjSEGO5s_yFsdnlmvB*9)iE9w-Hp| zT)E-6LvtB}cWN3MPZjQX;lL#nlNN!ERewHEfDC#eyIO>tAFZ}@BGYEmRp z+d?13O4ZC;q8QSDPE-RCb?E42JPJjD&kSOni;CJvF=>KndSKKIyE^^d15C_F^Fv!%Nx>K}26USaO zkpkd){+Zes)i_?z22-Ws_bX-nz~unTBmmzn9vma4Q(TMlAxD}mt|ALA6M<>KBgtOhn*6_J&O0?Nf+!+H(NJu(>+!mxnn3zr@Fo)+KwhB7!0JjKpPpP84C1cHO|lKD2Jv@s+*=So&5241)eZxc?ypq zU3h{}8Etxy<&oliq-}Q*Z{JS~>}&F3ayMD8lGUTV555J&RmVMUjV1%{ z&B;lL8dB|@b-qGm&x(y>+fcjYm!{q(8i_+Vgr86&OsR)B;Ysxmr;^G9NkaMHsnitq ze|+wIzX^ofQoGex7C$bBoWCwg`V?H&Wc$H^tJMI2u+~Vf+z@RBVJkAnQ6A{Xw7rRt z-`;eMgz(i;3rRx{K?@CHv;@5N#$@@6WG90li(kT)EdF@L<(Gup`8)wpL8ePzLBYd} zsWZ-$spQ4CqU17q02rkQQis7$FE5N+TTOP|8$k=+qt1nU<7Xiiv0_n8rl6TKtFfv( zR*+?(wQ$}){jNM_r(FwI?$Z39D&|!t@42GVa#Wrs<%eX`4x|Oe0BMPFVf?p*&eR4TwXoOmKCzVo zwq86=OOv&z))Uvi3cdw|A&StGQr%SKeHjD|UGHpna|E@GK4&Bje6GmXpyLU+8=!+r zo+=={F;?)wLu0~#Yy6$d{})qO7OP*kIaqh}C0*=Wkgy(3w~i8Y1i21qXj4G2#grEy zSa3@|BSbcDF@!SuDHm?;BG143&IVndD7DlCq``%lR#hpx>iJl3HxMF~PC_h(RvmXEforMQ}1H@X5 zK4b&fWEilQz$_YuXUeNmX?X*)i>GZo;FS9x17*MIVeF%EU%a1~>JAhCbQJoGRd7xLGSDFsQHp(QA7x zaE=qwHr||U)h&IcOWa5upW9GIQrr;3Mc0V?z&DJJs^-OU*eHBV2OTeR+*0l^= z=&vk04h}@xK|Pryj=n_$^#Im(+?o%G?TdJSV&X)6|EwqPQguToJ>u@9;0T`r%Rzrz zmD0cKK9Ck})Y|zrC$h!TsK_2FbG`=mCe*o}cMULF9=>$N06)2G6L$B8W~6^G(@JYp z-N=9W9Z( z`N^SL{QfoOnJGG6?vek($@slPW20-BM_u*=09`b#7snQEXl`fTS8{Tt_-sxD!8$17 z?~YD6=plG#kan7%N8B9-SG8M=v|wCB73F!rrU9q~=!0>}^J=sC)CEH9dG!9clj2z- z27~>!1EGsq)^IDa5sITU)^CQ)KSClgC$#fP zzC@<+xy^1$HJ7@(yITmGdioa+ez!%k?v0G-<12bD6$~Yc$N~!ITYF83`h(A9a6Vl< z?kw=M177H!fcfAb>j}DY!D)6Q<e=P5L%`*7e&BbNfGwdpQ=LBorYM9uK`y z)AC!4{Ad@r9dSVbDe4dj!Z6A2u){4V43qS3`{jkJK<3H5gbyv4Ks!Kq+$N{k^KB7t z(E9lK)T=VEd+9;kyE}2m#Q*<1UjO! ztyEXTo416N#bK+_r06@oRVMM#dizuE8yUJKpq?Qz-vQ1`qV&lirQsGbrFG#_zgio^ zuU0u3eEL3(f5_ry%*O#37*sk+DCrt~$@3y)9R2r#jSrRQ4E*k>2fag2!JtCJ77ae1E|>IdcM_N5V-OocpzD4nwJw9f zP(b*=Ke@Mdqk55%TMzm~rP7oFJD12-y1f9Pj4i&WV!hu$|9KB&60VJ8>Y&P1)<&0p z_`qLc(LqzdE&f1TTK+x>54l9WB5*O1dJ-8V@%r`avZAD4<)>ImPu@aIxRw(-OdohY zGrwn;|Cnpzx(`Fw8)jQ4D+&}vohPVbMp?}c;V~*(#3r0ePZ+_utRgIpgH1Mr%v@mp)a+PJD3Nj z@S#pc`HTa=;+pfzoW=S6T+Ty|n@d9}G6*#7DFn0dVm0;)v-^wolfuWa2 zwx!_IpANg>n=?MUib8~cG4KrMUA> ze5Qegc-)(KfudtA>G>R17M%x2Nnp?00`$8ZcInckLE!HSZe`|j$EfmkKvIBUqhhN$ zR<_)h!;}RL^NX-8^Mk;oI`JKzwkD3dsq3g(3bO6)4wy1rCTk61US$4w)<;ZWo)vcY$=M%suz!#3 zleetDC_FD&5|;7a<|BB~+eA#HxF|2aC}tX1I@D-)8Ogq^h;qpd_JI7L=x-xpE?3qJ0^g_M z$@o>;&n6{K_HkKcmL7*-96yKvezf=<=?ib?IDKGzObq`9zfSX@TfxTUvw`!#*nTCy z;n$k|ob`=ZZsLNv$14wE{`86_ZkUd#M5$`q?>6TOo0CTh0Bn+mo_melUD8^xB_@Jl zjzRVJEfb=yecf`G*z-Lud3mt2v$L!~3RKUTS15oFu~B;e3s4B!>%DVFJgj^qbS+D| z0pvGJ#${|MBP+AXm`MK9hiuO09={SR>luBSw}FfL>Y$(PHY$5~gt=ppuZ#1&XQ{bO zL@K!am0ZRP1inF%*=}3ix3=x%=jk0j@|*|V_UrTW1O4Ya=^<$A)U%I|w4sL}?W0HF zS)~^BKCM?cMM*$NfE*fDi7(^Mb{E`K(BONO#G26&;iC1mxv4@u0G zLa?9njY*TKoTK;ICuM@*z_JuH^HU`Dz*(Rdz0fX>Q{GOf<)P9z-!0}dsj_CZ9qK>! zCXpV%)Zvel8O|)pGn}!>IR(Aiu~Qj@*(u)T_sOJa@9iOb%7ZClT53OfU17Y?z&)w< zRoI#jjghOtl6PcS;(M}^sS@rq>2=*MT;;|(xi-_c?gfuYQ=t>8yMA!Cw6sJ=mny2Q zeYKgYgAcfq7{uqq5mf68lM>u`QOkSzIin^}s@z~3>moRHz^V-*?}qgg$im&|WZ`E& zAH+m;^yD*%#Be0kK`+fOjop!%cpobJ6E_yo!M@dO3l4b0Gk`oq>1+3p$dJltL z1&RiGAp4(!^H0;jq17+=*i=%iC^_kjxLbt8kY2d);;i~O_T|LsrwXV>%fdf3jeJPU zii|1_lolF}w3#DSU%q#b&{A*oyuuLY7U2Cud-^ge9QcxPa3YfB`7cW3b2{Nkz-qcD$jkQik-1&UG1OZ+;g5~fvt z*DhZdISbT_d+U@AqGEpNvZxETs38o(Pe4Ai1=y~@;W82cBA`o)5^*!rRr8%9zt4p_-fFs zf5ij`hgi8n)f=)h6N}oIRm2q8VWa!Log`pWp3~q21z`zo_$#jQtF~YWjJ!QA`wn0) z^P#V5Zhr>|#G0?+Ljc;PaxxSVa23%Lj>YLjI_4Xs zo}@g{GY>*=E9V};W#P_+al4BQW%cj7Eu%fFLZ!vpj7388q^u-|f6Y z_tt{(skY%P<{BKr6gp>2!ls$|W)6_)``crwWjmrfveD0k-s!M>ba82}Um!~7K(gs% z*)A!I^FE3^74G7Hr=@;tk&fVZyMCQMwuJ^;#6)PeB6xHQ&VOjU@G1INCG$Q+W)ghX zTtLBMoc@MLak`t|WbTQZg(@@HZ$AklCbahyvRVa6Ww_N7W(P|lp{oG#br8pv6LT>` z;IQ@AqzljYBkKg6uLPMbpzVg3f^hOttP%aZe|&rcx@L#n9>BL%B+K35H#O64^lxAw zKbj3^CHC zv^xWa5j}WtWy8L9twJa=M>*H)rwQNmwNlBpr~XRP*FKh=|Ks#O5y7d4RlM3GY2qD1`+tFwOXJQJM%IpY5R9sT z{g>ymJp#8{^Vov&Se&AO#X8Ldeq=&u!e+FDjY67$k)JAzHnM#bmOXKCTDMw;KhXNz z6nPu4dW4*UeV#Zg8{L=AWyKa<9m$U-$61iM^rgy;a7$pLG)(3YVcno}VYj;my8IQP z<|GM~HZ6eQ?xDEBSP<)H!!~QI8(;Q8+{o|~z2>%{W%q3`j`I0({U^Kg=>VX)Z8aDx~}Jf~lcojjmGTB^unMoZ zFm;6c(aaBWJmK-_l+yh=>ZNw@ji{L0t0AcL)(eTBlMeRxvw_qZRx1e8gGfHgYB1Tr z>jZ{{KnB?~(BUt5ClWW}nl?lEJtfM8=-qJ`M`N**trPd@Us39onHS@hSYGi=voC5M z!6@r0Ol(8|jLg9f)H5#&MS{FQ(S-|98ivo=TTMtu3%Ng>tH}-#kB)vPKi#&nWULud?XO3op?00=zvR$&9wiw3%9(4WYDfL}BA z^mM0hrz&k_T`_M>;s8w_7B(dT08Q|UMrO#-0z$my zJm_i-fH@F1H^YZ3pa9L9B$?Sl0{lMui1?XapqRfOG#cgpI#uisB7$IqP5C`9>akm& zMo9*`-JDG1F_lMxkvM(<0rI9QCp!w%Q%0w)q&z_gHf|^a91!w$(CsqNy!7QZ1PjKE zbvx|*&lekEBXQscyhPsbIO5(v$};`r)HiRrCP=l-zTsO{4@M`xe+vLwL&`|DbwU6N z*2ZKyRuTZn+JLgCLm636l^mpuRKDq62j;tkK(%%)rP7}n!*GwK%t!yMiWfND>VrFB zz_I+_sue%MMN%6hH9YYh7S-lZn2qdL;32R{-Vql zZtfiL7UTnLFx)~TS#*%6l*X9{09!>ohSN0*^KA!T5yR-N;&7v9ta)?%=z$v^L zaw8x$*x#{U{o)V7?wtezjksbD0FT%X2)8VNMCf>WX1?UvaG?tz=ZQ;3sYf@($W{vt za6zX|zx?avPq*|a*uHlrID)=?vJm}-$ zp`vN|Bf(9|1lKB`WOo>)WmvfJ4nh%VK&gj$DR5Jv$#B{#{Oo zcod*eI)^v7I>7%&958bxNo3*Q*52)BF{!=(@o(~tUzTSq7jYzgc8#CE5qj1HLcy?t znXxgn^+~5D6z!blsj9OyJNQnv-O&|YH|^LX(Y3ql4cq1e(B1{}r-t9CV>tn{@7}6h=mvD{a(nV0 zd)}NV@@1i1IB(fJ@vLr|OJj3#;Pf`02V-CGaSJL1Fo{9#_j7A?b&9*1jRg34U$9BL zf+%6KYOQDLFMwR_F0XfdnV@>{{eO!~d^#44v)aCHTRl)j{SGjk&ZOY*bkql%7D6`> z!zqA)a}xD@icA4-ENmEdbMRhDw_+r&_^!r`L2+7B56i{*JKvUdbSEqAr>AICR3yA` zFYbm(YY=-z!HSUM%o(iSVEd^zTZ z%F|IaWpuJBXm+T99v$H1w45A|*1My)<@&lo7bji=jK((kbL%y~le`_*SU~NI?-~M@ z3P&q`f)X?~BiYIfjg4QU?|#4d%+djkL2U+}x>XlMeRxrz>c}q&V|{Tme`+otM^$9&^BM0S38^X%ETof*0da1s2p1yes*;}|N6R*esdl1v3>Dok@nQZ2u-X(%NqVmN2SMtwKfJD@ugblHoK{T*WMeNGj*%=RT7p6`b$i4udI?e6i%p zy<4!(D^a+URxo9 zIg)$tzxSv9b~=#oo}-^8HlmhCW;By?AmWwb8+AL~9CM9FdMCMV;NR*0syz8Ior`MN zNhXg7`Otmb;`GT>fdJj`=3a**Q*Df7wZO&o!v1UTvut4|wUYQ<;RZ67Ac&OH(p?YJ z1w={=^2xjM2ntl+VZTW#J88?%*SZm*hij1tZU6ffy-G};Wy|k=om%gB7S1)DuM=L! zt=@G<)u}(LzpfA6XUMsnde97mK#pHB9Y5Fu60z`%wx4X5vL>wj@hh@kvr_mS=*$$F z9=7Z0HXe608rN_7^S#D18AkXn&1YXtm&psC&rIF|RF=M|B-pjDg=mMjMCr!5T)V|! zT%D}-P^!pXOVi}F%4T8Qz=lP*d1rUG%O#z-^Mp;K_a9ytiWYUgPcmQca+=ssdMish zjA)-yYKwA^@3@K2F9z#|1f-qs!d`R25E6!3D#%AY+26S7;|1Y7RQ-y{Ak*2ez17P1cwsi*3>ILjxNxQby z>BhzuLvc_(M)H-K(OFlP;FK6IZB-H7xXp8IR0mi2|?(|db#hY=)h#_bX=_2e82kmboe}%Rn0|zTEFH1gklXg+()$<$EDt-uW+;_ zF%A%ruz4>Q}>|uUNmY-Gg;KX^=_Bq zj!J?@PRIheY2S>fx+rG(m| zXuZh^4)IHw1zeO#92~yP-_abdo=rWD^1k=$d(rQwuCYLjd}2{j*p66V^)?s-nP3|8 zh?huGB}ge@ju)E@3_=4#^&8Xpx$keGnpe7@VZ+-{C1^M`P0fLFbyJDsqW{y*{Vh8g z^x`E5R;MbdUbnu^MC9k^v;$%IC2;9p3=+=vlWePgYZP zG`)}d^H$W~Q+w>?nHBuT`yxM#flT@#3lub$g6Bsnk88d6H*(E>mYe07Zu-*tk)4#~ zNEGB`o7-swG7fW5CM$m$Jr4>gF!T}9T@E78b2+6TF&}!tW$F$tCd>Q}ZMAfeYV94s zF#2HhO~So0g1W4Gr^N%@<`=UVmAA3AYW?KxY!P5e2s8-t&sNJ{+jJFjtr_Y(}O1}S@Y zX;KQW(bI_oavF4F z4n0FU2NvLjkpPVyB$|jxXamw8xn_qkX77I=grA7iwR<1sc#GOte05+LvAk8D!u!#S zZM;C9>08W8?L-Fr2w9#CAmw9-u1#|~z*0LJ0`(iX&-1>Tzt?XN$nPYHM^&38iLBfn zzL0R6@g*WUR7@BH{NCRoZSTFNLnY_> zU4a>0QD#+!#ekPv(xc?loUJIYMN~_CdYwL%=dTsBBA_|LC%<4>Ap?hR7PZQxiprKX z?sSXgZTw!}2;1nJEdRcfdg8Gc=Y1HbCH3!&V|*wmUB8Yh4;+pSH&htfuQ#6jR_+^R zeY&3;DT1xd7(7`4=Y6o}xU)wazjB72OL9ykwmE^!Lc*NzGqqi4&V7odrLP&IfbIA> ziiBV?T-x9x+`l;)r1*efW_HF-30Gp6CJVCSMU>9=b5^?>SyKuf!97ZT!8`$SA2Zk^k_FGKXF!JbZ4(LLuWq z?8%SHMw4IT-aqX>-%k}o6nXzz$*Zhivt^-h)C>9@sN5w)ZSrg8?>)SADxt@ZPzT5Q zl^9R?kKRAWrx{TeoO~86qpkSyE9(8h|0~&60=SJDH-0u0{S^b*dW#G-zh3s*5%w-U z%4n-D=>h}I&MN+AuJ7!2J=^A<{X;4B^ z8tD!R31#R8>6UJ!Lqbv-q(!=G=zccm{Lgit`+nZ>j`{4p_FC)rO*$TENWAO2SFuhE z*^Z{(-V_p*f`GjQZmYu-h5jckt#TO2vsA3)dJ$JgHa4|Q?vi1GbA^+L{>KnWw>{M% z?M`Dj!=~)Bsv9)lzi2DU(Y`z+B#%weIk^u*?N|I=)5TiGFTPD^dB!jy*>j){b37n8 zg5g7M+I}1^T1lQvOCt}rBU&7OSA#d9f8Zwzra7e}p2N4}Jd#7R=2E972h&yk<+|ar+}s2F_%>=x!9&u6GSCnsyQ@yv^Hd9K2cnX9wL<4SjgB7EfMyZbRV$wmy_IF?XY1YOo1W5c2 zoHMQ4d)xAo`jqJS?;@EFCK=}smrgP+-aK9%iklCbO0Iz=b67vfCAM^P7%*wQBfr<_ZPok~r$UlKonseHys{(j78p#i7gT>{79 z6#F2Q;?!@M*q1iQlYWeK87;uI63b51O1H~ALK88{wavVwQ6C^|-Aa`_j>sqWZUbrl z!})Gp=U6)gym>r!%Rhc1_pj1^mE=Y^9j0zA9t^9X^#C>N1rRlRg*0x%nVdXhcUuR@ z_&VmcYzCyh=JrWJ6jwK&Q@-O*130lJyO-GsQRYg}oF26?K*%Ngm2~_I8FC+BZ;F`| zBoAs=WHZYs)F-&vsYd@}N5^u%u1qpP3@!$JevGL)8uabq&Hybf&c% z_XCvlIW3QM$>*mMN$5j3M0KxVRWEwF&_s{?ahE}V{cUM-+E)lGEI*%WweGx~vH&ji zN79|SX}0mnB@%7x;nZzt8_U8Q9Da(I8C+*a#vd-)yg}ovQ#0_Sfl`fRqrj zN9e{<^^~6Q?^4=jWq%Z0`z<$1P9RmxGw6yEHCZm(_%P z+tF#i-Fjb^(l>CL>1w}IS-&@Tvzd1~@mG)6ckf{Fhd3Ysn-fbd-IYsh-CXR7^Y2*@ zBb3%oM(zGF)_$2b@<+v5PS#p>OAThs$x>!_Pf75>I~Q z`qQ~z0k^Q~- z?C|h7*yWrjTz=9+M&F;AF;bJ#K}yW zx0H|%4nL&GazDulN=IysoG*Xh3j|mMWS^vzX+VhdA)xt%gEGcG89zG3oA?HeJ3xtb z1zCp*iFA6Bj9UBozyCm_k!2FyE_TB z8vO7|Pudl|(WU;LU%BL;X4r8{k|VKxRoo6#z!1v6m9B74XOkGkuKK6MF3}#cyn2QI zj9n;m-iZ4gHB439?^MW8GJ7&>vOnBrKAhn84GNduW;x;en`U48gAnH{Upkkrg#8Mx zA1|%!_;)BbK%tC#&#u^g?}PT;x&4D%KCE?^%l1E$te$SeFk`fGV%aW=(9`qp{I7eZ z(ij~;>Bg9h$MZ6WbHpuS!EUx^DiC>Rq+sd`+s^%*)VzjbKC-FSuOn@33 ztCuWcmK<;!sTw);T;?viOw(92d?ddIqAYqkDs?RK8Qd*GTn*@2^yvKQ056QkkD9@3&qUN9E2yz@h&YOTc1MMw%>`WVb*W)pIp5e|z540b_`vSB|5#$+< zeymri$Z=@z84nSFjK9EpN3#Q=a=(F!*@eHT>sox-GW=b+2k#lF;i=yx&cpi~OZ33| z62rR>n3ioOV#!e>_P~8as{7$_7A$1+PPqTGbn|g4%7e$-HgH?HYC*j|MF?OM%CnRLrRK&$;6`MGTbo;$0_}R? zHt#w9rz|;BR!P$IH}3IrdGO1^!goUDJZ|@NfG(fYdsZezs|WL`7;3ei#AS~<%5oaI zji)*-^u%xagEUxh5PvpeM1YU((^kaj;5CT=`aFCU<{bAcj`)u*`bf@=b`;DV+T6%O zPePn7E0|a&HMR(!N57s8*&JCLt69N~}Q^v#5QMZ_)h51YW+R11cEd)RNk ztB2I^I4J%dix+!15sNZG+W%|$4|u#W=ad^TZnwtk+h{AYXy2TWnzSe1_Vu(t$j?qV z5MlgsCNWibdX5eca0{qOazohE^RWpEx$QWZ z@V|a?N#?^)Qd&0gJ@-9{{hP@qaGO=KBligZN0duWx3~7(OcYT8LmB{%A=}VYrFm2b!`6ORd-@lAG^{EKoz% zEI(|`TLuD3Cq_rcIrm3-yO3}R?5_uWZPr4sUud-^$O+jgM~U;GqI#6g5CQ1f_z93C z@N-?-;ur<{+&U9k%z3xiarw>Q2R+z9zCHMuE+2yC0NvtG3u#Npw-e+bJTLIVIEVbZ ztFsC}kv$pQAdpwclgB*0X4Vrtv!O88SVerbOB3oFZHoA2cl)<(2=O7e;Kt@6&(p|; zh|3C2DFQ*z3i?{L_j@ti7SM>{^>~uZDRkn$LjeV$t33>~>R3 z3>*E225q=P51+iKzt2y+FNe6xv#9gtl-?XTZg?M!eN6sGdC++7P^R=FCBxj1V}{6J zYk;@mPnLbwbfxVn$k{nJDBX)#N2(-WC0C!`r;KntmX9bPJq_G+burpjWe6zTf9H0m zyE0#97c+9Qwf&IHIdU%oF#=!;Ar;4uPeX1j9!iW$_rMSVX^uHPU|`&UYhVK z*sF;~kQ4nJc!i6)j``Fiqz;qjnjN(MSRKR~SkDjILjMIH@(vzpJP@c_)M~$;TJDcv z6fovq+LjGDgS>)}tuC?+5+ZwFnVsPc>R^N3nZPP3#KZ&0p8-PHu#q_&m{$)je{p4S z%umd9bHmq}T;9J=u*zoq7FqwA6W>5$(i{g}1}drsTI@Z#$UBv#`Cj6644US^4i&y$ zn5g{c9R2lOs_7XLbxMzyjGObF%3qr4+bx`3EH4}~jFcC1xs8p?ffe~|#9lRpHSGGk zJT>e$ZD}v_wm1 zgPHHRyu*v~zomlCb`2k{E1~Z_ksVtaK;u=EYSqfoGe8Cdb)b@aZqqr7zV;Va$T)uE zkkG6c^5H{ftMk+5lH!ruKZb3xPcbkE!h5te;BU#LX~9fySFjF=gZ_Spizcq*r(f58 z(6u1rPInYa->G#it)DA~ybLNSM0X0&Jd()^Jq`fnejAJDp`-K1RB991y%_PT-A`5&bJj1EVyAwY{Rb-78 z2P1|YckFmrrs3@=w;?#n_cT{mg6x=N#7V~ZcymL#n=eN&W}^kDd$Eu5`E>d*T5A zbw|@uk}T=_iifN5{H2{^HiXaq_vxM=$b1}g21tVQaboAoO{y|8{{z}xEE`Z@Jxxbi zG&T%f6E^~@l=%S#q9x}j`IP$@jA^({&xCMPx^nfk-5W+GP>mREi)Dy4o-oe69WMmO zru|S?S_olzeuF!j!c&I3iAYNYOsplAUd;=dD=OnXyF zU+edG5=Z$%wBOo~BkW6+p~v0YZWBuUZy$jlvfN6f1rut zo^^$poPepkH9|U!h*!Se8n-_L zYzV|lmQwMF9n(oyY0!e@G?yM!zKEYMiru zBKExK#~5W|*cE-a8tLu(X4HKGyb2#!(*(7t3hcTd!|Q|VMzV!+wG3@&nmnH$Ky=kg z3ap433qkbO@SkS7qZVdd{j#Wo^s$<}N$!_ch@#K97g&$iSP95RciZ_ZIO3^nt&{7u z_7xNJZyEMp7hTRI?OB14k>FXE^xEl!N|c;Ose#;l-9Nc0zcNtv{coHOu0C4fectuZ{-h z$Q|vx=sNw=l8IQa+W_W{Y=mswfs)MA#78eqhQ6m|=;L~}#wgb4)kXdXciz#4eFr)$ z0pok7NI|@uvgNLnf>CNheoZJTxbCsAN=3X)V|hY5GN{5sM3>>9zPhgp|Y2 zG1do@IAnPsn3U8>a0Xc#-FDyB^PW)p9OGGz5inT=5D-8((8jg93Hj9*4{V^np}r8@ z#;SL;IZXsLps-`=&H?0@G?3~KJD|yB1dBHHDb*Zv4%gT^*LmK<8JL5Q9Teg`IFU`{F8huo7aJCSXEe3qtpZl=T5yXbxj(JjsV2%o(eQT3R zBF@Y^(2*IZl*=`Jk{%wCpka!)zp4zlWmzrCiy-bu^O5da1$V+pZ@?cEOs8|1;U~m; z4icCIuWmSy9v&I7ZejvSsH;p{tw^pD|AK=QY}7hdv!!zs z;FH*OuGsTMkjMsNkOOD`DP^Z7%Y~=q6D90U_RUtzW(TV| zNx(6?RP2~Ua~bznL;L%Ow}8kYg*DnwKbVR;{IV*TI9V zL#^W8GU0x}{DQB^q*wWI7RBlnx_^qvtLtu92Fy#L@7qK|XY-HV+sDi2*>RK~6y}16 zI2&XHz&G43P~9=XrW!=1(IY|CJf{UTyN=b9)c4pJIAb_}qT?!kbrmb|#BT*ZlVotK z3%=s8{F(U6l3*v)@HM7y!kOO2-6KWn`p0uqdP#Qlf;jO%yh_ccguD2&ALJA-W*;H) zn>8vuiP|k%i*#2_^fRFB;(@?F^j*EMq-Uepb#E#_S$e#trVL)i-DfFy;+z@$WxOvR zIUBm|bxg{S&tuD~bxA#hqr{G}Ugi;rEyE+&kz30s9lIGKeCYp9uS{k@IL<8=1BkAU z@t+k>5I~c9@&=8W%YeAzC2<~?c~hU$=f0yyoQ0E?F;@bw_81a;01@!~GGEDa1%Nl2 zBEY^GwPkrO%artE@DuhttpNV45{y4)k<}tq;%c0j2()+!ptsFS5&go+9{AZhD(`5% zLEsQ`{6$~j+?Mg|gIsN#3jD2t@(rowBg zy*2+BvpT_>y&A~Nw6H+vuS-e#mGmvQaVZMfqi-C~4*dYwAKz+4aG3S?sr^n!Eh%oU z4ZtoiLf8f*|FA{>b0PSMwvDod1@DFF;Xi4pXFfdhY`O*RkJiXb*(_Q1idsz5qH|F+ zt@SPIY*UXQ`Q-!f^DHg7w&0f7Jp?IE%;G;S=DYC88Sm&(@9Bw^>#Zw{;f{s#C3aU|rUliL zT6J4M&Dif%NYi7G>qzAn$Q8hUFwaE*`@CWLxEFq|1X@7DHw>h$TJ$e476{SnS`pPO z3cWX=M3BDc@?VD_9eG%AjqXR7Q$<^zH7h3|KxKZ#?KY)O@jjg@oy1PfdUc2L@# zyDbYPoQH8`- z_#k@ltGu6{tB#-`-}9dvK~CIA-$I<^qd;27&gUP2iI}u^Gr<2ck`hZ&z%}^Oh+{vu z+e`F6Yw4>sVy-T~ID5ORitKmL4jH;XD!nRhISi34{SSOZGC}Twc5vazczM`gR{#unI6)tU7}@(p zg4_AGY_gnihoXo++rhHd@6{vYgO}vrP2t38^2XdpXpIjssNc+VY}VRYcr7LK6jREt z8jB3N?<5S5z=Zhs+gd7hY4>xZM7_W&e`X2xtH*dUj@tlPGH!notByKj2d-mUsq}!1?3r5Go2%`QAwu>*r zCEi>wCtEL_j^pWHSG?f90V6@4zo?VlTEBf6t9Jv2I&)G zR5CPVwb9-9Ox)?xnC8;|@e1V`-mX7_IaJ^5Ky*r?nQ0S$^ix1&nLH@oy>8mF9OWNC z#Qacw2Wt!SSJgSd&xvdp&QC|9Wf(sk1&dMqu7M7TzQpnV`>bpC_FrO?%U&_jM18OA z{3(EHc@W9iNjj($v&IH~I2L@Zi1M~_j&P@uCc~x23U)P$Q|UYt z7D>P2mVdr>^AX4*QrVMzEMdHZtK=<2UYv6AIm$S+uZzXtICRG`>Z6`^hk@sM0%sMY zO4h~m56Fl}U${UdlQPe(Gr;|PmBkW4(w+X7C`5^WkzoGd5UKHM3T*EbjheXsDHXbD z-pm$AVyUCJAZY&wy4Ov48lj<YwL@5<6M$ki@j9AKvs}BA%Y6c`ea4%)V8%JioJVI1H_dD=`4Z7_47E@2S8{BTx)uNwaCy^R5uKDr+bj$D=Fui)1halDMlx!RRO}#iot+4|0^2N zoNl0CZR~G(>-pT*n!QK-)U}7yB1pxPBgR2x?E4Z;6ggc~kU{nvh=j_0ibu1;HdYZa-16^fDTH_>W*di$@Ts1$){4!Mz2gPa zRlS4uO0w(&I+_X8wuU^eRJ_hY>2Quy)u>=jMyBX7X=5*iBqQl$X|%#3FXCgGb^~qx z^FRSWj5K3>CqeKhZvP`Gra5is=X3Tb&v@73;dlm@g@VBiW4orz&Gi!a6a*-d9wBle zo0q)PdKx~*VTR#-&fTZmzg}1z2xqalC(%Z)8X=*tS-moUy%`H6M`~IBF131K6ad8k z24?dwR8Wwe{?vBF(kp=JL}X)Cz|6;389&k2U4%SEHbX#=SUn(uPCVkjW0y_7MInpi z)beICj;+mP=y@!Ep30>%EoSSB!`TIH5ohl1(WqzytRZkZErP26lovY`=u}Pa zw9&F%^q0ClT|seL02v+^3xs{d;$uAZ2CVHsXu4T_EaP1mK~1CYu6)K_AM>v^(x(5G z5E?}03}6`id`6Lk)@^Aplbkh6E15g8Ujj#P*&|Qmtt$`+O4PwOXbv6!%nlK%Fofk86WC|X_+gqBl$%?3@&HJtbWR$A>d<69J86H+#bh9Ce_uDIoa>JB4W{#tQ0aSjjaouzw4yu%q*F^VIDK0&8ZaGj7{bi+KF_844`BGO|MmsNJ$6qO%z$FG zDagf$;t2HNG^RJ;Kkh!PdJUDvE6ROM{B^6B>C@GEKw@Z0%X^COBWIe}Gypn;(~SUh zuWd$tohGW<>B(Ap#PfeP$bSLcnDQk$TAy_>B<4P+$}5bQ=xX6J91vK|d#w*72o$`L ze`Sx3SK&SenC*Zt;aF(AGoPu2TLAFAvOGIelck(m@V(gw0ZloCT&To--qvQy`m7>hc}j9XKZ8Ee13W58nZ3EDnP6>HkqyM2z~-|tRuHDY9a14QJd z0^ZafUwQwE+{uk{w+kP6wEbU9m1qUCD?Jgwe{^w9m1iBxnZ(MKCyUH#1C!IK9pJu@ zi%zdu9H1kR&M4E|(fc&_e~0*YgDHosC~F^epG4+1H%X0n%K%@8&}(m*I=A z$6I!RblRtjoQ9hMnLy1(DhYWFe&(^wvd?gu;Mrrb0gz$GR|7<8Cn(E(R6k%LO(*_5 z)+@jL&$0Wj7v3t@=^=IMaqI{4-Tb@9r%fJ1VS5b(h(_3245hsFisk=TFzR$D14l;` z6yJ^I&PFvQud+8$yS&t}J);}Quc|5h6D!isxL?wFl#$@~e`2JP@uZ_qBB}J}%5asG zN6&$}^S6{veY8_RJVw`5ket)LnES!PXr1HhWy+6^efaJ&=$=;fc+^e=9=9?4gFx!; zs)!j)_bDU_T0JoTnJgKXg*V!)_$b?Q0@O32dwy3aNn~#uw960OLa2_EaF0N_HUD_+mhl>}y#kgy z>N@3eYKSl+14D}NK?4w3x||S%ka^jLjL3QQzwCy9yGc4@7f;_a$AQnvr$3HpQ6?e@ zu(r&Uy{cjq;*VKF4mNb?Kp7g(SAYnm-+53EhA1NNdo|5~0wh*TB35nHGX3VnS@B&g za=zMm+{e6I3qCb2##icxiZfRKCvfmt|BAWsy@IY#395nIR z0{Wogjw5pkSX^V)E}MUS<;)e_Da=iw6m~QE1jH)JGiAmLVyvZ@#n*zSo&9YZ|F;E) z2C(a8i1b6MDcMPgUa{tES80lFD`Ie^xi8N^`}=zti}J7SbgaM(xg(Lj|J@X zoO_zUJzAI#h@At1kyezYfpn~D+j%}N{QGqP?kE}-4p`U&Spt#r2jRs0B|K|YRh$`4 z@Czz8NprC;C(Ko}SlvGb(+=8d{mbPf$klY;Q|<@ud+hhusn_)%ILa_j{0V9T``zsf zD4KsgHw$mzIOP!e(if##?tVciEHa?vdA6F$RTJ(!e1-bk@%{>{6v*oG!TR^sWL`g- zmmZgRT!22AQ7bB(`3BgA(rV)X^^Lf}{i{r#ZHwC!4l`$%)30ClfJ2F_od`R0$)icW z(5zmPuJMg0@YU6k{(66CE9c!7gIM$@B~4-APYuU&%pFi-+K=MvaVq_E^s``p_L<+s zDAsKonT@!>UvK$ei9ev2HVWi4So=8nSsi?}NnO~&taT=&fnItQxD`6RH!eXhLg;E0 zL^l~fNTRG z7e-DzA=L9J-1spqlVyNfSGNo}6dzMpk~j>hj6J;$cZ`%27m{t?q?ipRM$pD`4>HEA zkz^&2vWVz^l5@ga`#GfEL~Ds~t1`&o>`_F+h^eK8$=RtMgSg&-c^{b}DaFF-E(PUNx!Cz5!c)@W4bC|C0DUqANJJ$}~ptO&$$ zWw1!;^_k(r*$xoFzJSFZ#H#nn0T6_T!X1VX}D)ph3 z+QAKeaSb4P@6Ds~t{QI>i28Vqf$VJLoOQ+_9l-6;%Nq`T-JQVn$6n;Y+1AX>W25AK zc?zI>^($(-o~qCUJaDbk=U5`Nr{wL&L@fsWy zarc2nUA(C+eZ_r>-v~qn>oGss%+(fM144kwxh)b1cHX^o^S3)ufQ<_|S8Cq4X5Ouq z*gdPoM&g{_!&iZF-fv#_xLx%4(l!(LLLt#7#!vj8>J0p{H}<^|y0r@pp6^Z(PrIFv6#47jXOyj(rS5`E`ojRY=iqInL{u^Hu{x{=d@4uz!C z%BtNpYY#JVTc(gOvQ?C~i2nL2kyK*t5~ zj{*x1Th;tt<3Ihf8v^Fk0;r)yAi06_XiSBUu>$j#2qGp5lT%N!zS}CX{Vxg!x<5rs ziqkEi2n*Y?i*yDMsh|d>RB&fhHQupEcwxTf>$#~%9 zx^Q|Q4OAeJj_+|vE#J|~rYT-vBCRb2Iohs!x!V)^T-mLC^xwUx>ZKw-bMB{4dSiHx zNXos9Pj^Rl2y$$*AgP=(Vq0Qt`m&XBX#M}(+PN`zeSozXsr4#7!#9&Zr6JBpj%EvQ z5pC28NB|Bv=$@#09MbL9sl-Nm!$!`4`#_Z*Z7h^wA7-{$H3uBZ{LUM{NTo(oL_WHu z+hgw+p$#^mIh^NG?!B1PhGHI(V>G!epcDe-< zZxW{xdx z^E(}rm8DyMtEEr4BAOFo^P+I%v6j6w50OLSuuUs-G+m3Pw>BUY)^Sdl`F^{BAfs3ls= zUrrGZ0M0}8?C5pe;cHYKF+lL!?OqARP0;%OB0cjxqN#cMPGoHQ-}BpS6gnr`e1a40 zyM&y+s^_wS%kIKifDN{3QPf(-b-aET;8uSkfZN9;kUOp3Mb5I2DhlhLqe zcy~bpvC322s$gQC&F1ND$50QLw@e@HhbSM{$dH|l5G3$Tc?)7-hsE&fHB0r8vGDN3 z_h|!iNa)OW8Y9T$DycA2fW9R1%I%{KP~bOz+i}DWz=%h|2hXj1k;nf`TyWUdVKk0lXY?JO(b<;m^{VpG!v3@0E zYiRs6QK^3T+m1qB|99~~g}WUEaLP5lRdK{A;7x$o*6ps}7@iBxUOzEBJ(WkuTs8SL zyts<}A=@~k)%*KNU3ti&(^;|BkYG;XvhUfz!=A!N(^R#&<2Q;0itCMu(Mqk=?Y^oB zXR{?fhavYfuz3hD9(2$a&+8yZ|3tg8H2 zu~E1kl*xTX5%n94ng44NNXRo);_=i_{G05X#=r0hz+1F?qE0%K7FHTa$?WVJi@KIh z07A_h`cmSJK!(IZo}2xxf25>~AXrF6G|g#%c^MSOzc=D{f1>#$`s*(x4zH%?M?1r&u|m3SwJeSKct?DCst3k6nI%mW zOezkWkA9MOe&ufXbC6RZHa%BV?1RKs&X>QPoVX>Rc0oqiD>oGDgoxhhehyFi>Znzu zc?}hGqnTr)K}d}JzH}$vIv5=7WvbuacYs%JL9zOXHN$k&HG@3BzX=8Zw5mF{!A*Os zKSA-AfoEu|)Y&#ix1OWSEVdcmGV#rISgvF`$sbv1j=?s_S=m5<5fiK`ix+V%Js&y8 zena~{`Mj3~X+P{b`)r<*dhNS+fcP`8_NtM7mseffk?4o?vt5N+ zoMEhKtVs9O2!5G#*ZppopcWGT_YcORa}-xBkN`m<&sG*IFHZ=o6`+ ztJ<1qOh0cKV1^hx3%=FBNy9HM+f!%@PG@y*GTU)2w+bec%N?ST8_-Hg3R<#jbF25m zlC;8^SMFx=(FDKr`CM0gh;m*4g>H97$zlA6*I2SD^#epDlxMQOOe~wEDo%4n*KA8k zwIgcnkilJ@*)NuUJJa3_KEt3T{4_?OESHAz8sn_+(0#w+IuFaqjwFpvg~IS`_RtD@ zip6SlnCY44I+SyiIn(W$d41F4`aUnRZ`P9{P_hjVD ze+%%@xKbMM5h5@MxvtgdHvfGd_21h_hK6Q6J~5Srkh05x1eWmO!YgosX35@k&=a&)C${CfF!<#%=2ZyDYLu_>`_1{Hl^;CCP4wup@LvDV`gtBB#lWLo%cB>4snT+S2|0 z683T+m$TF_Ea58}6-=lEk*xfRfty z^r^I605}=scqA(OWAiJ_#))FUb%=MmJL}oi)>+?!%bZ#JN%5Aifw8QNlaP zK%{&$(YB*BmGN(BwTIPqxY^9aw#ojw$V}q}w!mSI*IKZn0JpmX>~Q^)^!F3m!n7&! zwgYNIKqfE|@{k!u?RE8SXTNFsGrPlH{0KtmL|EkLb@Kj6I|a?e|6Ltzo` zrartPYLjVJEjMne`l&p3^${#g3Um}O}x$hqE=j&36A%!;6WubQ5G zR*>%Yjv|Z;1l&~EdSPjQWl~euUvRX%3NnsV_;cFFpYxRecQxHIDy2Vm$8%g~WwE`f7M$U~m|e8T6*oNu5dxzv_1 zJ5afR;ba7=oMp}42-jSW4aqkR3;mlQUmtnfoN2+gdAv8G7mXuWF=<}l!&B0b;#0Fy zZ`=m?sJhp)0_>h9>N@4rkCGu+ISW#zHKyzE_n^KvVCUJWHi;&5_dKOpT#@8l|Fdd6 zuur$t&X-zTQQqk8hrIsA4Q@~AH^2XCTwH7aW;q+V$~jbHM;7ufD&qL7H^BhP(oqrK zY@o<=aVbyUN^H70EfAyJKKHtoEGz@=NgqrmU);n|NB+Dr7b`E*>jJ@NlMv&4eMt=7 ziPYx@rxm`1hsC17wv74h7q5I0NA?svx1ZIjA$q-XlCIc-`;=~`&k}hGHOd%M(%X8! zCnD5T)}q?jKH{DwZF|e^+$8Y93}LT)|D0qHZ+^Os6~nqm-2L>-#~RZVY=c?+ovFpo zpj*B4eu%NZhMIj-Zj3w@dxpN2ebXJ=MqP-*u)0QaDqPMo`D_zzEpTP_1Y0f&8pC&I z6nz(&f%H|`?>)72=EMPsX`Uzv`g^}~`pil0I_I@?-`96I*BSq&*v!QWrV{esn_k2= zT*dm($7if9)MuF}H2?jBRF--Z?ed@ri9eb51SqPv@1+bPcE2e{btdHq@(VBE9zCOM zAT&!+)go6Hjo+S(Naj$qxd;PQXHO*Pb25RJvYeFbc*#`$nJ75Rm+tQ zh8grM=U2m(Y;E_oWG6W#+;!nA-fXX92p~#rxqSmFI;!XiBYP^|`IP@yaRdHU5=Plc zWYtH8M2qVfMs5Ud!9DB?4st=p?Im=79pHyyr)2y$dH%RPN_?IhmkXBoN-C#qJCRM&Kd9pUe#h~}grZ}5De>x% z?V1MvXaig zHx75X8U3j_8U>ODQ?)1v3H|MUG=%J=7?{$tc`GLj{6OYlDzb^miK&*z^~y5O@&lsl z9;T#zUaj;jN7OdM%S`t>$|!Q6#`i(B5tc#Fnm{JJB~76lvZG+A47Xx65k?k;foH=l z@su#OC!Y?2q6_WYoPiIhmUH$K){a6b?0^?tJbe^ZK8biK0v;{ID4uHk>C3h8QOND_ z9WyP?`$XTKBu?Apd&YSzto-8 z&*D=}M*S|a#uV~cZ_-NTWZ3X`2Y0{ajP&D=HKy_F+|#@n*K|=@ODXAyUfebeq`6a@ z$B^W|(lot!<)gVopJwvMiY0BLW>|>joL-56ZNs~U+6Ue?Yu(y1Ac!-`GB|)?TW~{J zB%jpy?c@|`+IAdVoNFUh>vK-)m9i`BIR)!SP!|9Xfr;g;C`5Ml%MyOAF0xCT3GNTx zCvS2-VV4`mKds7GLdGOJO7FUO+1cDiem42^YWuf5$0-are8Lz|=yTGWj0`irtT59{ zFF5-aLgvr{DbwudYTLU0Jq-&!v1;uytMA-&T2bB}t2*XLS5I-_crzFWMf+Tb@4g~G z?1KhwjKt4a!|QDhhywqV#^c?ga3#Wb-xxhRA2TxpX#U;64LGzu@hANQhL}F zd$@wXG;vW}A-!?NkGPD8{(;5Ee}|Xk$yq5*&Cq^oi{)YU-RYRmvcfSl-gDVR~A)bF@O_LgCzC)$e z{Eab5d1CaB5c0zkX#(YL;sYL65~oV!wkotqFBHGLxTaQjvqc-l|tL z$yoi6vW;<`D>0BKmq@hc1{WX(u2ycVI0E>lHOq6?bXB#`YvJQS0`V7h9C-Bl1el(6 z0v|ayUS1_z4w}zyrI6;=ZHh5umW-QF1zMxch^BUVH>*1%6{4RJRWk3w7*Y8 zR6cT4_}$iJhu3oMl%|RO_%|f`EJ9KxlDZXr){Lf(Rjc7}^}=D?b}qHr#Sb@g;&A!P zNBNAIHGZDAh!}?C~c0)LqY|YSq6Hnh?wL`Vz zKb`1_2mY*?(==s&V;eJHlbS>mr7)*8u0j&^54Ov+@!JA$;Y0leUWp6 zAqQcHaCsr8wH1*sIUR*SE3wr+ldq}M5QjcN?nbxE>Brwzm8~9(KK!1OHFyhoi52H} z&MXpvrW0FeGfCY^kVEJig-%0l&F;PS^u7Ns-mdueOK?I9XkRuSj0BP-xBnHc(SK?@oh(5`_WC|3;%cDqg7O;4C&ZcA&j>LjxC{kfJ7iq4k8xcm<>Y_@ z2y6|Mu?5nFAY|#)@%s^8d*}2z@8YLHS7fVIzruV7I+#m9Qwe|Mqr6|k6F*~~b$!CM zZMD(O-;!yD;mQbTq?(ABN!PA;qnJ zO?`DLQdm{T`a;+8(_kA;_e{k5g#@?ASMMoT%J-mK!HbP(L5UIX7>10?z33@ncC)5i zdb=1zzFiLNeZ)vC)3$RIXQGSuysWvk_|k07Z3Xidf*k9x$m%>bQ6?34k#)01)OJ)X zB*jf(^_wwSogNL0(P-yvURW3uWixY!+O~NY1@?i#X4SeQ_}74I2!0wBw($5bczDBl$axQ4$}l z-}A)gg^)F7GYnuWAPGgc$XN|8z-siFBR@#8+6Bmm!FfaEvf$=IU%^p!)YyYiu4@L$ zUkqlQ{NOXYm$#U?b$I@mmeYay0ncq?TqOynx3LiO+z2qTrFk;0XV|M@%2S%F&G zX!R1V=bc>@(Na;MYZ0%IJ78uQ>myOohK%>4(85Lz4ra7PeNSQ&XIbpyQNSK@-Tg7S zfb_|GocGbLwY(y(^dMDBB&oT&YoFP+vq_Zo@ioX~%(!YdYh}2oR3rVVBQ}XZ4iwXk z#dbjTH;bfky}{H*5r2?1R^fJKKwRER|G}0*{-JmpzES)Z) zlsvQN-2eMNp63l8UMR3Lb6s;?znS^|%5YB%xR|O2b*Oy@gvX$7-&4*myPi#DJy0)5 z?|t~pt*4JjIw z=&P@#aYmYXKfZgW*u(Dzc;79qFlURB7ViHobD1x3dUT86C`}TvNW`bbRpPbJ0^=-8 zt-7(CFHMy%S`z)YDc9g{rCLs+IjUfLKUvk0?}FSvH5P=+>q)W1Kas4PE3;XX3|H)^ zZC__7_k%|XaRs5#Zvx&njBR}EO1B6z7^`x%n6P;ue9WEOf@(9^34_#WIpXFB6w=tr zSGmC+>2_L+b`c;@_yqGdF4%898Dml{DYVE@H(r0C>Ux!`EvI{uW+a;$;+~5& zn7p>X!07)B;Z`j;OTEE?vB7i5KMmAir{ZkJ zwTsDd&T>MkgZ&BI7Y^j}-uHhI&qT$$u+!Y!l)K5a%`ukpJ0}2dwn0u>{+%7V*L?1& zp~A)Ou?d{KBA9sn;$ao!p(w-*m3Ru>OSxS?B@FdHT-NTNly6gZ9oo&=qpi8te)Kf% zcm4rS#7mKMcSwv+<5w2@a=+@jq+Ji_mQ7Gx__3nVZf%>hl&p&?zWf{J3LU-k(<0TJ zvlAXQ-%WXx_?n7ab-A{8x`zLfG1@z%w*3nCPUu$=V~(Si!dhA3B2~kczc%pOE?KeI zPc170b(BlJ$IG~Mt=pWpI~%O;1$C_LRW;d<;vC^T(73xYaeRMYSj}AnOTnI96V0{p z&T`zuoSifiZ6$$dANJ!kmrgt>T!@r`yO#V=@?`Zc)L zWCglVK-pUsa5VHOPNyUJdJA$MpBYlbAI*+5`M*flC-!0tV~(|;mP)^hEr=g9DZEuk zGoIJRi=T6qZdkm>N_5DdN!x2uB-fszOIP#a^dUjd(yVNo%u5J&XpPv8ly7u-)zJFw zI6O()S$5wT=M8pTw5o_)10;5QbwF&x=O}eM#o>{kmgJ~h1<@6rWN6Pb8gY{jC!0&3 zRiC&MJ)=9|pb)Oz@Hui?^O!fM#Cr3Ml0li`1#Z3+W&Oj;$)AOmn-3yhnH*co50lQd zDI}*g&uOf~)gzT9PD%#_rXPe0#5V7fw<&5= zXn{*j>OmC>3S|6boTQ@L6^~7`H_yEx7b69a62ZyyvvcmYwG_*p^Axd7rI6^`f4~I+hD!uevuy zfxx4vEvPz?1^&_b{b*urc-GK?c-es>^|SWLa?bc2DJ+Md%Cx6s z87qx?KkXE&UdI%`%UvKNm)Zl9+>d?8S|ABUQyhp>W*WZ^H7!jfUr78{m zDJOaPqM{hCFRVvrOBiaSe(r91E-Vx(ROTpU+@J| z(lisOQ*0u|7!wlKn%K_=$Yl!shp%-ouEcX*-6Gpm`Y>;%y(LGDE^OVretZmn^sGv( zjQk^c@$ErSqc_Msk>K5z7H7I1D*=B;%8dJ-uHVRWQe;_dfW+bkUosQ6hTH+<+ZYM? zYz82XDKy-6)%p76N+vF%$Ot_`*oGj2dp*%19hpqSa@V+NxEepLAcr z;x-I;DOL-6P8UFRmKDy`$Ts_2@cLtSQp+2hI|x@HfvI8BqsJYsUe1aRhS;%bdq9?9 zpb$Gd5*^PUdQhMDzy!Q582gGFZ|lLn6)eCD`h?Fsr!|yiCM~lB*f^&esg9ZPN{c%j z7fH`AGGtnR9P*dNRJn{lA+p&=Hm*LBSjsQlcHWO}?F^TGbjnS6BC*1*X}c8%V1TXV zuv%UJv6`s859Vj#VmD{>cOvX)xW`WbpN(oIZ{)9s%{E^BBW|w*a$UaqI{!`HPr67W z>mqbGp?7525OhZRa|?bFkRE^X)X0^EasPUYsO?wcQxB&RN`~&LZzfys;NU!g-<>Md zJ~;b>c1ATX#hMi>g2RanybPaJyBUM}A<>`PyW0Ae)oA&hrs;Snj@)NuBJSMEdZq+H zE^=KlXK=>+6dQu|GN|`0WG;pT>mD2A5>=$gkiQEXcJTZ-b@WTVD4ve&-?1q#(!hO~F^QSB8@sNw2}~v5g(F@??JGUwtH=;gP9eh5u0v%HmY1bg z>u(0Qgn8!m+7JwdHFE#d|N3oSCn}{xZ|bDVWcTgCdudX6#6;%pkB}7Oz^qfMHQM&D zoj$^qm0y#jXLtK!t27FRQCniqp`3G7nK_T)FfZD6+IEKaw?1(Rw8oN!s{Q@eXs)Zb zoFee`am>(iCJzdk*$C%!^wTsEMVnDRR^n@^2Lbqn4YGV}AKj-3oGvDUUzOzk7K)mr z2CwbPdhb}j_hJ!8cOND78ka?F4+y_=n2<u?82E- z#-7Mp^tndEG{{(S!@J6O>>Gt;K?%{5=Q0+=9WQ;Cykovr(}vV4Hr>6p?0fBmm3v}? zyDB^TT+CdW=1GOzoQ|STo0dk-q@kiPl|dH1tGK1$XAf{|6H~sOVShp{5${!*l~v)E zX1$Oo`1%{^_wC)+PU7~(mT`3~cqyY(eu#j?m-F#nv7F8cbtPytPJsyfHoxH9xz<{b zAi}yS{;oA+4)sLT$(@x9%P{AM-Qj(rZ<8tD?i6%x7e`YePr~*uz^y_dN1_JpRaVWh zr4*$YH~S#`;kWm-ThT}^p365w>!v(=fq zm}_nQ4PR>cd+E`N3Z1! zy3|kaLL^tl_`{A432?22rY5hgF-%A9S)hyYy+#`*)2_*-erUBPv}~am(lnAX;M|IL zSU1A3B91oo+eDg2_-9~eO#Mt6&JLezR8RCK?@g^^1oLejC9|>hizjUx3AP^D+ntBp zt*JIshuj2pLDAx0*mi>bm{di}K24l(vCCVkzgNzndOcJb|5JMFXLaHG!;^Hx3elAP z=`M!IUigA8YNT5DeB&fiXA+R5$pURt*D|~vw^duTZ{4|Z4IF0U2bml;wPZI<+m)jXCcu?YKvBR-k?98u3%n@?+r%M_qM_ULKzElOIFGY8137amkg< z4!I5=U~tcmu8w4?lIZtUum)<1I}(B~@U``go#d-|9Er$f$k-bCGq;JUE zOGS6K#rrl8Ksg;xZ9DZ%+#VfOCAQo<7rDCk5C`9pwV_CE-*YV7^oC#_{-$e{_J%_7 zU99B3j(T>5-~wLfiKz8rny0}$T~~uiabyXU^4}_!(|E}#IA$PLc6lUaMrjW%_)pUWLp(mll*fJdZyQL|j459w|i=Ds9R4mwboyt|fc!K8H@ z=7)S0c69BGF_uw^X%M2Y4k=n!`J!_xbo1AQj)xAst+=d+bTiOhQB)>WrXCeThfBd< zg^sL=7kJ9a4uubgD&_{Tre2m4{o~1fcT(Ij9Z?)hPraO{fV8j{E zh>yId|IWzTWG>IVtFlb|bs0-&0saatVRnYiF1WLLZw&E{}jLJBxVY3h1ROt611$5eu{b3^l@Ne@Exp4U_ik9xE zry_fW3l|HF-kEL?yOcF$hU}Q3^5@!c=@bQob89Kb-$?-VF`!jmyo&Q8W{YtJ<$nsD zJ2uk1oTtorUcRT)8e%JZ2ZiC+Q1w_x2RhhFij1H_uHGl^3Mly`1WO~`jI;3(_YDwI z^NqnBD7q!i$^SgVyXJ$RW|3>+o`(O`{?3Lc(XWrC8FW)8DJ^~W7=tIQ)+O&ek=C+i zD0@43wRL-Puj1g%-|hH8G2;d9DxRA+GlWw{lX@W0_&|REs?;GY{qB@bDcr-sG7dYyQ?0)Zg){3kjs`@8!RT>i^V@W^nvPjgrb*qIDv|RqT}o;Y5ecbIhB0 zqE>y2%f~tcZWUV9Oco-MizsHv2IpQlmlayna^P|I`-weI zXKxESrHJ!Qc9+S#N52*(=CZg>iQi8Yo8~@itF5uA9pp+IQs{~6&`Y=$R>kBPBjEei zJsVD0i1+yxHka|4jXsg1HfdZcxBKaXopTJIBs;wM`RMC_YyFy+iqGWm^A;_++bio8 zD5!@X%-pw)-T-JZuWA#BzO5!v!kASNEK(JIk{N)9E@NQX4!1tePc*&b5bZ8E(|N$|ua-mJ@puMQHa33Pr5os0yT8@w%FCRXbOwwfwRwiYfTacb=Pl`_9DCN-au_fSuw&Il`;d^&;z z+F#-N-i-OR{b9s$Cr{|+PU7}e z8IuaNplW09Um1C34>J#`)V&7%ywIWN(YJ_wv^=Ugh;RNKCLb6my{dMNT=;|smV1PM z_TxBk#QKa!DmSf4AiOu49XO4C002%;xN8EJiAZ}bd2=^jvjN#(vORC-nn&B&g4gf! z6Mh*%L>~5MWwnT(t`ph?40{$Ec*4mj5{r8m`%ym+JB>1}_boCmCyH0a9WJNizECP0 zy}o#qt?1NH8U3lp)yKjA^Saer8ZpNlk~n9jk1XZ*Goj>R5}e{)8?^I8%+Hxc7~d-~ z>YqS$IaH7|+Fx888<S2^BIh-2xdYM(zbRK15m59D(GEtjLUK(F57`wSa@!l2V4wEitc_ZA$Uk3;8kO^qewSOPgcU8 z&2TcAb;S}zt8zX?Nzkb(>Ylx$ErSv zkDo239-#EeX|n9IB-v*+-o2{X7z5ce3o~>;VaM^xv)s<=0iaN$tJm_;qa@_~edWz7 zs+F;~;MtNBBPhkPL$Fii%q2D+iwC-rOH;Gu{w8bV7s?8t6)(#Et!g9{$Lt^xG)upi zMtN-hG2}Qz3IO5Q1fNoCvW#+3>t-+Z2C;IWrti|MX;V^jTyAE@B`@1b_RILKUsn72 z2}S#NJ?!E$$z{X7)PXhPjwbN2vmD8Lb7h8(3Nw|T|H|4%z4qp6SmxHuYku>B)80BAq|lyx)s8_0?-d zO|nmkk5BB;%yLfg4pVLw2JL#uvkY)&dI7st!$2v*rUN?VhMC01F}apB>;Cp{L|+X< zQAA<;hGXAR@ghsJn(!h#f~hp4BMU#~UJ?8E1?H0yw`s>cSly&VIq(RSGaZC*ZCy0@ zZ)e%zugMsLW*CY4Y$g*uPT$My_UX615sAWjiiYHp$b5Rlm%rkJ zs3+k42RC+X!(YCGd%nA{<3#5?G2__XbO9AcO}>pfPo!V5V9}y>4QzbR5UZA)iWPs< z3VupApXXhN(5-#*_2UM8W#0iwmAIEIcvx$YjDyzd`2o{R`Nd^{)?iJlr>sezng%e9 z&A8iR-z?adL>7pKl?0hBHbPD!;&|lTeTI4_>=fP){&3~xVYZAG#W%Gmy8k1?v36KP z(E7zahM0xSvEb18Q71}>Xc%_sSfb38w?U~3*!i>+qxC;>XazZ+&%~fqLo);K?Ql%a%iSC0>T)B3Hh|W>^RVHMzt4Xu;_*3`}8*D9d>!z(!>-0Obrg@{a zN5?|d!o&Heno|Ob4XPcUSk_tb7%|yGU>Yr-aR>Hjyd-jfAQt>M%~Wv;oQvu zs^ERZ;aQUMxcj@K=6XcZxAe=2z1{Q;kGx`tv9q%E{owiI zd&NuT@18o-F|}Cm<`023&X57aZs2Mia%j*fj2|QRh|y|e*|Ui;wjk)u?F`2VDJ4+V@!0!i zr{k5=wP{z>j&e^&p|+i?{*A6%4=tD8_`i1@xQbmvCEst07Ll+cd*;EUjxY(^=bA(5 z_H2A+eWbCX?|2+Vk`~c(PK?)qskvz2JCk)Zvp+pJ&}-v;R&k$}+v; zE&B70&P7?2I?oKa-D!vvlPUL`va7Hm1I)zX&$?@m9RhtHwVFD(XdafEZ`!?gAGh>4 zwg1_e-^kHM+O!paOC6_ZPlnE;@oUVA6rbcE%c1*R04{S<8nS?ESah$s;+}TqdM#Cr z*-@%?xrGR@vT&O%_C0LWTc0OseB(NDS#gm+e(C$CC$7gGe{s>;2g?oeJ!eD^(b^9DSb+p^)1ucgXe+6N``{^-P`QY&A_h5 z(f5Ukp8skLh)W_cX&c*>NWsZ!YCbp9MqJct3x{6Op>FI@X)qIP3mV^fBw(_y5+3V_ zV*>T;P|Cyz1+0pMEUZA}M&@^t~`#tTJ=`$qAyY zc0l{~yUU32VQ&XXwe+{MiHE(0Z9BSq7ya^W7MId%&$N6K0;|W9`fn}K@rI-SMa zjUGAO9{Fv$vg>xPJv-*wpLd$EX}sl6u+>sKr*l!v&@`dpUNExR81LU!BNOPiWSd?&L=V|NVqv^-A_d_8fsdG3VoUA+c-y=PAyLjH9@|Mo(6f z`1UG{faBrla4t2ahgD3|Y6!0$hkZQ=NIgX=HQbK|Et^-9cNybmGZ8<&Le#@LUGlR)Djqs(W4O zp1MMjCb3P?y?i0z=O@Qq@cD(Uo(xCDw$0iG!KgM-+r8b^pt2)x(Lgy!Gj2eVL9B8? zWt8+iMIx9;%GXr2rvrNryS$s%^~+7gD_(4uPX(K94-2Oa*ZfDoz>Xm6_+aVC!FQm$ z3jAa)M{@{f*%$CtlJpU0#8@xzX_*)SkteV#k;|l?^#?PxuNbfk&81GIIyV5%hih!c zAiC~ieU*lYxBF&~Q1k?V*-@&TY>R;y)$oHliV{*cq z_oS|rCs(txMCL5sUm~UF0^%aNGL8M5#O{RN1D4E-$;{7CgoC#PB(HoaF{B(I%~LY4 zco%+tjCn)zDAB~-&~1RTPHs}@!^lA)^qbCa@Z=)r6krSW7r>ixa&w9Ji|D_V&eg@M z-#u+>*!bEs$8%xw!%ymB7oTr;)I^+Z=e-!XD^{X+lgs!vrAJP{8mu8TgZCw;X>-!3 zi^LbPaS_1K5s*o{waBH+R!;C}#GSKLio_Sk*}EK5D_u(&-NQief92Y0L}3pdcPDKy zetmg*6-l??x%~F8C%&PyQ!kYL&MBy6pu(C)+iy=;{=VADbH+f?J>hMl5Ff;6lcwHy zKs1~o-qdh$JVyiROCBiIWhd;hO4_2o1$-zypY%oe0Nkqj57{`d=n!FjqUf4+P+iPg}+EvqXm>AXR>LU@(##eaBigT7(z z;~KVu<7weXS2Z(K$`#fmzK1f;%008Yu9|SqRq$8%KArU3RLUv|~BY zTyBvYK!)%-aPOSW0CL%qfTwa!>N*P;A zx3|2!Og^1nmuRO_19*FlYA@1k_Roc7hCXnsmIlX71NUm0=5A8S^Zoq@s8715Yn)e? zbIx$h#l@~in{u+owIUA1e|k*=pa>S9rEq+&70bwL0w!k$pZ#`z4cIGFjMH(eJIBjO zZAEfMdMs~uJ%WMwALD~!?Ca!?tkcF2`7CFe=_ z0}ZBMlHMgE|8_a9%AkEJkm)h>!H_v&3V&sNbjFz_u>OM~J2d>wz^a-_5z?0Yko$)giH}PTljGW8rV-cjs zf6bfz8a^HJ9FHPySU@xTy*8s#3_!=>5;)@~#x=OCbvNcY)tyn&Mk-~v?jxRN3|krF z4f1-u;Z$Ld18@-$_C5aVV)l#8!d8Skfl`s=A_(MCN7NsN%@NcIUp$NxII2tnf=EOP z-MD5N-R$JT29V9qz^If`0h~>4#i2=8W2K|3Cy5|wUe!pZq?rUKa{Cc5yZ_?YrE+^`q_6f7b4QCLen&IDID3XIuoU#?$bCz&Z|c7k61zOM7@H zuxp~&w9(bpKAcq|0k#ykX}Y$5aF-BeUKh*USB!z>P|GI+>z@yyu>kj}NnPf$?oC5s zW_bU&|%MvaZw|`TPVj(GM{sURt zWGxt%;CTR#$QGuQ*U%CqVFTvq-!)u!@?HpX=$8?-q~fDZd+vnCA+^e$I8VARYJX7$ zS3!0h+b)g;>|12F6`^qRtVPf78X({O9hjI=%!NpzBe~&7@jmRCQnJa?-j{F;g62tk zV9Jx136!)X6a)O=BT#Tf0Tx*QsWcv)93=Q%T`jvJFwh8sa5EpOrJGqo;qlD90|>D{ z<|oN~y?|YC#_?JmQrZ3MbZH#4CB=azumibEH|M^i*L@&~Bniuj)Ed4FMS)o=URjFG z9kz=*c?hh1uk|aTcL#I-_jiKt1{rVoq#v#zzF+EjXmohkd{&!h7yWTJZ_8wsANY$` z0TSiv{4@GtJta-76Py?nf`Cqw(xcyz^Wr8oTnD8bjsNIP@`f9lCO6KAl8(yhWP+`Qsr)0)9!oG+m_X&okHAI;1{csE&2%P2{rQ<#00uZE9oXxP4~ubI zxlEaOEV<^WKjj*t=C$gQ1XL+Fti-sXY616FynWsHaHp`|n|MmANF^>URHab>h|Del zWkl3ZX!I2Lx862^N@xXu4>mW&N8NzVmGZ^^V_AXlcqK!-_q^X?JWKvd0k&Z{IQLAl zLxQ(`9{~mNyFYOBUT>-~8f(YrfyFkbGO_N?S6V1G0q!knR1l97%$>uq=!G@qkT$CF zUPzZ_ipk_(G5>#GHhTPl^=uYm*e=F2>UKz>M7E>O+r^Pl$?XnamfPmGo|z3J-OY* z;6TKmIr!pZGJq6p&zmqhgR0b0AmKuw-02QZo1u zk#k&-_{r&y;O?Yw=b-2rSgB89e8b>6DqWd%2-TMY24T~suZ(p@n=pu5TMdosmqSy+ z3AqC>c!jQEktPA3+B{+*EMWn_@G9FAIE~%|UPy&ipC5|+G*ZiSSmN=F86OBWy?;Avh3}Bdzd-Dy* zpx33>V^G^6Kf^obitXzlk0hxAgQ6b&5h{Z>o-v^H%DGmjx1UcV=T)snG8X*09a(HA zEB(fDB0TzUFUOX?8ObSlu(2VvIi? z;(qw4x~&Oq0MYvm-0Jw};w6&9f@gfBY-x~YOx%sA_XUj2CFR99O1HpmJz!Hc!-c6) z)S<%dONP)FQuXxvj+Ub zeEp|12=vG0HZGa@(u`9+i6&;wd|ObTp9=uiu&oCum=+9y?F85H4)>lrOL=I!SjGzt_LBPq`Ar|;)(<}>osF-RX)~s!)0}f^1W{^v+8Mv>EB{xpuC~e^ zhbnkSBsT2D29+~E6eB<}Q6#u?P!GjZXXyjBqHyge0a{Gw>*B~-D*78Uqy}Z7VpnbW z%aWzYn~_oy38s{`ot1edg4<~nWjxY~w-Xoo={)AgUXFrPlF%WTnVR=KDrP2bLjeP7 zyB2gmbh(;?KI&Vb&Hs#{cvf4n$x)%psR9l%9zve+k+u~4C^dcZ5NRMvg zRD_~D)aR42KWDDmJR%Fcth?^#7K|#Fym%7v&He~1R8J+P=)_vSO3YWBHoL2;+uL8pf}16 z#FExW+-i-OVw)vU(XxX3(wDEC>qbcg2&0(@)`Ikv9gEZ-CYh;TAj7!n zjlnEgcML{S1xfvXq}ZULPu?WMYmJI}rXVF)1_H3rooVcanX`@Kgc$h-= z_o>HU`F~!J@fAgB@4UyDaUz|j^VsGf9)nMXgdp&!wgp7dM|6I_mVj$crBP88Hb}gH zRP`cOsnsszUUaTql;ptQo61;#5C`jBZVOM3}Qvh1{_~k96wL|tkUHF zgR630R?3RCtV)FjMb-SPR$#BHvvBqdPi-%l)R5Q)@Py7&Aje`M62Cn z4WJB!^Nx|-&oFt^k=Xne8dX!o^p5x`OssE)+`>hyZ>rqPY{g+SuUAkmjsp*nIz+6w zIzAI>qGw}JRt91!Hvg;H27NO`!k24udciH1^&w|ordV3Q@I48(aaQjxQgYsY1k&mZ ztJ)J!_UMB$B{VAojO$bS0E_nwJi_ zgDt!}v(eMn2V-b`s{Om|?uka3OD`emH- z-Wl5P&m5J+u60j`okqakCXw5qBFk*woij0>2Qh1snfWRF zmi|K&U}mO*R`;`K?D9qqLNwk0w!&&zTWy%UUX%XMAAEZvWb_?B0Pou z#jB?ZsE=nIbfrJCt&@rN^r|^tzF+1~*2i;i&3{`j>FN!6KX7DRLm`UpWiCz^^{6Fq z1ah1RSlM?#iJWNs%>Op~g`4%U_8&FRJDB0^C@K5u6;sNF@AiRQ>4zKc8J*zz`%-Af zCFsC9kw;fJj8&2KxflyGAlMj?Fu47FiABaZ=0N6Xi@J_>M(@}jcRV^* zt>R{mZSnBsx3Jy6HSPbD3cN(1kM-&EKzwOx?lf93r1ZGi-<*w}3`tv{b*ufLvf%3JSMfdZ0(|10Vx2_+ehN?6nZ4(jFwOkb%g{w=j6TH93pU zHKf(e8rYxf$sC;L45?mz1$8xH3w1H*D;} z@uno0Vwt_b`8m<3g)GHm@DY8w9j|Gkx3BUBz=Y>mK1s;^pf|nmrg!nj=9-lKG0K=b zr*E(IY*%EiRCk88`Exn~dTYA$8Q7M;=wisV;1HGYW@$7@S+5H`b}E12y7^ju)$qM6 zUaP@SVlY9XAtM98AZq6B@wLZhZY_VzMR$^4TuN0=pXCqrECGQfWwxjlo6j@IW~vq> zBmL!Yrexb=Q0JB)8HBT5uj$iKdOTO0bkSps{m=3jnU$EbfAyu?GQa+26xE z7~VPY{W(-j$tcCh&njP1joyk7tW*-qcs!De2mOj?ETwX**U={6!+v$97&(@jE=9=! zRCiOJ+|!HGqb(XtZk=g{^Fq8>FryjA-b<~=^js17$AnYIzT{zFYlOy%Z48Uc+XKl) zO)bJ>NejeDx^2Ce8e`B5B#9gpZY|wxQRfVhdYb$2_P}dS-$3gZM=;52JzN4* za<~0*oa8(3Kc!0YyMo?gU(C8uKvO$#+^~5c@eC<)T_0fNFT6^BZK!ahTsq1>EiEn zWYTIo{-YECK{$ASa^Z?S)(3)d4Ur?c@iL1UQ0Gz1QB1Y~1#>j_FTZj!<&0}++!(Q~ zZ*HPa1%QYsZp}`E03tg-&>t(8!1%4~Q9P-`VmktvpH(qt>UGt~cJ3pR@Vb?-ehx_3qwn+SaRPe5m{sps^vE&L^>$wo)Za z{E2|V3+YdI4|bLREMX%8SL^TXfOzfM6n#O_$V&oa`CvT~x#AfMs{Pl5w`#X0)|9>6 zY9Zx4B80Ngww>C!$K=Kts8X_jXp7nQF9~Ky z4ty6~{iG(smw{^9)^_Im1$2#6uu;6;7Z(utGFt%(|GVQ|red~VXAAEBB-TWqYi>1F zJ^d8+CcW|I-7sj|a-`@TlE-BObG5A~dGq&aOp5V;#6faw9;e`I9c4~U=jes&0^dNd zR$S+}aA$5XLp)WOKNW#MkWB+xdkpB2p)uzGGi|eMah<&!jYulK+Lx|$ewmXh%Yn`93W~Y69CySN$lv(c&Y=%%?vW$+j6V@$?^mtuzRilD6zn=Q;?cATIHR zLGdVt5c8ccjH!q2`lsjiIs6OOeV5g?ZzfgDSpAnI-t(R{kTzV#BRykzoz_4f*Xg+O z`)8ZKEc67!=)dn&aJVNF%DOulNGoqz{S4yI?tcXKKvCBM+h5dqM_sqO8;ANXP~m{ zCd_W`eIg5mQ%uSR=(7^|Ys$crdH#L8qcong1&yt{I$cFLQk+UVKZFK^7rxyJqpP6O z1y_+}%N9^iacU-+SzBC;+9_4-A` z|Ht>og2(f(l<@b6o_?6u#$UwU*7M>4XPIQ5Zh=FbOG?_|<}@_q78*(#e#0~=@!-8k z%vaC9a^|BA(9WvpZ35E*I4m}e&Dpf@Tt48B6qi&~NYJWx#oe7VnGX)zg<&)&7aHc z)tBejvW>sjLHe(ez%Y__39vtS^%4vT5Tc9aL44d@1d96r==f)E4Bu6^ts4(pR921} z@vLU_DK~9t9Pdg8I;d_*D!-i`+rf;Z{JboV^_MZn_CNo8KgI~_kl4o=sF{{LF4kRp zP{2O`#l*D3V;~0q1Vhg+Xbp|7V;ErAWqQdwXFc1HA?fcoi0-SJb8IT@cC-X}2U+{b zs`rUk&C~k0-~Zp}^y`3HmVc3>5G#>X`Zg#e49<_v{$AIQ}q)Y8!B_uoOHwt` zP@>Xk=^Ic+QU$ri`amh8=}f}fL}i)Nk95CDT?RJlFjfy}!;Ax8yS)eQ<9`&8gDu!y zKk_)H!q~WB+iDSRtDhneqQ%>J9e9vFbI{~8e|nIn6T3@yr)FNSx&_!sd^g}WYi(}V zd!7EvACzE7lF)@PcRi>u+2UgH{nra&JQINmO~8iZHeJW8BKL3Wli$!JWFjV%24q^< zlarD{-P_Mv-g>*t{C5^V>`610yW4)Ds9C<;?Mkn@8Uohe;f-ZbkAZH-Csn8rg%D*{ zZ|M&6$C4Qab^lnN98BJCB#DQa1f0WXpn}UgL3vy>$R|80eyqCbSA6Ut{w#tT_YMa{ zXeXTHV<>r)iWtC;7ttSP;k_Or9biea%M|~V6?(KQB94ghJz17tteIEKy!f#ZppDKw zD0i1OlGllSbACSG*yKA-IP~o-cg{K8)Y?yVl6V%+9~x`dnJK_}F(%^ee(+mZ{?el&y{g z?`avxKipV^KMZuuo)QU``GV{LesLl=Xz(BLVtV*KJ{x;^r;tu-N+i?R45(fHEP)Nf zOxV1QS}u-9H`Lq2DmejW$AVr*eA}Efd~1yqP~ANx$z~PWoL#tV*2J`3&wupvB#vnM zp^=+ClE0nOcx(R`;>oN`)u>SPdshzjQoT{E0UI=^hCUsy-vDj{J9>f6^cr-sjXm`x zI-#sn9ne_K5@3(1_Ga}jF}(IIw7e=SQYcW1gzaZujBj7fkoa};L0v{6=3b_I8J^z} zpJ%KyO)xHwHf=q_H7cx@IH==q~z{uBf7O91CHIk64fXopML_+lIg=-j8xwR?>H$)4~( zspO7{$A@QgI&0XzNZXRuBpOYaIUeL4DFKeIX`828<>iYf!ybBSZ`4v}zJ5aatysOY zDcixneBnJe_r5@U$M$oG#9{WP|3&%br)72I>HGhV(;%Bo#=s%H`(k}UR%M(!$93b$ zwB&ViP@OvF(SW)cwo5d36RQ(EH&?>+u*gWkj0oIpQNAVBZPO9tQW*WDVFA{ay8zBe%O4P~au{D72DT}#(cAeigiIm1}QZ3a_n;1k}I z6A9K8>2t*qiIf#(Ep-_Y7aVT^Sh@EYg67UEt#UWHTBLPGEu&_U?3+THccg0p;q%ni zY1%FOuephwPB12;n439WZdPW5E9f@=E@pc2r-Ill7$SW!=0Q*Jt~54NwfrncZc>yF zig_;lXTUe3ujTWJre3xs)}x^XFwYW1fkYwUX^eyZ|AJnWEv$Lcc$DeJA!h*|sCZ*g zn1kDL)E#(iuAtRsi5VZcvg!2|P(f2sxpRar73-Hf#_r#G?wb);v6@wQAcbYPdL(Fdn{R`qmZj~dciSy2;h5Py-#u^Hh{?ffd?)-!d{nvr#n$ox#w%7A_)(9% zKNyj0>t|L&j_i(|j$&SP+ERMCRkl zINDx5q%Tj+G{^ZQeMUsV62g8~7!hk#j@GOp28J*gYdDLK!7xHHD?={tRMWHVQn4{E zbD*t_?b^6tY==`bi+VrY)AYKV{q@HifUP|v{;+qyXG`C)-J=WR-ujpA-_gm4zeH=6 zkbcYQ7u}vsN72GIqS>$icf81hC!joMQ8s`$Uo=^`jwMdZ^7tw^lQXD)$t1m03S1_H zxovH2mX~L&@_|RyG!p^phbBcYkU0CINnU-r1`&SBgjl-vc0pxHkv_Ryt%a<2N7M10 z-0vaG?XaT=Ycb}op0>-gHc~>ouKHMpi*?1%m?sjU`|r#t+b;l!zu**Oe?c~&TyRLP zAM8c2MLuB(XOfN=X z&}EoV>Dd<%xHgr$CUR5h)&$z1U$W<^uYS^sr;6DYeC%d5_mo7!4@r0d-P=2K5-RD( zccAQB9$GU^XEw1zrlXFZVYu-$o*kjm_s|7iqbD>q_9hHorTU_~^C5u!7A0kjGC3k9IHByyd8S&`dCG^8IGvPU>mUEc-^Zp>v{)A7}r1aS>kBVF{Y(yx-~Am znq)ESmWLp|){{HLy!f6wUVFn;B~ltH2Jd#nw!7Zndu-5kzYhjya_Dl*CZMFI?%WKt z!1@kletIZQ&&;&vqh~hh_y4$h>$oVpuKim9VGsm{?igB9M7kNeMF~l1BqgL9VQ7Yy zP60t$x;=x=UY$-epc#M>&#UcBn733zFJQ_lz1pRPx=yP3{nhmgh)|5X2R zlN}j!dlr-+QIi`m1JHm6namnUwnC0BkMeHYb0%Io;kY;33aB!WZr$Q<)mOmdUEia<6x6^?}HmF0gfC#@)`Xn;*-J(4l6c zy?fs%%xLX_OzG+E)FnuCldHPoO#_-<>4=(|U- zW_j;@x4+zqxoFd)5txJ`%dZn^#=+(osE>%fmnX50R^ zCGw*q9(Jmzq|&Me5UwA=*Sl8Tv#6aaM-a8VTS_=sySvSLy}MwQMi zSWpGXQDCYp@*vVl$p`zb-qr3Aa~p5{lCpGU?y(?Jys(7&tWZqLb>V`EsaU()$$3{+ zAirpQ#j=UM1kn7Q_QaSj=;{#87A67eg;jw@U~gwy7!R0sm{ytmRR!66vqqr5lIVNu z**=%lt27int>cq>3}Lk3gNR-x;}iyfJ!a4DqUu+3uF%rG;&ROJj(-xY!HqEZYI=M7 z`A-)Dc;X7P1hQX*9McdK1YVGHQ~JqMIv^W(B`$ z84NZSO>mybou}xBuir&%*FNem{$1-r&l=}`r5|*nKPi1ZF0IgJ%g}h8EPb9_^AscZ z@jYzsvZFg_eZ23L%k6HLg52gj<%fMf`y)!gq^4 z@Ji~A9;pg}y-Bi1(yN{&6~=TNpGnl`6!>b{28J00WDv{&&aTs@Zv(V<;e>?)hb;RN zTl0zu6l1GE7qgahjAV?sf~=39?Eo69*vGa^Eld46A-zuoAmEa3@Yi)P`qI`C_9TLw z3M0C%cU_cT@W}ldGCi#$q6WKx7o#{7wV{bhGeyjBib6P84onrrA9y}wM(=ZZmq8>4 z**7p^9C(auiv6J|Q8JS4x7WgCRb|{F5H1-XwxLi z!j#`aO~k~U5=$EP0?9s9ZfCL@ZRG@VdhE+}nT2gR*r%x{5w595G8PxGP%tf2u=J>F;l{YP_dj-djHnN7Hl1@$OApyHOvUys7X z{n79Y{qmyJYpzgY!CtqaY7ysuymYq>oAwmdDpXf z?|%|4KOH;qUUruPmyQ7CgI!VaNHS)2P$#2Z7<8xp_g$s1p%2>6HoL$&(LgnDAx?7k z{n`IK$iEo^3NLSrFn5*P`@@ll?~GqP5FgnU7dN$ne1B-km~N8b>$&0&OZqV7Nl50O z<9e9>J8E(_Pi`o4<%SFUs5Bbc7h4|65h}m(3{#$e5P(^YF7AIen=X;2#<=djtzx>D zAvri(`z3WiP;{$CmQfmzHf2E|4LWSrg}l;eZYz}YH}xq0FSUH*a z>>FR;TNpwb2yTOfreeptMvDAYn_rIZR`gfgD0*Z$7LpGO{)Bt4_KmqHybxz%L2ppMrZC_N0)=qq`uBn>4Tw`@Yj{at-OW zBA3iBNOEEr)vY8h8Eq^%F$}&Coy18#loRP^es#@vv&<*gY)iGw-_H!841r%|2i=?> zhJzrrG848zh2^z{vdT7zvKVeEfdVXrd*#f$jIeLe1!hXM1I7Hcpu$tL<3fsWix}YE ziT8@9f@NcB%Dj0ZObo&c{_YdtP(IJz7Td(I?WD?Aq$fW+8`96(*Cm}AM8YWWMcRQN zJA5X(!}NCwk{r}+)8R?z#;Mh`XlFJP6#h2RhbULu?$Fl6(rd<{OyWtLb*~h$lAVSi zY}uIvA=S3h-BE%TDF+NgZoQi`V7Bo;yG%~!2`&xp?!Z)nqAD#v)E}V zE4x)AS|1N-1Z)rDO)|}C3tcRh?LN39MJNX7vMmn92i{2E^xuMXOp{jyM0t199WZ*O z&Y>*q4CI6VT@@LAkAQ*JUb2Z=D6I-XoS^XWLySw`+0S?CHd)>KSZ*G5d%tWfOJqg4 zW?^wh@dZ}aZK*X{DiG6vnb(ou+)jUzEOTB-_WlN|eljUIfa!l>9{gFu=*lDnnIRsF z!+91;drQI-HxQYQ&b!&ni+GPcRstG!@%~EF>lS_u-c-hXP1J^qCETYA2{bGnd;rFY za{e|#U6Uc>h^+ww5v#S4grsfz7C3pQD-riD`T=RzM3}4(lcm!fKAK=Zfc^M}N0Wt% zh@-q&A`B9N)U*CVo*lTSNqcb;xIGyipxPEA+5XPh4P<0@67klS!)s>As>vqRQl1pD zQ_vRr)bhSI58Zth#Tm*ThKoM?xK}&0^w)&Rm&lr)sAs4r!r#e$Tkk}b3YJ!yV@DUd z^`63%5`)j2S&oLOLkB(nnGkmek%x8inmx|S3~(n(@B56K~Vleh{)4*R3I0!#6gUjfWjVDY7KS7h>#*7{_YRKvAA2O=@(xIds)IPCNi_7d28` zFqoNA|GkG2nZA!!Gd+0_;TkWAHz!b&;?(`$_W-3k9|XrtxTNEg#{(d%a>~ts$Xezv z&@Io{sQ>;}CO?RxRngJNHz^VXR3XXTlR$3;QmMy?%l&Lwsj=2B>93CF_i_$&>kBj& zaB~0`ggmBBhnYcLp>BN+SM`VCYy!rni%{PqIex2UIV|_WZz`Bw7Gk(#OHd}}G6<@c zpjKmk)!^A^)!nQAsVU4Qvk&aehw}7KWBe(Gv`+cNEl;tlg(VDO>7Qn10)@cZX0-45 zKo(#@zDbzi95+{EI)>L-o$oXuHhpG1RxF0!t5pV8lMy@sQnuxPe?5~|%3Q->Au89lrLtRPv{!Z83TY|T6*?_2OV&S; zu*v0z99xQ}Rt=nNLDFv_f&Y4-}tT`{C4yW2Kv;N_{cIZg#GRL9)UQQv?M zOhsDz2^ZI=98*IdO7}wOoL9Hec|vIDg-u$r7M2M@MhZ+~SbgQdA0ib6iZkbcXE%T; z_?j0(4Zd6Dy98_-eGQ}CrsTG$5s>v@7oZ!<4j@u&vt|#7FOtPY%b|eimsU~)mo+PK zZUB+%P?Xr2RSw2Ft!##+gB4srn{B?FVaKtCj>$I*@A3KU_`0`2nJ(0Pc)!e3^~Syk z`rf^BCeod6sRLo1F8uc{;>=rRVk#Zm{vZWIPw1HSf-p@GG+F$}*Wxv4O2TF%g(N}A zF3eFs_Zff5X|*gQZO-CfJ#UK_Qye?qTJqGIjFaFtb^y!fxaSlh#LnnKP6b)}HCe(Q zV!i0as^M?^#;0EG$s02K!na^XGNv}?->uYOOn9|JaC~Bv8=o4EPoMH}{qJZhY#-^B zF%8SK63{N}Pt?Irys<|G6l1hZ1T=AX5rc#-781!(9PFyEfKx5TK$K(a3+WaM1~S6F zi+J^vysmsB+Sj^sfMlDKhR#H4#6-2$EVK*j9U??ovn8XQSjz>f-KWN;ZY3GGVBp68 zfi+ar-s5^8Revk-{*!RsbA>Ym;T&^ovMpN5G2EqE?88GRtw4nGjhiIJ^Qpn1%qtJR z$__MSF6njASEZ2Sa$D5jGEanOTEI-Y+d;#7w#wZHsKk11eCTiaM*6gYt;K5P)T;T7&hQA?f9 zSgzDV4$GfHetw_?7i7o$$hUk%@+Ia#RnpzR&l`7`(d>-3ME|60K)8dV?sW9rD)E7i z0ft-G)jeQI?&cRa2v9X@w#?|3qH={Q_+Y%o)z21t#jOj>UYEWP0G|ZEBn)`qkZa`J z!a5HgyFCiJ8A*1uR^&-XwXh-SK00R8Tv&K@4?zBvMYu1>>v6TG0}ey|<17aLxzA7; zdaW_F=^oDa9BxicK<^*MmVOf~C6y~3{yeuY*&yuqLph&R`H4ekSq{KcKtWmTz@{Vc z;rexE2PgjrKL+9xnD=+;@OP0Q82gjne25VlkA`^B?^=e=?=x>0DVdM!jKP)MO5ESu z3?Vf;THbJiOZdk^Hym8QaWaC)-|0>qB!yC8_dtv7tvdBSfx$Jqi3->hj}U!jRMyLd z-HiA7atYujk4OwU(W%w=vd6JchhZTgM<=TX$e(y8a;7jMwm4I8JF&vyPsL-aK7xYw z*nvI8Th(7S_n^s8f|%11=ZuX8eL4(1NXiEXk|`GA_yT=sSAI5rXPIM1<-5p+l~c?SR>nx8eD zc0DcNJHG7I>AXmdyI@T?mhMc9;GszqNx?&${u`T}aY>I%#d9!`7&Izi9PuN!kYBOz_!VwmAVuTPllr!~8N#0QNMRJe;K} zJflkcjJv74w=WX2HQY`lKw3La0Vs z&ca^pd8h_nvJnLNL?pb%7!~Ak#>HH8S<)h|?Tm!}V&}sr=f{Ou)UeS!KxpPML$d3R zqyrM#bzMsH0)1fx_(Y6JkO*|4^8t(P?j9-4krY_a za74{BcyY*g5z=RsiB7;C#+=49k@kp_X`XygK|^Lb<$3-hn%8PI3zt1LXiI6cKK4Sk zvc!Z5g}E%~g#XsmDLOzN;sH9^~9U&$?{=-r7 zW_tIV21(Bf-(IsTCl@~>^*5JiKr`=&awwlT5Y=W@HY|O6)hLCf^E(?acX<)-qXb0< z;`jU3bpW(-?KV@V!;6~bA6x(LX%gFVmh73|3hHw{hDuT!hzZW$K-!ON}EZ~{_?JeWVRnDjFzK4Yw3~Pcd5j(%EEb4sN^FO;+VIN)XFtAn%aD6P-ThDi3jLKFl;HXQXtW0xUkGrj5=w^Tj}<#K3|WsE z?;@MGi+!HRviEp<#WkPcX}YLP16lC6DzuS%n^GN4>@i9gUeJOjakpj=hIy z^te~aLKKz%{9OvzPvSLcH;(`u50I*Uh?5BXo>C9Yl#3~tKt!fNx*ymewIET z)FLQg$g{)J>NAA|Xo)O^Z{2lX>5nq{8|+=-jD!>S$J#;N)jmHROa1Uqc-^yX*(0OO!aP0=+{Hi@#EzX`V@Q_R`*4C|q|x(io-gq28zbcUc@x)jDF zowwKfkr@b4R_S7uYe*u=H$K@{{V%TJX7L(g-jmFq+?k!LSbv_pxy}E^(v|LhQO|ah5)W zFaYqmZ*zHk4-h}Zu=VZ4(~aF!KvNPCedZ5`=x+`pm83+ZwD?C8C^r_x^SOmh|1>B( z8ZC2dV7kDuw(R}&dg$HcOZ_iet{&@;C|~5Up7Q-~kDX4r6tVyIt68hMU9N_QT-2&c zmmSb$AKaR(SfP8w-vRhq>#x0w%G@=6&04*#28=cp5*)-tfRw3Lb1qE645pd_*uX?> zwuli=<5`4tA<~C@0h?-*ZSyBpM7KmCcU_dNHNGGpkY~G{KFo6_Xut-uXW!-a)J2{H zY9(KGdDm2eeNr^OK9asYBAPHv7>NCedC7bQ^xpUJWM65?GkOz$(_rD1liUXPMv3*e zhIfYWS9^j6fSS&)kT6lW&vBLzF6}5jK+?baAm>#l%EQKUZ@a3k%J-~ofeyl)?0on2 zx+nxfR?1^y_&P-(%V0rKp=}~Xu*U6B&^aGd9ZAV`oQpp*WE&jup^t zf`U|8wW(YSo4$3vj_G>Q6lbW!xj9kreTK&KYv2~_ofO(y;#4Pz`LID2`WCG)A?jmG zgbUBM3%m=2zUYZ~?+?GZ>u3?piweEtSQ*8&Tyo^Q5aZVBa0}4e#(=XjcNi{U-YfWT-74)d5NrlH2_!Icr{_j5RY&f^h;uKa@p<#_s_4 z#N3K37gkyf;LMSf|7d~AtQ#khTPrNGSGEW9@kt<~={iHGOX@ z_F{!{ry@#$6Q}nxmd!Aq9QR2j$SuA?7JLE0q=X753t5QE-QIOyD4Q8CjC?E@P(+w` z4Cez%D1qnu#v>xapp#V-rx?N6NM9!6%v(`5Eh=TZv@kORjG+qo$4QVUCx-a2)Yrw8&HT zJ$bMN0%uvio#)8ncDGYoO+8YjDc-pniIEUkl)ZkgRg@Oj`ohfIXe)|cgoF9ylZuv) z?P$2e{r*BT9SZ9%f&X4aP6cr?ho=ZrF`XX$#MphXYaYa@g!sz3x%=+;vxJ#x7=7Xv zZ9(=(JKLzw^BeXIzU2Jf;w8ayV=&W)hFlv!vHl3-`hE2Ax~!p~E1La669T0fM7idob*Vlp z32#DZk@PJfclAEo!d`G|RCCaf(L6EuZ{|!&4BZ-=MdpQkw&Dx6H`F>{2jZ z9WPA|5(@Rkl3&G992)0?ILiX-p}r8hv~A-$25T$rkN~jiIY21GIbEWy%{_|eKu947 zA^ewP@tnfzmq5u;cC%G|^BA2V`7o4AXK4awJwnVX7$|IYQ^A5Dxd3f!Y@#x=VV=3C z!7@JOF2i}lS!`G(jw!NXJTrNT?8sdL!HuiuQUJ>ErIbda*qa;-Gg2Ttx|YrS<($oE zM421b8$?gwj#k@}SnxelO*;f5{-wDsG*=lXiyHmouw3cOTHUW>9JC6ln3mk4yHl~x2R>ST@BUqMce47wjkpZSpCM)SCxsJ_N6A33b9s9ey7YnU z_Vm)wds-Jbg=biK<8EwKD*;MK+Q02jH*v)cay`OwRE{#0QKcwsvEI(}_ z_JBcF{Z3JBSZAg3bu~xSRRZh8_0=GxMMB?VSyPWK#6MPF)eirV9oPFQ41YZ}92gL$ zL(aX41uP7S6>N$_858^8mGi^4t2N~&v$&J?jR;nUE25~L88TaBC~jHR>pU!vCgy!_ zYN$J)_@E$jc;u#Tdr_jk8p~u7?@~E`N-W$1X`X{F*)=rP{9AsNq~uhg)9OYHO{-17 ze$as5lMQ;?=d>dU%iv>ABvp4*g^FIbAI7=h4}sCWhcBfOG3bHX7mcPTQ~UnP2UAvY z4V^?R(Z`oukJ7fUI0;iC>nz&j0*58fejRM+bvVeq+@0nr z0tj(E?6>X`r#iFBr4la0Ws|2Kye~QfF(3VwWpJwKj{q8Y)>N$l!PcMj&-2nT1yH$y zCw9+$OgX~*Z&}srvl|TNdAtm@@|DjvQ#7m}Z>xssTepOGJS%so{@-`Xjy>>rXaJhm z^)+F=%y66(f{YAltzr`0H)$R@#mojB&6}vxu;0A2CKvZII% zbg`GPsK#tUB;UEWlxwQS_agE-;h|gG{gJ|*PkSC(9#t^y1w7mkB1V~p!lxMc*fji< zG_ig|!#~`F*q>_6`+fiXwqK-(eoNxlTWiT*k$w!$QaL(?mM^SxD?M@=c)SD~pUfyW z-luVVjStfczQ;sFyyd6R_v!qy|3c_#QL>~B=!k1;ujhYq`bLnZ)-1gioPFp_OXN*z|7uAk zFJdM2d@xJ`&S9kJ)x<~riWggDz9tWm=#joY>T{yr+C033EdFNZ$s@*V|M=UITi&U} zsNCmIVL51>eKIZ{hLqf~KlVP4qPy96i2GZxPaUe~taBE9MfLqm?ltNNHalvHeAk}- zZTil#qY=KEYPx-?=#eS&@8SO=N^Oefak$&DHMhQZHEu%%wMIg>%Q1Gt8~djKlj#?x zIY6f%i+RUu(kh6-Q&M5qF-(6G#Vib8HDt84+|he(*+dNXGoZcK)U8$-s%Z6zHT>SP zck*J|f%A_g#zVAg!WeHQ-NUHO9RK*p%N^IzO1eU!cT}`_3dH}kjK=2eVj6@ixeR`! zyyyPV!V*dgHxBI(A@-T(jN4tlRi<*tweJ#W&2!-TW`0BOl=>?-t85u&| zrlySnYmX$bhBIw=xORO*`X;}!Y-!o_bUc4Og}?+X6s84 zQDVyYF5IM_6e$lS55C6|&6@jf8!bc|c^~|`xmuUblLD%y@4I}k6llWujora5Q1xVV zUjE)!#A>ay)O<(OKNzVgwfNuHZtRmkeIM}~nG4$`L4ppplf^2@j~$JoSVmsl`LbC3 z>upTLE5Ai)&tFlE*sVNI+tSQ^>iwmrhB~GhaKhRSXV(~wMWF{*J{TS0Eq4^ON5nme z#J-;qF=7!7TD)EBx=IHIl!Wg`Qg@yiWWYDxl5bz#Nzm-FN&;Z*`H;8@X4CH z;+yy^<35op3DY(->F5x!m1r-@XQ;jRd}v`pzOMX&@*{JKu+D0#q+J}9_$x~IfIVt* zySj@$;T=pYT}CSDfryre)o1k0<$nLxkc<==9uE6|*BS6xmLXnA1SkP&56}{5t)#vi z@Z>N1obJr>w53)uE4ZEkeAid9OG9Zq6=WR4hjL-bHF|^`YCGY|^IdLh2wu%TfTDAJ zb~Y^mI3~w5B{Ktzwj`}y=3T!rtfI919-)Vtl*78xXd5p~-JQ#QxFMFxzPtagp!$0T z2?OqGoNmTm0{Q_n&rthCC9q^7-BlWUEb@47|3s4Ok4{6G#H9 z_%Gnc`{FSDUC3}dKHsHq5RW}IO_@ITQCyuVpy8JwP-Qs>zE_JUnmJbU?LIH-?QBs1 zALLDw(d%2ArFK=*)z%CF!TEocr+3?m>fy0f;h=5-kD5E1a@KmKN#w^k_VbByUHNLo zCSJgGF8-D=$sc%tWEej6!!~M{1aRY}&9(atsM3wSy{h#eACe84lbzNM1xQ!=6A0T< zCEF*;)*V4q_-Vas$WoY)_Tp9NoWv^Q>zpJ`~HB<{b&Ljfp}0h!Pm13(v2pKlqgevyOUT6<%Qf=z4u&z|lfix=aK9 zA{F=b;ZzDAJo`D5xQ!f^6g-JnfDef_m*)}=PGMF zw?ISqX7v!8d|_=J z%Vi(+rJ_WI*&9H}0IwKXoL*;Ey!A%(8dskPGcBrAo3$B=vmNt$?%S|C^ zMWqvf_hIyK9Xn?02Fn~;I1S&|LNCjNNqqg1oB_PXSZshZNCkW2RdaI9J59x2(ye&7 zZxMPf=6^{Xz@PF%xyPo&`b+2(cZkHZWf9pqi4~Px&n+0ih=sSK42$S6$M)3c7$+D@ z7ht3#fv>&|<}TX?zvIezo*$=X&`k1xzAbfgytIjYj|Hqzn1qFfM7LZ2Uti$&*n5Vb zl{pfW-RLhWaZv;x)juHXw8kWWloC$eCS7B zUD>*$-FUjGBF_4k@qi>go)9EGQoV8w>~lT9$5bKYg!0f<1-46=KvH%j?{~ca3+`cn50EIb0q)QIQOl-KixJzn*S<1>qRZY(be8|cME*|!#Vm&* zIs#gC;~y0Bcz^B1cDxDPT-hLlV--H`d%s#9-o)?zW1K3xgAwS(k(vs3u6klp!;svTc=qLY7es*0&%KaodK6` zcDfWuROag*PG16}%;V!1fNx5c*5yHzFy6by?;R$vx%VnQ7l09Vt~!j{u&(&~j6Yyy zdTHrlTI*{27X7Kls9b#JbNK7lUA-UhU|-bS98Ht1>lsG9g8IOL=@PRbC`#P%Yhyq_ zz#wg2_LlYJ!UDte&mSDs{jDygjop5ZU)LLGzR;n)%Nn1Oi;(vh$GH)pIIH{JG?eg= z%D(ZXf^-R^^qgv`%s$q&zcL6ZE_g1So0R@}AG?3bKWr-;98wT}D>!I5)%sQh7l1z& zycZt_@QpZhc>Sc@1_lYqN=1=`DVYNNx}u&Ja=~x=~3L*?AbZj zXrZ;hR~n=t?L&B8nO|iK$(K2k`Nm7N5+Y>ipuv`f5!hoHe;FG1YVvGg+_7|be~LoR zA5>)4G1WtK%=OU^yVVbDl-_0}YVCij@&G`@cZZ7;leTx?8h6;Ws%s1RKJV@AH4nj# zUFkc!S<<{bJ-_*QOC>E|*)9I6!yb=498?#@mC76`S4x=#E}oV&Hml`a-!I0c2-E%8iLAME!Lou?=wu-v6{#qIEAYv zRQ@bW?2bH$X!e@-Sy%Y>rwTjVZEJq&v(b3lerMLa|B;4ch+M2Ck&3+bs_4mnG*^|f zZH}j&?`BClv6s>TmBk0qK#rkkPa;!*t*%>GNkrcQbYexA?@;SH3wZ&jvt`q({jPCUF0%!H(8%b}f5xV1wf6TwR|nB=E&FEd+|^&gx_QP}XUv zoyr%v+^H{$bK~tyRK`p$FOED_5&sP!pB&`24WcD3s>H63$C>j6G0FEB^EUZ3g$~oV zbtX9Nh)YPnMP6h#D$l~T^8WU*TV$Etpwl-~^-ZJN-4^UM$&Tj?ygsXxO}WHchU|^L zM#J~}0XpYLAS#8e#u4&pl4%DQp`07+d^8iHmP{w~en2gAik`dB6)t zRJ9~eUZ`Vj?$M#$0k_>IKnKSMn_zH+KJh__f@{Ds48RZy#;wnCTaYNDnU~;Q9Z9HK zRd##&kR?&~Y!4`lU^dWEzm+&)i0{LmII*GbR7Bv?dkRHTuBXtu*%%V=vy;qmA@QDU zG>mJ;M2tKCaET|>oUB7y4g2kPP^6LH#FdE**NGh#tgrfx5DN&=}AHmtsrRTY->@?_1_K^>a zuR|B_9Ehu;(%gct`6t#h`OPtw0B4d(O8uT@4Wq3l>x2@x=Nb_AN(2T;00rY%SDc63 zq3f6zZAK|#UKR`&2yjT3 zc8O41KZtC#DKR>Kysv*AuDhT*^l;H#QfBQ5(tpyE(bXMK61<>(VGb{BgNT`M*)PA^ zDB8^1JdQeMrOtMD$7jp%R^0Gk48cJfxpYh-)Z$fW+SbyHkn2)sEx_D6fD=U}cU}OQ z0p>A<{j=$?Q?u(W{li6x*152TgMhbpg9Tr^ipQ309T5mF&VNU_BsA{EH7?MroHtHOvypv>EjAlxDZo0`47{=26&E`p zT(f=E++O)Vcj@d(kGfZ5E{nZyN*?qCIm(%5f0x1XCo#@?mP|=>*Qgy%&^z{CsTAOU zM=5fH6S#_zKrSU8tivJ-aEn_qr2<;<$H;i*HGeNz(C%3%hC82h0VgVRDDBw3lJtd+ zFM!@#A-5duhWD^=O%YGNhy5c|`2Dt@ZYkgniB0dZ;|A8oZqlH0<}shKKhy&L3v!0& zfoKGl^iPW~F=a{?x5z;38O=h9%Ot|DyGFU`4s5p~c~fA@xc*P!;m^BwpqGoC0d1$a zp{Iil%!Yc*9X2IOkeEgVbt^w4m4UEgIRz=T8b^v%p``m4GrrO_9S2O)VZ3T?melf~U+cJ_Saiqr zM18z6Ugv%PDqe$bY`tXE=;5rRlJ5&sVsylj3-4SRBKmBm+8Aai?T8wx`jt1mrFHJd zef`QueJ!dg@;bFjGv-&?wW4HAqK|_`D1-fKT)a>5<%+CgH1Q%ZsL z(^b^`E?~qXbCgHHoGuJaW8W1Z;^b0%-G~&*M6YFhiUfXsp=2YdIY7y8zMgNcA}DyN z)8#)UxG0r9WYEYG_Q}9X*OGRzsbip3K2Dhc;7;WPIq1)jyMbtR(e7Q1(=I+RaP>{1 z^00-?u2o18n?<@EHMhRt#%rQUC_KIESB2t_iUy4GC(N>A`p_{`uiu`pV}iRQZ3QrV zO7Dwds!3VWKzrs~x+I|@SW4k70>0{Uvv(ev&RmHjdU8^3^8t5G6z=X&OKnVdXJ`Pp z(hg@w?FILvn&fHp(jH$3zQZH7&odNRcu3Vuu+j$(j%h4AX}&_PdfT0$Lolx>pH7#$ zpmI0mLxnsf4&x6udh+SjtLTrqq_ps_20n@3r%nBMsy>tYvHCODJ)iQ|>?#@%7a_KO zhIG&qWI9)y9WB)fDszFN5Vn_qQOgoKrP^G4%Y2=}*>vdY4C$>cU!;Zgu8tS%5s$Kj zY~4S}aCiHmiKnJ|Ap7gjuL(kK%`)kHac*VlbG}1ka-r_}E6=E#KhJ!`lI-*lgy6_1 zZmax^Uk{QX`&tI&(P7v2D-$vDh8JZw$`We^wCRHOaJylR>y=DIU}XDi8Wi`Yl|_tT zjr2Zzy^_=wIqFdDSStvmxph`(O(^yc zS>b=H95|*Y!e_s{M}xw!R-_klIDwkRe%T-OlJF_(OgM5UKq_3@(wNl$r=wQ-Fiv~^ z0{m-Fg_p94zf&Q>f)+6oxv-kp|G=}za~caotdbiPbS5k|CBxx!Q%Ng275CQX6+l*W zC>WODC(v@kR%4M1yRV5@RMD}y%IHsT-<8{fh)r~D{bYiBN6QQ_X30LopgnQs(22(W zdM~SoB?bd=6Zt)agRv|Ic7I07AMlUizsC09lE7dX+XDIQE>{%Z9K?J_K6oqNWaLmY zvUt^l!+`$;3NfWJ5lXw3#fyBh*#A62whamqd(dckFHY<6Wm>%>@gr$8JfY7=Gy<|Y zlRg3_$}pG7pnfxHKI(ZOil_cu5s;QO6~$VO==d@%09{=sQddVPLpTOqmLTIIf5F?e zRDI++wAkG~!M&{R0x)vVqt%@GD#lEo)<_4?;h7_j=;3(c;BZnX7)k(@MGpFbI*c(P zafSA*j@lG}N8K#)Z{gf|q`XeU1ZI2RT75zp%iGe*p{t)lAW2Xth_fUB7VdFjk1Rf8 zp>%t+iZcQ-f^7!Rpa$jgubWrNe1=W|i_$)`fH6F2cOw7|`=$U6_7Dz7DcA2Q7?Yf2 zD|_?Yd%To$RT>8<9+TgiR#lJ=oM*X}s*7lHeuR)B6%RPLY_o^9mjgQI;;mXfi6~RdY(4^%h}`c0V@zOnp1?=bI_YcbOSGs@=-=-qSgTPIJNA65OcwcU zAp;cXaEHZ^NrK(vZRGkfk1@qbUi%+Eh9NFwx736}s^n&YNkzxFW;6LD7b3DVOJaJt z48f%l{8nnGfk;;t-rn$x&19oXl=u97b6Fdy2+n%pJ|o?6X)*k&@OIs|%yuI66t`8= zRU+Vw@pQRXYmS}oWzc{FI-;)sG={;%aU}5gK?;|3rJ$=x3QZS$CQV#$qM9M<#-Qvb zSrZ*DCYL+<9U)FZ6m=e05U4B6A-+XD(wh9f72%mI)LGFih0r;HcP5A-zLYJbnh*C0 zUX%L_u5$EqTsk$bwH02Ar>(?)+1;$ll|Pv~PB75fB*=oNRBs8LcY7sXXrr*!Dmyb* z>KQg^x4lA9n7hFN=kv^3<>JM0?soyoAaUM!!&mVC40_xJnBN*-xCQEIpXM*7si*eK z^MK{ytNHK6as6eVI%Mni>XpzF6-XI0>lSQjq}Y|M8*=QAB=BNO;JfMkI!ZX^b&B~N z-N*eXIxFODiKVQjcX{26^EX;z>=b;xsA7x!>lw?0Z{4q&biG0^J{o-lQxZ{7`a>vH zHpUrK2CQdw?Sj?lKGyPkD{2?_TmwcMJKShg`bgK+8ee2hk0{Ycu(LAzCCSSek=|4P zkJn)E_tq{*J=JLa22&xCTyWv4{a@p8z)Vc?)M^2Miy7JLzYtpg_LRCOw4DV)suK@; zh72|D4!*gw7JE?E8QUo}!Jf{rClD=)u-P+G>&TJx;uKOr#Foi1gZdniYJti%ax>;Z z^YZxG;lJz<(VDd#l@V<=RmmQ2e;VJOb)I!J+aNVt%ax1?$MQfzDUYTjf@5Vk5?rzH zp_B-$*Wn|ax>aV=@gu_hEdX>;j)l9ZS-j`Gg}#p)`QsU*MO`ra^t34(tR#hDy)?&_ zz)&!e5F|gr7e3%n-67P+gEl0gD>1nTiG`>>qk5Jy8HUIZQk`X#Lo>z^Cy9O0K}4=3 zX)Rn#HCY&G1P)4P@Kucj-5iaP4>6in4A>#w?}AY*VhGdsZCV^&h~NuFQ=tEY zee!52zCE>hIxsy-Vk*I^<*^t!I5moSSmT(pf9jcTX%_(8faciT*HCcOWoQt>gfRN3 zFLe8mJ>k{zMGlz=!!p%D%G$}n>ZaO}=f*J~?)8tZXQzwF(X3|<5DxRn>iQ^L1AmZa zRi0%nVKNVG-XPr;pZy<^vxT6bVij_ol)S1ByCQr}w6Bw-!hAQ+YM*lyV9Un zlhB`I4wxXTO1KF{Lk-xjQ=aW6EQEt@S2^YCAS)`E(<45!5&i`~IWM|)ntU_@mYYiY zOf_8N8g~c9{%Bt}cKQu4bcQKC?^J)`>dbblTtUw8F+khKn4*8uYuezF;0sRYeoc%# zBk}d8xZ~XKUe-gR!S?Qr^;@L2D#YBf4mQjs3{rvJ72JfzT@(tCARPe z@ORQ>WDV?Ghq;Or)w(Mh1kjZA5Y4MGplkaicnEVgn&GdZN7=J(w(J~+3;bvl^O5#N zj5NUPCWykK45xZ56$XV?EkTTAF?bmFe+4q>G9XcJ$vOpmd zO>k~RX+!G#<^&BQLQMX6H%@q-a!sQXlKm-{nTQF!6cV4P5a@mu!VMy#Jdc&Ie2&c& zeY7!*(K!U%1P`0_QbjqUGu*_J);=}pYEHb2zx>K@!;hthii8wK-INSJU;l8-}>*iHPebyc8({@(_Sd`EibiSr`nv#s{>4jQf3We(b))GrY#0| z=Y$T5cDh8_*>EP2t?&Y{9o=@2z2?q4kU!CwFhQtY*IukHq0*2_rUJO~;>)}=XoY^j zr;uG5pxxg&$^d2FpkI%Xe$J);&<08_Y z)n^_OSgrsK{be0fKBLCUXH(IOQXPrDK${xv&4su2B^=GY2!{b~h|vcyIJ5%}!e)&7 zjgXZ)S9Ycl!bY@_P`(iGwqE1`zhSu)FyPsxXF+ff=Y()#kew1)ci75AZz!$EzF}onUl9e#gyy z-}~O*e|hA29Ovx4*IIj(&zC7A^%oe*V8dXPWVMD}K!i@AkpjZPZ#!vWpzq@b?+bGG zzWr>|d`27KaapYMNYePUgP&>T6_g8?E_4nNH6ZrXXQLYb5CIzNsb*yXQ z)$_|2R`(Cnr$4kx?NDsye^CZpkCLA}d9g~uhS9z0(!G8Ws2EBrXYMj4lJt@=PR3`Z zx?zG*rER1LT)+oYm@nE@6zQs^OoV<~_t@$;2^fZ*2yJjrQ`zM5X&^a?E*}9F`YW1c zIOr>cKw9h?bAG!SeXB?nL0?3F*v`p;QdnWUj%YjOm-^gIvi60)u7<^!FS6IS&5EWf z{qaI~{2T?DClbf9oS#(dx8NiHLWcw!-{g+*FGAi7IZV&LJsPwxa2{WI4;%>iPQ%?3 zl_)5Exy00Wk*o8cYw&WR{S7O48vu2ilvzKV{6u#@^>`SY8;#L}9NVbM#yxvwAOx@?XBn|lN z%DLSTkThyt_c3nd>`3ohO{35|k7bc~@ysQ*j4buVZ;#lm+XLtBwfr}$IF?M(w-)l8 zKL^QAt$&kkv{5UW&HuLi7&6jvRKbD}JvD!Z8^iw+NSgC^mcfxr{rXzQjLw6kHVUpkEGutnu$izAwE~wT z2)WYA#EnSSpgyEy{W4@c@P1$vWO_XCQtdbU_0ZCl3N#hm_RjfL>v#fNF8SSIadQes ztzI_lpJ92NWluoH54{>msv}SkX|nw2r_!@%rXk6A)4exvHHR9X!l!a{eKa4)n<&2c zHVjZG5UnX@Ovdrqe)3``lZGE;JYM~Vvml6MYe82o$ndmZ``K3%#amS994lTV@(#ZRwZSUsUSyc`6+~ zQP9rM1du73q;`78w4%H%N8_#jD*x-~*F(evI;yZm`&TD}-aUB30YwzPtv9E0=`Kaw z3K?+@Yr`Zplq+8n0|TbIvMbx*a`-*tI3J$Ed~ytV(rjT;a`w{ApS--rAw;3(P(rNU zp>8z}Y7!07);rw_Tv}5BeqjROfb=Tx0P#yuE#{aV=PxD*SxR3JP3?V>(9-8G-o?*+ zY8)k`3JSf--V%j}$@IOXsg7LbIe2vUF;_Bn7JV&du%r&HOy>stJ%|Xz2RgeNf{}z0 zOA_0?yJ9dSN0ZVR$)Z1uxOz#0Dkgs@XP{Hm%*k=eQIp~F!^CaBor~GYn>)Y;yJSHr`qNp;3eOVdr;W@HDp67& z6QJa=8`W4a`y{y3m2wzLljw#Ob4!;Vkn%;ykhJbr2rK%7O%(-tr!R>b7*(9u#DuXt zUb|HgVWrHDZN^(_cX{FWLQFRWCUwh1lXbQ;9h~{n^LUqiJno@+#9r#2e)MC}NV~Sh z&Nv?xns82v|9mo!l=1CQxwMQfNZA%?6yx$7viCPRogb*E``T;J9uZ=m;@@jCz65RV zw%;RgoO^VniHeA{DNm{%QVP_kP=O=+xt#(j!b=kFknpD_Do!`_SkS zb~-f|Bz~HBfXOB)uYqXlC#_yveF?0PUzj=S7PZKjTp!vOdQu+wsOmm^vxp$9*)30r zE=r}K0%%H_iKe0?u;5S7DO$~UO5sl2Xau(pn1@obX?D9@=(CQ+TVpj9-A%*`E?m&H zAs)PQ56~N*bP$-jrqEC{A%4)8mowjRxcy}*d6lu8=@TO@IAQb#GRk(D-V`P9j&h7+ zr0Oms36=uu3LNwL`L3NB#~$@7%EQJ6grt$hCp9X&PCl#?_(!88l#T`*)NG)oqEW}h zRWWV?wPb!7VSj&*KS`A_cg~ah#m!r?bt$b))9XpoFO7sAgjcY}bH*-W{|)tvhspOq z(|X!2_$(B}Lu=g68bmMpA($5?>ngu$`Ost@EDxgbkVs*_j1Z>_e8Ix&9luJ{s9wr8 z&%UKqWL2`+ui6KePMkmjVV4JGV$##`EgvmPB%?HBKFMm4)%k)(Duufze@?F@ur(-v zZ$Nu5kF_(0W!Z@ z7B6I+UR_Sqh6!16X*OJi6RFzE#_IiL`Lx$TLMyPcP5XKpB2}{I{s^V{M*PxbjrMXF zIZWDaI#+l`(Ky^4>!i&Yi2dfeX}epY!+i2|bDB6^e(sd*cij?I`4XM|MO@K?SpI8= zUhxlxuifmb;GVuhRTa__PqZ@syur^ppyc}{{_JYCoh8a>9t?jXImXpE6#SX&Eis>C zt(jcRL*`H@0VdTaI|e_tR~o$u@2X`tgtN`xA2lz{RhK@Z&h?WP=?s{YYp$n{ytaH2 zyf4^AG*-&z8itEw4O%}>1HWZDZITy8Wi-tOI9;n8)0;K6eT;~Ho>Xz#`$JqPOH4snOqz@kQwAgF_$*oa$UV&FGp4*oc{-(cnm&q!i!yL2hnJ)n z%6Q%0P>}+*LkPxy3t^N5za3MBBR8Dqu*vAW!EU`l;4qJ&C_!!N zLE(`lqbG@zprYcEi`o~EknC<(^^f2onc0cb$_5pfpX^UfXA(akns#N`c;6ZnNq<`C z*e48aKa(leTs&+*lBi>u8S@X=^JbvIS=vebHetW|Cv)-UrQPPpQqQp|X!DD^!dzL=o zwal=9rSv~K0;Ct;2SK z?a4vFALU!G0kvFvz|l2!#9;O*j~QSUlW#T#M7A}x*)6wJEMOyt;P&A{ob$MsZ)E|BjXcWk_rn3?}PWHfJ&Je=jXZe(&q zYX{heL?%*>`I7o2sT4I@%Fdcp7`-C_{UR@J=Cz(eOrP_`Y7^5~h+k620npeZnNFM@ zqYx)4tHnr?0DrG0aqSKYq1B8|10y&xvK)z-y%mUe-E+p0Q=vaN8YqCIg4p`jB?rClbs&V#9szc65$pm7q!rS^9l*CUa*UsP=eU zGMzu1Yt~%sLyzFqxQKr}=0wDl-rx{e)oQ$n!PmJM;FLm<#GS0$>a9R`Kxf9Z7t}}x zKoTTuIgqS)8i?Mkd%orQmsE`;+c(w#Pgalo4cxtLuf;dCJ=jy)KaZ$n?@;W?^Hw~Z zEcrr-N33xJr%xu381oO+lrtpZf`lHq_9YKI>;{!GcRlBRZPohqZl^c(7L*trDa_?? zzIZLNG4^N)B`lW|5U@1bV~bsf9Q%&7x0iqApVvO}R~t#1X0X-R@YGVB_+8G9Oa;`r zA)c32`IU$W<q@@|U#5vJ{3q zEqFV&QzBsPmaqlAsC`Vek; zgwN(iW}@nW-Bt`|Uq&X8{(Jk7jlhul!?yUc4+?*HccaYfP>tm8rm_KlcqBb&s4nOGAUF(GR#<-()X&M z3OxKJ94}E(SvCPj+C0bH=5KJf4}Q9+4MLKsb4)b=KNi5X}Ky#u9~oUR3HkROIiaf6w=fG*P5 z=UeJU)GGy)WKhhp3z*oOYZ3-aAsGt@B>~(QKgZUZI*SQk>~gcP_9LYvCKvkaprIR5 zxL}8$dZYAXTNyvgr9@Cm9i~RI;0k0bDbT6qK5NK*TXWxnrCP$}D^T06NCxO+V@ zvJaLWUnL@qzsB91nEv*F7NqVK#bivGC6Z3Rt`}iHq81dOWK8BZxqv4cWT_PpCWCok zam9Er>Dh`{bt_U-zve(uLwG3-y_@((bnNQ8kKLN58{S3&xkr|IKWm=K{gTQm&Kc;q zAE(ozR9dvlh7HT#(lby<-Zx}9Ige>L60(${Sx>)-_ryR3k3%Isy>d4$QbxBCQQB}Z zyRq5cv`I`*O8=&6s|HG)uti1g6w!LKTrbRi^3c@TJBhrE`k_eW@)1cxbZYII!1j`E zLWPT5c%d|FTHU33%XD=jMGP{NT_V)iH{P6T&f8$iJdr}el3`k>`B0hc-wMgjtQ_~o6>j2~ zuaBFzx%tb!?qh6Y1JW?>FU3(APpj|x&3}{c7^!@8zc55wL2jBVf6s`ehS}>_e zaqJb=n+O24L9=;4%m%-+=8bqEI80M`8nc`#CF8wX&?r*I4F6`KvYA5SC$*!^n#+h4WI`F zy#U*c&yK6rN$e}aKH#X@la7wEq69kh2#g01RTVeD%DADj0%XKC{fyIzss(B z?ZlqgSrMm09{mPb^Y^w1Obv=k(n*{igziWi6&YQ`?Dbu2@73FcPq z_S=5J9%37^W|Leo#qa|4lm2FzekG0wQ^DWVPGY==zyFDeW5T9idnh~8IONdOcICd& z23PX};^uSUInh{$&9YQn)oJmU(EOOLDTrdpNPI)z+f@y^uZvImWlV0_#Drt&Askyj zoGF`-`d)ZJnEI$y?%ycxZ@IrOa(jzNY#&{3S{io>HLBaEzxL!??t4|*K^mDdO`GZ? z_516^0L~kkd{1br@c4D@PjLSCQOcI7+t$-tW#O0nOoyqFnC&75tLy%=3G5Q*!y3O< zsmtUi6dTRC-l|9hj)T|VW)_bJ_X`7?#3iiw)3e*RdaacV=RrK4l+`;z<0EIEnbJf*Ne&lS1Me?lm{b7tr?xv~9eTP*sui7OlLY`Cm}eW_W@=zKQwZ6c6& z(TS(y-VYXmEQeskJGP%e_7fy?{PRJg=VfFKBy+vSUf(HBh?g^u5tId@BE~ngfjcGee2b=EP+AR`>`16Iolsoq|*0fT#Y#-8xT*cTxEg=BYaGUHx(KuP+ zsXyD;s2^*>*@CUiEMeo@@Pb-fYX|v&+qULIM+P&`v zmOSuiS!A|<@zAD*3(lEaT|AJ~W2@9*-;5^1W-XpL&8%;3!)i~p0xV#Hci=YcZU=l$I!$`Zq z!!mRX9~eKlaTXxqa`IVk3x9aSS&?{V1Gmy9#12YotIG+2q9j8`*dY|XYv&mZ2{uw0 z?^(tN`4b<(Fq?^1v~{)* z6Q44Smd*f zOIVE&`I|O*C^C)on8eRkBrEzF&d0JuTxP>m6uM*aJff|LFxj*zBki_mJL}ZM+ClQ6 zW%X_?9a^m4TX8!^!??&7Z$+%XXhtVG-+Y!!t{$2TVK=uoL61G6eM$PKb&Tc$dj6&s2 zOoQ9AxZWbX4f!zcK)7w9#z1XSUX_neX?vvWkNbsN@srMDO=Z8YRQQCJo*?n2Sze`l zF|}1jmR-i7esZ-3zJJsxv0YkO--l5!Y8y|BP11DyiEhY?Go@LzjX2+27{;+pppFPLNU}oC2fc&y}Q69hWUB`qkT}3=0f^X5Q}VNSD8lM zZ^M_8G5b_aglh-rZ*2``bB~Mur25;`VEaFZ<~gRRvgy>EZ%RnRMFbB^#~$PFo47^+ z*ny#e&9Z94-mSxk_R<;3Zt`d@=u71`rNdAP2)i9ynY`8fiON2D(KZmO+r9tJs$b$m zD~jkWDhd301JiS@n>S=N)mxYDnfn_3F2#snkNx7vJr|Gmpq+^5CgGgXsy=)_%fo!M zt4QQYX>ggStXlHUs3Wq7LJCxi_bUE2ZDG;|lEtShYlY+DYf-$!X?shur3%7|bExqn zA)O@oc3+*TlHqMXFQ%YG0a-7!ZxlUff+P&1;Pd^*NhT67Qmn4LPcZb|fu!h|sbMbDdj4R6@+`(FSgRu=U#4Fw|4?aQfuHb|QL_RKl z(IXS;J&hD`lOrSbXCgGTw);1 zjlsv2V67{xFuwt*KG$zuU8~vMJl6z%3uF6`W}Zx@j3m*Reb(h-gmPo!sU;&#=);>U zMacxrMxoaI2ePgtleNPvsQQO6mb3UY0@*llE{f9aOWNSltZK8(Sqx}l;^SRHvk}*g zdXLsFzUAKVjYl4Iw-7gzTXNdxG3i&=RV^EVik{KB(8l%Fw#aR>nbDxL_F5SZXs@uX zvDL14019Z}LfMv%*lHFfifZ}h)hwUy?LqzP z2QE59AGKzhw-7*)c(tY{YxL_PUYx(e$DpgM?iLr^hI(W|DpdMiH1g-I%gPZ@PdRXJemmP_WpvK@{$X0IW>+7x)RVwQMeh3;xP+k`Olq?bDy4R@8(VhxA0Gu1lTo@vm1ZplL~i(m~P> z%#syD@3=SKirJzmOL8Lp25L@6m}Od_pZP$}Ri3{MkD2&} zf3@j#+|TmSf&n{CnMjoRUgm3MU%Tz9cGd~ggz69a2N|NwDo_n`+wShKUhPNOZ}`($ z+6F|`v(?3la}E=}M9dxNT{`+(r(EzMntDApeN9NVBmUasYu!Y>B?~jAWJaWmlf&|v zH@yDazs~(Xg*uk@W`$l^`^F7EUGr*31&N27zl)!u`J+1`pF^f^%`xV2(3--7W9}57 zOeYV%FkN5zP=j1*x{j5>!P`IH==gJ-IUU*K`Mk?lbsb~n-FucUMT2%l(+lje z9}Hi<7iAxWY5;R)Nn^-Y>_M?jZ3^aF+25OVx?iRQ;`~y=$2vK&D`XmqcQt#~#6;>V zkC-}riZ$0vZWe5=sK6;SzE&<=v4fu7Y~)avq#dy>aq*BuW4gF)eP5M~R@k{kq4ae6 zG+Uheo}9)Ft-sa@tS# zy;V2~Q`%jn78Cuv{;KMy)tV<>%>*gr1a4IKcQpT^c6QC7djDEMwc9)3duA@|m_Poe zQuA#6&us(gKb$dmHQV=G@4m&_;4h^O=2 z5zu#QT{R?e^>CA%P(B?2tgEO3ySx-HDp-_%D??u zTZ;0dwWBExHD@QFu560N|7_f=ca}OCb9$E`D?+(`I9hT%V3GIvD^85M{PkuHq zE#tpvvM)FP#@jT%0e2h=IgiU@~eA4?B$Ls&uzn3UQT;)8rD4y@n zOd~Wul7Sw|(^EN?KX~F(Cut(!CDTh}4$S0AtDg&WTnyb_mu* zbE0h5nnq=#DP@tk7&BLeA9adw`%nc`d<~?FX{8*bL$BfMaB1TQ{auJ&H)=xb-$eFH zWGUvahFF!_=_=gmBGauOm{-0*lV352*9mwKN6@vr>s!hzL^*~|dWWP#Uo`Ox!{PfL zjvO{~s4}IkB}vyHcZcZJ7yvtQs?HaE6E7#!h=-C7WlD5fW3{zJ1&e+m4CB9ZaBt)6 zz67;CZHlaFEhB&eWtT|axTx(5%@>^WpIkPYSGa5@TJ^ouYcTzjI*DTrr;++D+Pf6v z24Z7JH(BX@H${uyll3V>1)v!~K%#N4cB+~Se3_D`0eD2T|5y)f>>CI<{@$?TU9G`# zx`Hc~37{-7nCGxDoIQ!`PqN2qf+9*}jFN9FzN#t~PdZl+oX|cZ8gNes0MPEXXscvy zIN%^VHPF)emDy=CrVQN&hGhi;4oI|BgD}AFD`m+$Z@cbg7wh|uT<|ie|NNm}52Hf- zN9&LD>ITgF45XW<u{F_pNcM-lV6C-)NBx!M*^q>IA{8 zymAcy%4*TsI6`!0swU_;PpNFpB%{@b^O_=`*~zBdYyKqMeGAGL3TLFImAdL@&BeD* zsHpz%@OD1E>I?h=pCdtF(VIyO7$XOuB!jqak5PNp8hyFHuQnJ1l1iS5+wf>mdRyeH zhS22z@Yy~u{j}LPpZ~r`v?2J{MiH;r88fmNc2Q1xn5Zi)0egCF;3G)~4x_^WNRa4F zSuN_TfdH;g;4QpJg!i*U11*FihZD3$F{S`}LE)SC$v&7Xyk=ECPC@+D3UtC3XH7fF z25U-qGMla+3Ar)-M znV-&~Q1&5L!r$DCI5&&>5Xrcyg^@I@`>X}B8>tlLeXiGqGqu$fz_k18@3_Q@Id_2$ zq>Mc9v9o^sU{ssD#`0xQEYOnm2+z%FzNxAUNR_d<8;?}3D1{+LC7Y-!C#gZc%c{2v zmmJLno^n0YzYsx3%8op(v*D4^d~^w?pu1=R)O@Ap`!)>lwuIIGhVt}^j3-;JW;d?u zJbr54FN%d*mGQ$dBqT!tmE7nc+HVigVbq7S8}%VXAANyIbq^|tc%PH3G?2|3tN;Wd zChw=!=+%Z~TQnn-S}8uQhTFJ`_A4s91-l#LNv+)~M;yVw-mVLpnd6b~%RWcW^*F-W z(myK&KrvL=0ha9@jZTSCZ=lBt0<$Mpgb2NJ*#ICyT^*u3v-!UEB6mXyW4&ze3_L*x zlxP7?&z!ZN90aEGonu$0fe8@LM3x;woC)c21@iYj=I_q>{8xhE_B?!rf68o2QpQ0) z>y9L=-PXF}_384Nv?qsNsC9NLa~^=*uvtj~_qP_c?WD&x3~a1?iC%3fDJjVT=q=+> zdAkY_AtZDY^VJ=28g*-m`WjvB!-GoHgRJ^2NRYrBoBagx~+lN@N0q{ zt)FAGQcQjBy2flxLG(nq0!@I zabpq0|AWIYgtWn&K2F;VdjMD)n_HQ06nViJ0H`qB2jC`o#fmk+^zTVKxc|f}b#bCg z?J+Vv}Yd>2x5w3WV7i-x(K0j{=P=9KuALiep$IiuT z92@^jzQ0{qD?YXZ>Qqvp6!M?UdesohpU4w{xU~AwalQ!8#l=fn>!d5KRrm3kss_rRyW894hY-N+Gpe$4qJ$KdoZJtTI#=Bx3@CYWfY!pA3l~!Z9v9F3gF3ri*qCt61P+DP6FNfH&2PYp0yDCK zffDu(J*R*f3EeY%o*!L87oIGzymoj6jF{Bl8pB5O-Z&q?1Rlb47C#>q*VM!(b;~@< z;EVhs$-%7ta$ldene|_cr!h>=w6-}mQ}siUurj}_ z&*kRl&QA0O5ZU)`&N(_!bb)yVgHGWGZdZMP{5(|8$4?w!UwA*- z)n5@}eH#Oynanx(#f|9&#p zXm7qeT^S*IW$XDV>>Ra0gzoS5rBR)djDA5dRaB3E1Xjo z8gDW|-)6{;>A%1D@pe7pZY78Tq)#Pj#^tYH<}}GZU30U>IY`GDtN&*J8P0|A);0$) zdX_y@psOfmuN`Q7_#YfbG6pvox)vEvNds#vGqCH<%$Gmv-Fh3u&F?_hOa1p8hvoL4VR=7A@W8HW%i*MGkdqwTJaLGW~}`_zwAj6=oE4cqVP=y7S~&2VhfI^PffDDd)d z?kk`>`1h{(KSqgufyt$^ zuo)7vgs%-SyfaMax7U${2uUsJPdn)cr{Z}%jeCgZa|+B>jsg0J)2asV$+X*BTU&qb z?>kdv+}mKO6w@J{?8-K9^|lg!7!#B(aqUp(ef070Ehy&=mK5|=eKr(Xmu8YG4J(QM zFS+!u*C3P<>)xUCMig2D+mWUH^D(SGZp4A~ilHgoTvKj{BsKee5$#SGMWVFanyV695*i;%sl8H9w0DmBB9*%`dzvtGmp&h%y`N z%4XX|U(4PAwsR5Sc(k(`gcQD4f(^F9P_JtR|GjhhegILxVAaWE5y4c#g8=YY4f)-g z6T#J>FCQ`)5z74BdpdVoDROA`DRu5vWjLC*vTME#drriO>aTzHjbwm1fs8VDSMlF( zVxa|ms(|8JL!9e$`Kdzls;?{v+SZgeSU`zZufay64XA#>jH`zt^~Bi_Z9JT6bBbMO zA3S#687jK1e~rvWDw@15D(Olx3KUTKEtR`%q4bs?q!4P- z>gl+{Kg`a#z}}i7$5sP#-y#4a9UQB5(OdH5%^Oys8XWUfo=YJ7lIiSS4ewr;*EYiE z2Br}I_dKj8@DVcSs{o#`brRQy&)%{?jcn#4;7=QXn*{DHRB@`tls*Uf4luJ*0pY9{ z*mz#3g69q}+|D*9&q_T;au0vNo!!r#&V1^KV1=pARB44Z3*fdto0wkX+e$(C6LV_8sL@Vau;h-@!&pfZWy;j=O7<^R zaMpL(ZvT6CXQ$$wA=<8v`nvvm&0mEJGO91^zi8aBf1Uh)Pg++88X-jcPqL^9X!2>r zd`5D_4%dgXMOKA&rZ8djMKT@0EAIg<0m^=fW&8@s~PR})RTkc*qF0f(Z#C~ILRpW+I5x@{UgFw3a*7&%=m~q@!yrW z))qsV~$q?35fn;#cB&CB4fYdgh`46A+BB<86~HldX-^wHT^XCO^=l9 zoWRs-8(hJ)3Lm&aGNDAIF9uRLII7MS^?dB4XL-a^spG#7{QTa!>QDNmAX^2z100N~ zaFJLcu(~#XZ4#^Vb6wq35ySsly0le%TS3>i2|4Bjy)neLgCHcfcE(5FvnG>zmyHJ7 zZ-h~KU*xj*1-y1=G^z|5ABxLEQY9DXsy%vw6I>^b?SNSDkgg(NMLCD!OntX(6gUG> z+R|k+b;g|x#?&IeErN}5c-m&8_|$r6v7*T3$(?^bPdM|3ul=<6hs_#CH2P|k(AU>j zMi+vcW_`2nzawZRNUestF|saS_fVdg`jA#G1Dta;6oA&F1BkNrPF5c?Jp^}vWuJ&u zp!+q2A^xbWL=${qMN)awVl_l9+AqU^edRl!)vZiQa1XThER1s*|tSf>i zQHoS_FqTq&CVlb$>|(%kMG&np84TYVjCv`E{3rxHZF<}{W=rz>dE#`P;j)UmHCHz~ zZfnY&3W`W1u=fc30p@TljR25rU`Pf~v2Au)+F%z^D5}TWDB7dP9%l{cBSq90Ha{ZO zb;$MHo6}ALwV6Iv+7N>wLMwF~TFiQ)pw5`DwyEo-Cs>6wsPCRsWrhFARQgy!`v6XIq!He(!GEYh|F%peo>IhfRCwZUgYGaZsoBW037IO|6dmxEr7KJQ>^YmWXustHi0ept;RB>pU;kzkd7&N~&{b-}=~nO{Vv7yu;STBUwWAIbyz^ zS_PHdQ^1}37Z5rrZ>MnR_Washs(OG_TnvCbH2^{~?}j24+}j)5B!JQ`=QQb21Uy`_CE*9Kv`{cY5QmsElHnnd zo7;d4rjRQ0vQCH_j?^safSXtu&cQmrFxVrKpAV$%WT>9Hc=UVtr#s777DbD%qTH6fubPSsL+NeG?Gh!y)k<0!|C}N1zC% zs2YRDa*rRUvLg9-#ce$7H6FJ`oiLQ@ePK@yT7Yf_^G4Y3<2u`b{e$Njsp-=WAkL&& zbE5rKE*UR1$b~#2eY(Fgda*yQw+a5APlA*(S=)>l4;;Nu0_8$vPT2Ifh=;A=jtu=; z)?94wVF5_{HF13hw_%{~OvGnE9Et-VU*FsCP{PKJ@uIll3f1ZZ7OL*P_0AJfqhqRO zNl7Y2lU>uoUFGj3W&&?Gs=r(5ZQe=efk7XNy>3>{#TjwEFuF3+R=GBh(l zn?1#05=tecqCH~`ZrNpxuk*qE%sHu^9GJmCmTD&23z~yWdB{AXuPvX_{k6?b=XcDc zq>y0#b{4*jKMZjXrzT3??h%B_jZN!p6W`az$AC_&O|94FAH%9DZqX{82MJDkuSpD@ zouAMATBe-Oo;p^4NIjI$Pri*OwYU&DO1yHhK9nibn^~EN6QxVB)nE zDpyo_V9}2)#bCWLV*_l)S9f47z&(;HP7=~WssYXJWh@*05iT;Mkf;#HrrN83y~Xyw z3s2*&*uW6>Dufj#h*$suK979v=7)EsVXSpd#z1J)D%acNbeHCk-+rn)z#!BONHSIh zy=tih?pK?K*yut;ds?_loC`ugw#I(P9cF!E^_2l3y2uK| z%M_16qHnU zcsIVyObHlw5YC^K5|*NKj}rao%YW@B_>j=R5RH6G=AD$f^8#IJK8Z!8T-l!nz&E#2 zfcuY-%7d!3Zyp)XMX@*^_o20>m@$uL5H(^9_}DfhQqH8nfbv5~inZdzEiYIWVV!R@ zuM238F$K@k4E^A17J>ZteBF0LF>#d|)AN=*l_iI54%M9x8@Ou`0>M(8!{_NLLtQ{e zfY0e+G53We1g3cb5E6h4E;>op{t*TR>&QpTBYF1=a{7eTAa#A$MkRpc7=(T!Tb6LA-&iz1V=;L z!x+6NVDf`l%{4zsC7qX+~PLg0H+q%ub@)hzEmrElJdW(Gkcd)GH)PhK7rei3Qx1ftv3mPiOaW<}_58c(5Rb3wR>#!t7E za+Xf*S5h7%f0H2ovlRH)8LrTHrZ>uuxyAbH>HJ6cus5fG>*-(Q`L{rE=}T8Ohi*0g zcN&5=l0KQwEEz_R&Uqik$JP~?yln1T;(gkiloc*=q`2b0tCRfVrZdSX5&8T$I{LpJ z>8~ddMz;0U8jbnd43B5i4DOF+PgXS|ymY`R?w z`UVwHsH0nv760?UoFH&V+20401{e`W^HnAveG=qi)#Jn$HK~*9U97j1hacwCLOFtl z)1Q=pGmPeCmK9Cx$A&qR>&Iz-Q5a z<(!^&)CODv4j{LrWgu%d0sj&HUFo|hXWYM_NyZ&hLqb~>7Ld(m~uYr@P$4vfL zvc*V=i%ldx2Ap7~{KOr;lMrks^B8sc(V63~g^>Qr9QVT#<4Jex6+!EV81oYpN zQGchIKMZI}T~o4cel9@M(+`rs2KU#mh_eL)1Z+=^TCOOuniNU$YpdchgL@YL{zwUL ztg>;NNkba&1d=HAYn)m#0Qi63yiSEqRhJO6WoH|ULK}4+5aIz5N+L2ptDaG$o(vEM zQpYI;u9=3;vvtgX0dJO;9t;-|fsEpL{$1Mp^Asu)Oyhex95O;FVLv%b$d2s*^@6E6 zOmIIAt|<_5C_B_(clGT6sh#W>tejdtK0e#PW&BHkesB$tJKvpaj4ly;*EMh1+;wWC z;9KzT&ysI*@5>>i_ai1~T&}q%DXXL8S^9IeY+-(JruV)8So+Wh5HLGz1LEM(Vw^ib zmh)4RC;?_{FAr8E(qJ|zmO?Q{cK^dW!$^!eHz1=1l1&uQ;m~$NOA-4YyAHfY3}%%F z&iI@9^#ubm&6THwtZP1*#jF=s7f0WKEbZ#;t~sg(5(szQdwsIS+Ux*HUTkx7&lUb0 zF42+OE9w5e8jvuo>NabZ))Hpi({EAZ73O{<$Bq49>0fu?EhuG=eL-(@2NS9lXV~ek zJrkl(<@SY|vVk<`mLibe0+9Kd>=|vC5~$bj45$t~B6_(+*LLFC!mHF(gf6Zl33JovOIw-(J(6%VgLK$Xd|L2 zRugw*CRzqK#bF6RXmD5%i5?$_9IEp^T=5>p0rB_)Ntv?QrY_I}HP>Vx(t59bpaY@p zc|i+Yj?)YB@8ZMDL>Wck7isI2Yk`oUwaP?&D5BRI3aAnI0zdHen^7MBQrsvn;^@@Y3qV{7|8jf4a zCcp(A&$qyhOi|V4{C0@UXSU9gBzrhO^o;NSX!`DWs@wnn=8ap06orgqRaQkdCo(fm zQZ^?EN%kJciO7-cz0Qe@BxGcdviHe3_RKtvbvQVO!#U^o?)$la|M=H=oO6xW^_&dX ztEFph|E)if1K|8{M`o!d4a71~lfb(I{3(e_?f#{c(>fjef9qt=_BVLvzLk$(LD`AU zY)m@hi`lW*RQB7agW!?*lcehH4Tz6h4r=3RXLF)S|NoY-f{_{zy1^6NvaFFL=&+<+ z_Gj~(=J?w1R0#0Sx*sST2IMX;>JGK#d=ETW*AO}a&_c61uLoD%*gJwA!FSz4_Oh%4 zn}K8NnxbGlz|iq`xCZy0=ate(WFNW|MBa8OK8$C#x^E)(e;^CrWwyIEk@7YMn}(zL z+aoHuO7z8RtbO;#T@{A2U#&5`(Hzin?sW+ zDbwRU9TZ@w897;W+cZ%OEAU@RSJw!z1}=}F6D;<`=m+ZKj#oGlxZomM_5A$&{x&c* z)YfvI-A4$a)sH@P=<<)>{`^76O|s|V72wccHNzmnt&;+uj8}UUJHLj|5{J|SOz(fc zt&4d*2h1I!yeYJc$Dg*pbj`xd@1oTM=&^GnwR;b~vh^oL{5SF$%6zw2s0UtIJ)T|V zz*ytG#>Kqj*^2SC2e$s-;VocYd1ti72f}VMM>!{)!?1uG9C`+UK$?L=w+xUPbi@Kf zS^Vgu&h^~vY=##Nm%l7kymGYtzh;YcyVGX^INI5|8yyV*8j1X1+5h#-GYU7OfJt#+tqdS;bb>G3fNBk50fD$M;?W7F2=OSkRMJ|5; zDp~A-g&`!;H3NKqvClZm|1Kl1Z$E7cUY{c>u(!VuZb`OmtGg?DeQ*=~lbZ01ab;Gz zi)A9Q6(#;(%ZS%vuBH-0b2pfAd1XG&Lsb{(z~Ze=0>C*zsX5;2HAf)Q?HS*G*_ION#{EbY)=1grVwSdsICgQktxkmy{Q!$8(KCtpdlpX z;|vfs0KyAIqxb<+BB+CSA`bS{%pJG>R}6;|k2iHmSbld?#rmxdfY35J10+Y^D`KkS z$F09)N4lK`&5*z~pVHu9s583kaTa`CJU+`fq2=VbMvT6;68izKVhdibdH#R5(Lhw_ zIZvnl%l$x3!I>lei(D`+1W217KweEvO=tZRi-h%3JGim|S5b_MaI9S1i10!dOXUOJ zk|3Z(=I&fS!gh-|F!2Wx%vtSJU*isbyX8M{6vNI?=1|YKbMyi zJ~0sF*p2XqLPe$Y2Vaj#J|X*LVY>k)^;z)|1$(RY%#WRqX0OF_dwodM-2EE#Gw_SE zC;EYxd(!_@i>u-99=IX9;hTqR;Cl6QKYl@hM%QBfy*T-BRII{)}-`8SwNm5lnV`royqMVGjjJ1dA#M z0~LVcl05ep2sDY2H@nL0*9CkN36JM~S{JnlDx5>5H>Eo~01@0x+jq_m&d? zSnl?n3E*5<^{@K$MNSOf^{es$tK1i$+v^ANFPjO}A#o+}RJ%KVUNm*##42jtTRq1R zdcpQ1MG_Jc?(wSD*j6cAMz!eInV9q_VTU@_A)h8Yz0|H_QlTO$h#1C=-7lfpW%|$E zC=R0FZ)LuLJ6EyxZp=~fKf5@wq-XT|TfwyHVmKW+azEq=<&HqPy`x~oWj3!bx(vjO zUZ!6)msOO0)hNoc)Tm6YJe_m-w6WlEGPR!_{CF%`#ZN7P5WM^3pOp?1^SD>jGn!8< z#9&|SyN@ba&0=t6agx)I(m%IHT??Ppe={sg*%RCin)>x0?a4z3w~>H)A|sh)2Q7Ri zN`C?%KzmYCXwTl*T3r-qe4Y&%3&^~}zeYYb6jF%x%PV9L`733)BMbfh{B%??KzACm z8Q4hv$ z@Q8|s$yc(Ao)WYPe6Tg0(-7DY2{sr_`dYT_EyEAFufS%ST<$2ZmUvPCf1>JqY?vcP zFt%AoPiUC8F*a1`18au;rnWb`6h7w zx2YMeUJV_;ko}FA&d$Yj3Nf8dXIG28snISj*e0Bx7N)k8GV(#IkPT}i|Gqbq{B{@F|=5oXhT zw`NjE(nj&eRZ&SIl~ajqYd-KDF)&D- z`4z-9+H~+wi@3RYxoMeIZ(h!Qh6Mmf0QSyj0Jd0Nf}J}ZevSz*K4)$feqk)>t|+MB z!O%IjMcK1HB8V@0O_G9%N=F~{(_8ge)!s0(VlPK#FO{CyNrdEQG}FsvYzNN*=S{45 zBCf{zqT$}#w1AvTn^q{2i$zYLU&{^V@U9x|Q%u6>i$mYBi9+f0KOkW+(GD_48yZ z4G);i4N;VlqQ(11^yMUome8G*FE`E19e$%dW}M6FMM#b{Y`=_Gyks;y|8@om#^8`a zu80c69^;X_8C&vdJP?pdq-{4649B5x*an9L(ZoV*cwdn#>1DapxELRrb$V{6o@Ytx*~K=pUG&kED`mTJ3xQnd%Q(P^oX z8}r_WP*`eN^K!Qu4Ei|O+ZlTH*sg6^P|hYzvXMfpcHOeX7k8udx#vRU`i^x-(d+DJ zW!yLK*+IE{Cuzt0BHkd73VEc8sW?c!MErla$bhbg1J=Uc!CB7RkNxmX+Y$+bX2a}T&`!YQ$*&Lgw|nEw+4u=OhuW`bO= zhlf@{ZA+S*F9xEty!_4`s5<0-nU|4AYB%^pZ+)s_Nm#i&z_$3RsR`2fSt4k`e%$(*=(y+0|tJOB_8Dw+>Mwo&kK?QyYiW+;xGqnT&CUI zrolR;(`?zgcO9~iw(S-p9Jil-3a%@8ThrE|TXjn34TR5QCV7^Pad~@V5N1SE@fd6K z!fZjC#3Xvq)cpYKvl6_yO<(Jc9iV}3?LkhXecjl0Fhw`eZg8WxyreQdAhE)2o+e|9 z>F^$m881Y`4odK3$cbz-X;BQn^<%@++^@pjK=Db3pWe1JT7F}|yE_e>HIxkoyG%FT zS2;c|`uw`l^?wRZufE^JOKG$JmILrefp&KnpM1dxFnO0P>l!}bc7FAG!BDw3N<>>| zJOGeHv{@f-4S%@8Y5w+WX2j-;g&`4dmE=IuvoLS>@COmU0NG|XLPuG%$3gwel^$F1 z7d4@TSWgm*>=#`hXM9{3Yh`lJ=htV%#Vpjf!~dF76fdxg8d-Z($;BIr} zKwJF8t8k`KE*fbCv_!W6d&WKr^Q+h+8oqPJVcU*}I9@G^NFUCR;Dg zcd9n&aKwpi_~9s-7|SH#sW4MNO->;kn~}GYy9J%JEs~Q6w??m;6KDM9VYWGWUhd>y zfAi1Nbtb>tK4ir8NmC*g`Y!ba-{=ji;uGWI%kZf^xPS+1nm`T94>W9IfnbJtQkStp6< zH*({%v#Fv~WeVuxqYXrvfzzLNB$ydoflvBPU9`6btthnGM-!TT3079ff}q6HQR(nK z143qo2n3bcU}yb-L^!o-d(3bk_HYG$d{oUcCi%wjMwYA!LWTKBf(q#c?|n__U<}!N6{(2Z)ow_1AY|@q?a&#DEW zyAIlupO`ZR$5|1?9_Cq0$B*554zLTPgrnS^yG%D>M6s6SLM~Z_nX~R%;Ka!*!93;I z7Qm@rdGYw`jPTPva&BIOu!-?1*3;6Inm3by$ek4{*A;C$6f1ASe1%wzyKg zn5^>VggNtC)q~Ya{Od81UyVEvry86GKYh?cR6m=QgLlOT{{6vAWcIYuW2uRMj?+|( z#%9mX;mjItmVXUZviQPsOxkpr%{U!&s6^MkQoU}n!E;Pv*=MrSBmY!n=Mi0pg$!N{ z&SsXEbmEs7e($$JQf(21ws)v8pCR=7)C{r5Es1=LR$k^oY*~?894?0M9yNTPpJ)~> z3$1?Pcsa?x8Wm-bQ_9d6n)v(2?%W~M5(8&d2v>&tb*s+InuIt_SX9d`6dXE~hGg2WJ-g-L2X#ds2lc=HPjIfFIeZPZdv57G zFejNZcO6*Px|X`%0ASIFtWe-xx!JV=1aWK01b9ra_60q9=lODPr9aIyJloM;S*y zgI~^P?ZNM4HkznkD1v|RmNSYEFR&w~`LWwD2WL z9lY_as_k8--6e!2189+F7|B2_=y2|f-}7>YV2vVsKJ4@b?Uyr}n)xRi&2D`$ba(ys z;*02JB>jGvME6;dvog1S`h36Wlji)#V#0>~{D|BT`fdQrMusUAc0X@Zf#K-$q!|%` z-koxQY7Re2$Ek?F_I4G4z1>ofsu7xjb&xE~o(257DH_e)7uob2%AqF=2~Zhn5pTLw zHBDlTxgYTE=c6pGY*k#?9tQ82bNyKGv0LxK;cOGS%Wqx}78Ga)9(}0cKzV;2my@)L zs^TBalvYxmZn3uvl#3>Dk2SM1e~fiLNA@@h&iC2AUQ~3^tu<+^6ec~G8DwGMJ=y8x zJLfmO>VhBTgc=f>sjll=8wORLM-okIC*G73X%OZpCp%US8lHcma7n2rP0oFf&R8G`ON-ICZle4~(X>M}Bkt zXl0Zpwb`~2!-PTja30n3L-(BaR<)`rvg`gs?H3hVNf?f3-H5rf%^r@wz6Go7dA1BG zc>TP$v6aoTVFMS0+}Vsq9FClP;4zaN`yb!ob$fW?b5~3OBk6*t! zmwQGnzEW3{-lMMI>|V($>UJ?au88G~ebCFtk_)FNuI(0I1Zc}Vt7pAo47uVvrdArbY}PLZZk=P&U_!rvluPynQKo{T61PKs&70G z_SDchXAv%-*^0RfaxjiXWX!w}B)sZ)c9fWAgk#+L;GQr2461|c4~ylaA9g=9EBtl^ zXw*$e6-D}kRA`4w0vN4xZ?@~$o&n*A=q^>psS(leKR{xZSPqaAB*0q*W{=HzqbO8a zNq1D;%g{LslEDS-{9>^5Id7YFUfVo4Y@?9&1ggNNuCRFWIZl;}B(T{o%ST<)ehB0x z(|}$jUYzYOOdM>}PNF=R;vvF`3|udsKq(u|3c0-T16r86D(ZNNnnq0{_QbHpB!EDe z*Pw}&UkwNW3SG6h>IC2RGHWg-X$>6%J$IVO%cByZ&|}Zi&FJ5yOcJq>AmJSG-xQ#* zMm6@V{qc%fG7FxzUj$a+C$4OyT)Z46Yu~M!zYqyNvCV#0xvW^s37B~494}5DgX%d* zEAshC!FkBk1XBJM1Wqv)KhyWJ%?Fu+tb*S^MC=$AByz1BzHCJuc1RSLa2YF9xiV= zZ#(c4gVw8y@r97t%f~r>y?HWGKiG-z=~`D^wCYiJH8C_2Bi#yDjGj9dR1AQ#m%>I5 z#`Be0f*}@7%Y9$c_MwgB?MtC#+~4=WVnR0tY2COKDB`e7j+L5Aait8n;7!TkY@1+8 z{r>Q@D-eXjl!Q%n$-)YfRnuF@^R%G#vOtU}jfBQZkb`dzX5W?GLgTfwX`^Sskk%%` zuj+*yzbbw!d$p77{eo6XV8frvXw2DpN=Tqo?Qml&snh#NyX?_^%*I%A#x}i{pl!kN z^4GC5eJ93y%Rp`wfa|sJbZl zryC!UEr(c|7<|mv8$|rGqyQai-EXskG6a+O=$(T2gk#68Xc!*j@Ede*P7#I;FeJjDxh=)sPi zRzQX9wxjZ0ENirq15^?%k^ke1?~2|qaL*Pualc(GgVbVAB(d_zhOA6P$^~la{dl2o z_*~A}?Oqu}_R|js3QNtl+KZdn3_BGC%DvOQVQu`yZ`8fS?k$HVoq+>10Q>tLfj&@* zhE|6j*YYrHY5yy$$)`J3k{1F8ijOOQH_Q3NWhINCy!0|XCs75v%&w0{JBs-jm)p1b z4C{)W7Y9b-aqjWVW`atZZ}U+}@S*s+FE$~DIwcgY*P%5dLSXG@vtV-&4irQr zrM-#Nf&E3EYMSD7%I^K9HHlVSIi1W1?J(`YqhZ@#_(VO)D4;@UR`=n^?xZ8?Mpg1i z4)kP^Q$V@DCY-Fm$2O0s)W{Rm>TlB=Zr}0+J($wL0+2+4>TVPyybj%QY8dx7Y&Jhs z21eUM0lf=SXFDW`ZdAsLjNCF_Rpoj4*K`M=Vy6&y4Ki3!#$(A8JF|3n*L;0OPBinQqW~NdNfBp z>;}i`_!)F~4kj~<59FUc`uXw{ZY5h$%6=~ibs{8k^`_iP3DF`|Jo9nR(f3e~EalvEv5tc}a*o648&h@oW*Krd zb}#&AqRd{oqjvHEy2TGZm9cShDE_)?Vma?9EkBFUmjto&&9C2sp!4>-ShJ*Bo_kCk zbXqIicxF+3xR|{f+Bw_^058(3Ll8L|xZd(0$z>W#2swH+QSuz?`D|``gHCQWu}_;brk7mB@QWzj#D%CfTLo6Qir^W z#pRP_Tha=bPn!Q^YvQxdGq*#7$z^Z@XCz#qFK&&>?MC;vpzyUONCKx+br8L*+G*+i z$bCs1;h^5FmmQ%|!Pi4~T3DUyYOo(J;4CPo%|i31w<-EVhLDWjYJGy#X-iA4j|%dd!ONhIGOOU{7}_AnE<_h-8tF0d_=u59nA z7%ytN5z_sir`hXYOhfKd-|z9=JI{`B0pgjF*Ppf0wB*#7hYxcil+2ftbUbSW)(opb zz07>O@1BH3JmQdfeZ}vx`aYXcs`)1WiLwS$2b1Pp6yi2l-P;D8@7nUKb=nut4(l5g zUml9}zEb~qq|)_zg1ldK_|ZZyk<(_#{CB4Ba2j?$4tT`UX2gug!;`hR2gV`p0+J#D zw_O*7{qoJ(s}4^#=zdc_R=bPK0vRM&6)mnreKae%X&Zj;oKd)W)G}jSlQ!*kqE(&( z8=5GrZ6!=N;xEw_{G5H$Z3>PNycQ-eBL& z8J`H_M<%Hb!ME#iU5*-e_xL@LJ+D*+bMiwS4S3yW|5n*OToa+!IS|i>rZIYnhyeL& zs7~4ko7VNOC5!Uk?0(fx{-bt8?3apbdiY%QqQ)by((JgN-JtDyQ4QB2y@n`X(HGgy zIFp+s$N4O`7KIM`mgOEVRl&S0*eyrUC3v{Y>+owaXh7nvb?O@ZIw?+rahR`N+3Hy`!efGQkQGEjwR=sj@TQibB$ts_rV>jc)<#ncAErRBt(FVY$o8fa4-aAg{c3HN<{spx()r zgKkQtMD4c*A8Xs*s(QW<=8c91-=8H`Ok*$BhJaM|I==1`QAjj<+AJPZA_+kWmrK^& zE;T2=py2(s^&oT-Ibf)&Po4S$UlX*ggM)f|X!v%YDG=AlDi4^eXI3B4;-7D_w`-tV z&g((Sf|W&Bhnm;I_9N&ECHO*aH$6YI!tN0IN$bk*uPB!h&9H|b zUu~K^@|jOx;*DBHJZ0d5%zlRh=nS&^^$n=`B@c~3RRAtczG%Wu1Fq!9+4 zqPBf5b~~ngGsLRJ+>zpvn)E2LP8Z4ca>;bx*2auz`V~<(Q2NK@Hv<7v(yPxVghT|D z1=m4l7&Xv(UmDNu(}aa(u{OaoVj!Wsx*Bbs6*p4S)qhO*7#k3u5!Hz7G;3dT9?sCD zFAg2gYCeBMAha~JcQn$Qsif(LAGKblOc-QlJQW%QLN&|V8p8KpU;ZcI>2SM6aAGT4 z0GmZUNBG9yPD_C&dOuCCD7{d8p{y>=HVxEkMZX?9`JLtnT?Gx=_M?T`m zHP`T62yETh;}UUQknczbv5A=aStVjrFRAf-mg(27Jm;e7chd<8dk0RtN2Z12^DyCAa_LoD$4W9pMWvCw?)HAr5IM*wGJS#CX(u758g-U zAyYpR+bAo)u96GSTKW~#j(xj`az8g$1p$Lqh$rvxG;zb$!$|Oo-I&wHv1QvotKZPs zIYaV}A&5EWO0&55>6HGD^qqU~Wm+V5>8Os{NS0e9m^&G9&~a$X_`feL3=2JukT2oZ zob*+B;qfRH-=rt?$ke(;D5w6_F$PA*eaJ==u)6tjUdWH!<2i<4 z=a5VVA78V{n;&>u_cq*`6*vw~Xh0gZ?QW%Cdri6-Y%5MGzcfp>$f?R9zhAQc9*9Oi zoCfDhucFvMhx@zI*m5|z9~d-`e391)+=QbBA05Z&l0S7o`e3>e=!0!&$y6M-ZoJW4iPDY@_{85&L|6@p0RDaGMinQARDD zj!BMo(_eD5P7f^1Ndd4l37_uED5q~vV>rw5df7bKjD!>WcR99pKzAchcC zoBcr}P9471V%-aBNOx<0|9*>T=i`HN_>D7_99_#(tl!wWE5K@h3Wq8s%KMp~_Cg2%sKzMu4Owk2(a2Zad6nAI4c^Ki{oPZmwg zfz@^Xt7h#Nv8FsC(3j=ivyshkMrFmitm}%IvZ}2!%KOhG^?3e7c5A*7%)I3md9|-i+yQa}tU_IgYc+tV&|Zq`D+rIX z5aS}$P-aJF4YZmp%nekNQg{eQ%}iy0-r;95jy`A8DA`QHl$b{nAWOJ`o~b~1;NIxC z7e%&k61$o&XA>e3JwqsDk}lM&K3o;2wA8O2R2RAVS<&;0lNjC<66yN9`zv%CxRb#f z>|l>KRF5BrAK z!S_|@LsdqixNH?Lbb91F-(+JDWU3*+IS@>C@{-CGN zVRwsctulMse%HsgHc=w>S2vuoPJ9_@=l9H=UaRu4vpHtNOfo0#-=g9>i#%y`0hivS z)5Z>${C~UkA00G8l{3o*@~9WbXR`shPTs8d%7%(qQG#y7pyeF;_yXhN9&uR>g8VsA z;i5g2(jR1ul*iW#LDF*q0yFys$1ie_XC`aKR8mAGkd_BU$_z@92E9&`DcG>bv~DY! zsWseXJu0g7KLx!)XFT$wF;n@9Hj?Y2h1z`65SKy0C*>Z>VO;HMBFl zcE#nxMCXzjLr`c zZbDrk*ZI>y6BxJvYgJ6_yF2>xqI$eF3%`zevS^9b9?GO7owDrhg_Adp1yZccRqh;$6?V50xvQR$YRL zu%4*3&5vb22}OeMbI~=nap>DE3-W_Wka;MP$Gq7Qgars z)$Zs0cXU$DK54@(zZY9nAYGNNAY#qtcv8QQHb>uhYlY-iIoV#|Xh22@wE+X=3#K-Kw+8dXqfIXt@ZuhwUAY#$ zy4u@vHrXHrq!{!Z^i}Le&!P&mzsZsEvtj7ckC>enNAx!9bWqpD3C#YoGht&ZE<(N} zs=jrLz#N^l{e^j70A?#EST|UmFC@M9(I(o>yoJ&WXCmK(_}Oiy6^*~R7rvB;TIz;e@Hp|Jx0_te_ ze$Z>ak{f-2L)X2q`x(-hnjA|^keeIA+W;whfBk&-M<1ce?=3zXt1>}j**&KJDPoE> zKZ}}TQq7MmhqLY6WA~WsURN`F^H=jo445Z&`<9+QKSctZTK#bw?>M%fKF#Slqad<+ z^_^Jw2S6n@u5j0Sum(tF>q=5qem-?B922K39F&;F&ZwF6s7~2rI9qW%a#0g$xmI_} z{CUeuCoVa-?>Y?bH+6tv!63;t`qoQTH!;#4Pqn`tleAw^I;6{VmmHDW&jY@a-5@>$ z-oc}Sb~RslvoEaYW3KfDG0rk3E3+C;{&PC@i~C~@1<NqSHt3oBP#9};6u;=K#hpYiFPps9 zsPJxJ9!ENNpLxH}zo~ah`?;t(aw+9VXHKA6aKQH}^PNZhtJipl0DA)Fd4y~3^l%TbowM&(POtk%8V^gq zarF?=5fioGn85e{pB{S>-yOx)p5Iz<{gdV$!xSk6uG2RYmE(@Zs{j2{wgot(M$hH5 zeMIQ}s~+HyX|m%Vr)>EFK|B?gdPB)KeD+_sBU`~W=;&1=n9InD3;xS7AQ`hh%DY4a zOc(yEX3WNy%~i$K^l=1UIbokTbd{@l+5~yM96Kjx`rdVTRE5&0hDhFAzn<2GHqY+L z@eOIJg->r3`*k;*R?AmxdKIv86~<Y?e#ogVFFTM8@2euE^u+-YQ@uNAJ8^P z|MfqKH$62xj~+V9VIhu`%w64NVh!hY-NxaY2vRwKR)X%yl68G`q z<(f(VsQZ<~o8~9Eq<@KL3~KuHYAp>Jpi1@bY_51K$_9jpH1iGj@2w&;%Iau>P6uDX z1>;bE;f}2v(|+NI*?BCAH)tnK;5+1F&oUBmO7!0VNQ;SvMIIvR4AdRKncX<5>PbvV zqJ4=U+R+cW0px+ zrFL3@=IE%}$Qbz9YdmhnF>clzWbj23WPUS zhvK2pB==Qyi_eIAB?DcvvZ3S_jOG0rs{9JVKy@?SZ#{cPB>^{q*&I^$B?o>TW}{CE?bd8TH_SgV{*4{^9e3?#c6HrgEeu20iU zrs`K|)$o4j1^cY8!ylTTh2I&QJ<2p}yyIhl$`1UCw9OwqoNC>p&a@xo3*L2m&-kwA zw$bs)9-2jsV>i`YT_RccsU-UH^DDPv5d&dS6VKODc@%A8Bi*A;i5*`$rw>Z9pUl1Y zBtYQbisqr*2(xLzg==NJDGB(>M+WQ+aEE3-dzTo=TAABQA^6wy;@`yFA?0R205kLUBpA;;;^^A{LS=IU*jR z9y%Sg1yuCgsu|6RVOiOR2M=~t4`KNQ>`ou(J}}3?ZsSvxeZ_6g)GO z5=fcYs?ATM?FR=QAJl>{C$m;?HjN@kCUUHq<`8(&4RnzuGMRtt`bQ}5=iUZWvK>wB z-A*i`MVOh8hJk^wcvra~3gGGKmZoN(hN`Up3Gj;nh399l&wsNS6=V}ds1g!G@V zPC}Gmu7@x?^6+TdaU_)i^Ra3bhaS4r80R-1BI?oyk^t>KcuL-7Fj^U5)^Ou!7W%iT zmhixWdJHa1Lj}slpj2IcDhk)WbDeSp1QG5{bDvxGzI+u3njlisgMU7{JzhVk03{-w zXl-5~zVVj**GT!TsHlHLM2F1W9S#S`k)A-;^ZvF{ylfdwxKSTDuh-LZ+zdgZG?Z$Lk)sVbsj?ebw!iMS}7)rFmGst#$=&u~Z z!9YpkM$efh&HrylGvEph{=K8cbw;szJ6XGkKauCg3qX1OWzJ`;LTo?ho3m-aG-ALU z2y&b#Werb1Pkh{Mc^amuHocf5aP!Yr*tK^$u|E?dQZm;%l;4&!$EA(+oELa|&fyu9 zZMoPibOhK3;cB!$mnULLi#fkPG&pyt$oqoKuYv;RX|qn<%<~3HP^#|we5)Xcv7~I* zPJZqIrQ1M3B_<1I^bI{(ySp;dc$5;_v%lV>(VH4}uT6Y-On*_}GP|ZEHybGP;p5jF zRmopske zFE``HsyU~K+T zXFpYf86fKX+x7}{G9GhS)haJcqRy-G!B2KmA&r=F=f@q&C_No;W|(C_%<^fc#hLu9 zs0$cL?izl{@-Eca7%-79Da=YFiHt8WtPk00`VF)3u4Z z#AZOl7^CwK%FCILQ}%Gs^wgPPm<2V`N{W7HHMK`eMO;AokWRd(ajH2(%{xs1o^4Xp z4X0&Q+s-x$xWhLokbHpcmbDJri&?nQV%J~u?Kim)i?)v6J|U{$|4IpJgH)({V488D zKy+8R3f*(6#!s)|5Yvx2sGUg%G^$#GQqc>DR%r+-$+cl`tol&Uw4j+9gt07dLCU&K zB$wNrux&)f${>11m_QxlEhiRJEpC<#%OAur_vL2e$67N)cZeEilHyg2MGN%6fd-T{ zQ84eyZWaUlI`jL>6)KBv-)={I8CuMB!!bo!H!EQ@>#d>i{%+!O7k9n>n6@f1LsUdH z1n1)Pm)e{2=I2nbw;h_BJ6q9nE9oju(k#7f(COvypr6+yd3z=WZtq*~Jqrk)nvtKplOa~(W9@yRjd|zTTVbY6c-`wQNps2S#%E`^ihHB z=H~}-DLOV5k6)c`&D|7@<&BwTri~o?6$YY7W>3qwjp%%m(Vg<1r{Qhm( z)-7h`CN}w%tAlTr{@O}j{1DfX=LE@wN!sh6fYUy2`;~}db-W1v=fmtC_t;L2m4k68 zA5L2aH}SU;E4WJ`>J#qzcRPVhOJ=Yqb${3j$VZ~yV3|_3 zw#l(AHA1hKvrqes(s<{&Gu>hmw({goPACuV=}RSOdd@e`SfO<(9l--2Vkbw4@1~&0 zok!01`oe-8F}WXqf=!tJVTlp{l&NHGF(&xTU*9gF!V$M!t;oFCN*}eu&$QO@Tl>uZ zz&Ai<(&u{Gwk^${!!~nM0KiyMM{d^BnXxu*hVE`bA8Rmsx|hja4nfg3$_o^#3?XEb zdBYN3*gcNd!GC2q1$^}&>wwAocrWJXMnp>JuZ;+kNBRs@*-;?&LU01l~zq6Q^js zWo2X<5zH0wE1e%Wc;g_sMINl}e_H6Nd|ip$NVah#zG2c<`f}LrwyFZ@L8tlJCC!s8ECPSzj7bRW}U_*#b?fta< zXYJ88Czu6pIe4kW<6wLh3e1CanBa>1T){*0>&hsqjh91FJ1({tX;F9EaIG)>Cs9q6 zD+ft@RE-ieE;gE@m6SODhKkQ!c_%?Oi2V2>>S5Mm?e7akD)>@oMdVVfIx%a#@`K-Df1!gCdd=6N-KLvFQ zAD`82QsKWYdBsqx`r(q+^&z#em@C(q!tHM437{|Q-O9Zg%eV-O<@#Q1fzZA(koCJ!@c1l_) zB{4dwoG57i_6a`Y*5im5Hh+3H|F-RJ1uZDWpGXBw_HzxtUUj$jr@@}v$CP7IG{&%7l@mu}N=DujSigA@bBDTZ80YkNO_pdsUAfu+`W zmnUC=5U}uET%3Pme!o!e2W5RJmycQKLTq3vgyFkINBn#mpdc_9>d99$jqD>QrZv6v z?~+Ci_tC~`TYf~u_vW?Py~kViW4lj~&O(5V^iTYfXL*|Ijr z`O%V2=0RlbIoYP8ayrhvU#|6j_Ca`6q+1BJa3KcnwdLNG4h}zopUl%dg#zf`ul{M< zlj0hZ-}<7vimuc?xI(bfLT3>^qCIeVdPA#WiHr|OX$bv6=B%;q>BA+10@ z*?Z3{EBPAzYF@JHOZ_X`7enBswuRs9MeTN4m!WG(qOoKvd1bsi;}vSiqvU|XEi}CC zeEW57$7)5hh&PJf=_9Jg*?hQe&^dDoB~5#h3bvVFr^!M0r`w^xXOn6dYlS;A>w3=V zJOJZoL@&;_?vWPk2($;5*{lxde}&A|<5)OfS5*yVZ&nwZTQ0d&?}!qkyur<#)cI>I z`vfQt4d2>od$JwX?u9xiX-84VTi--qTI{iWPC2RL2j%yds~ktf{N|urGv4@rY<+n+ zl5N6|s?Ib)6!RORU;#8wlzXSbTQrk{AWa6|5+}Z9bv{tTLj*{dJC7EU`PoLyd zrUbh-DyNl#$@ya-40(TvSq(nW^gu03g)6-+iSxhG{?dg4{>LBhf{XnGn^R%8(wd9J z$_)5$FLBJCyh2Me%|(XNownMoU^)5P$nK_{+dwjGvQr`76#C|eH z;J~B-y*`h1>O^KTs}sEhX~A~>6@&>i<8`>bon_;)<1F7dP7?TFAs}F^;jYUi*vVRf z_T-z{88&u`i?qCR6C&XcZiJ2rjiZJK7uw$3=j7&TT9dCqrb=mWA?LRO$7>x)K z#r{rbT__3NFXZ;hV3Ey^bMh~8obhbASTvnoQ2a4=ePLy@wj_+YR)k|%dByWi^_95A z2ev~?7zZ7PwG6^Ps{u##VU@ECz&QSaOkB%>-}$!<>FXY0nPC7^FnsFJaKqSvMD3j{&6Pd@Qs)S(?pvx+j*rBQBK_PMGeP!JCC?2n-dJ8I*^o7$3n$#+E zK2AMsi5n}!HhXwcGO*16Q2-9s5pv{ZAdQrS!jF17+^=9y5)PSd>US8$-ETiL@7Ya! zWB=_6!IUfRhf6v{{Le3?;YY>pf^oi)6B1HTTpFL%GJCPbJLYphbFwWboo{ z3#j9mza2gsMoZ4&wLUqat>F*0f&*ZqB`O}mUnF@t;!sr$OW1=cT`YWNd*?9h<0p?? z_3vB8iPXJU5UX3kTkp0ipdN+`FMjsulGrfRSEyy$BxeJZZ4(raQSyO#9@EI6knbZc zhc+QWQ)fN<^t^FFn3)z`93>nd;sGDqX2g&dI4q7ub55SQn8uyuf7q=?+s7r=;1lrj z#mBw)qylTDq{XEQPq7@*9t4rtxYg2vz)~e!0vh%=D%3rnx++^Qcu>J73h(x5_%Z9_ zS9pCjFXzI-lEnS(?6BY|&$UW-XTLxS1}8Ti*c~wG(cSjbV)o{LH+mpLg|D)zMma$LDT@10G;5?lP z>A8?3vSWSC6BlwQ-q9o05!>V4V~O4ox)Om5i)!wC*?OsOrAi4ETfry2^Ei`=ex=wRP8f5WTTL|n`L|vgbNGW6~-kO9OfdT7lc5}jw+<; z7IU@zr1lQA<17qw;_+;^_;A;=obD*CM~-?Ptr9X8lxuvKH7adpmrIOYH!KrBYW-UB zY-3r~Tc`y3QsI@Uw_=0ImiF|g1hYFx%GjL*orD^UhjomvmQ?x$(uAZSIjC%n5MFQ# z2s5K6Tn52jWSY4d6{yIs|H=e(5>eq?SYj`JtsVmh;;kke3W3>Mxalmvgi`+7@1 z5VQ1#ial(oM6G{dW?dwYD5~LR9&CZSZ3%*YK!Y&qyJnQ;w9|b zxO;LGc6;zRTY1Y}c+=91t%T6 zXyf68xT9(-T*r!M*xO1xb379;JyDlt1*)q69}h&dG=|O!39(W>;X94tL{BVf>COKB z_IAWh_EaOKUbI>uIyP`i?+d&Iz=W;AQW=B?^P{U46ZDA@O? z*->q5@J>>W?Bc-bjN*uiz`+h zFbl>9`JtP9Iq>#N&CC-tl5v=O)j|f2FZ=jc&J_tBN)m^9wDR&Lyu@dP<|ov!eIKA% zPo3JYv{_Tjn?z1Go|Zax5}Q()--XvX_SC^G4Pm<{J7m=Tu#@eNUS7+;uOF`B6Q-Ce z_d|rfhRwzgnbs7E*M-bawQ%ctTS1A8Iz*esBW!>>uRFLiWCbgSUE&HjnJsQX#Jt

    >0~|jxwDVEqCjH@e7_We?x=<<$N6ODh;IvpD+V+dZ3`oSasB7`?Y`WB z)7>q1F4yRH+$7xf1e_cMesflIB*s<_-9g=%*R(h-^!10d!#CbNMc!$6Bqb=Gj!e=^C5%@ib z74}&)h@1YZbGd~WNM%~@U`Uci!p9d-_qNe9po^ej>P(BK+R>cttKb~XBH?!57@dt2 zI&Rdj<^wwkhYR6>>Fpy%y;l zBTJ}W5?HSXAGA}wLf?06ii|BFbul3)6dZ1F!C^!BjM^)+hK*B6^ulFrJ&~pYCWAVA zQ%^2|c9FBzgT6ELYDY~v(T&U6k}p<7RFyz(4w|JFW(+%<)#r+aV{2}m`vS_n2MZJG z_=H)@(me|4wL9D{L(Onpe#_~#K6#_qY%PpKMogiV9g28bGv>h|fjrqnJo5GFCZM_a;7y--CLn>L~xR5eE!Xm%Bu*J}8-N-y+up z{d%($sLETm6txg?AACbzqlOo&FuG=L3@w1WmoUq7*>qL8@-5-O4YEle#UCFlI?6Z8 zF-q+%k2P$ka73St4O+(QGJ`+5NI!e~X-LF)70PLw@ZHZ-8026*g!s&>%AEcdu{cnF z?6rN7uqFgjk(oZo+Pa9N90ZuQN_l5F&nq&oxnj(CtdFY}xN$G6mt#B$!CUoVCn5Pv zGl4-m}WMIwGj-dOlA><4U*uMch%>J@FH_X_Kw_a`fg_@UDfBZAjqmu`yr% z)+S%9FC+ituPm>fe!SO^Pal-h!Dw7GO>g8<`l9J~LX?PsiPLA<0_|`9EGud4`SvCM zokhT|mrY7djx!UJ>@zxsrsI*O<8XHf_VHYLG5551qo>#QbuoUkrOGUJ)+r$=gi``+u_P%jqp^>K2rzv;%~?OeD< z)c7n&)hw0!dv&vxyE+oRc#XD`7*-dhd&eyK5&UoZL6ZvjbrkGD&^s5FiMp<$s^{Ho zC0mhy>1Pk>!c@B}e+aI&dc@d}-pa!l2C@`pLV}QfTGhcWl}O6!6Kj?pYjfDergutH z>(&nbjRBGHX09TkF}Bk>4KQyAUzK;QJl#%>Tv9{D;sz}s>@dJ(s*k#=S)&(3GOUsr zyOx7ZPF1l3>r4$_mb5M*9qwWr6SdYROx8DPm5cSz(Sh`#(>lr}vwNsBs%~~}?@1Qd zQ-=!rp>7X!mDu%_tQ2@iKUzMnDK7D;ifX5^*~l3$Ef2LWWYbs@r(CR5pBNGu`+^?A zU+Ea;Q0GIjdk5nnjLO#Zy)DJ{qL9T&Ttarv(0k5M(Ts7~d1Kw-j_u_di&UdYt?>-} z^gijA)9lEk%OD8eSC>VhOuENIGG`XNs$Um1m|IV(G~)~p8P1mtbVgTguWim;$?PaexXIG;zHeTd9!z;49wAtv`F z1?Ag7d571v38Hx7Qn9xH9;U<&enyEe;jBhBac%?Tpul52+mnKrb1y-#d|)tcb<=p} z)E()PyQ|?{T&L9Gzn{rJwwGOIVMQ+x&H7u9%vX$Bg54LgNXEe&78SX)u7=-^D7D}D z&Hw6UAO6$C4w7POKYf(7wot3abDJL1m7#hm(FLgkc5lLU%1a&+l)XqQ`8K}GyK7t) zGZ`|xy_8$Z=ie%h-A-ICOV~S;UHRbIgvW?~wFDo@`bJ}o1#4#pdL}{YYeH0RpuFQI zpWUyT>0IozEB)`sPS(ftd^tZ0%QW$@rsh zRuBDx?|{!ir8~nTc(XI;Nxn1QbMBK%awa)ObPH?}d#CC3v>1FU7 zuk970{rlk(nQVm6S*5&VGg$x;HwbDeo@V;mraZ=ooQUyf;{w&bvQv3mUPs zYBV%;o1@?&H@(iYecBc%APhf=N8o}_(AL>%Y}4d5Zx7*D$3oY@Wc)U|zLE(M>gyqd zhW`MI`U~#Y7%3h!ZXJ--YqM0^ZY3-UO{4{7A36(5ke;sqS4CrtucVOt1fJ|FY8^p6 zGE#~l8$sR;po?Y9ohB!S3(I!DG4=UNj2tR(+*ro|x!HO%YpG-Ze9)~EakDXB;k26ERf?O{Nm|a&F8%DT#DecW~{lg0O*ph`twoYEK7g^6;m6(rjs%=ma1BC?H1Ub)lf#N@&aK-2@ z1Rj5RDfXPF@?cURVKHSwaunIY0S+Yg^ih?qzE6OR`|F37VkbF#c!q*s$T9+_=UT@t zcHHwDqU~C_o-n-sQ!DU`5Z>gcCPWT)`nVNRn(TADN8zIG!sQqOTZ@zea2tdrmnAoQ98^af3I%N#K%sE0btz+NBN}$4@$qMa#KI-Xq&Y(6oWmShh z+vl{h?Mf~390XE=gb{ef@tOsT-KUR~&y$vprbu1h8ST9jibnH{%8RRK-Ln1>)j=;d ze#Kt(uo`pQ{Mr!nwQGqTqF68d_?@a0%|jKaSRdC|lQGz@Tj=&NhmozT;o@?Yr|&WX zWX^lM6Y$U4Xy7By87ZRflDDz!R}SLJFseZUb9A$Xx(&c(UXKI+n3Rc6cX=ud-(|$S zno{kjm*q_4c_1-;VH!67raOWjAY?pezwHS5Q* zEa-WjsbqEzM;EG@c@}z#qP?=0G%mr%ni4ITh#IyU%vZ{P>;hdixC$T5{TN}@*N3{E z8Lv*`eZnWwxfKE%uiB0u35aUGL2ug_xftf82=zYHY$1m0cE#2Gvla^a{n`*`)ZNeH zcr@`W?KJ4k)V6Y{yI>c$m&Dapy=YM7w0F0Z_f!mV0noAy==rif{0t++^}&NLyMKa* z{)x5?WZ#YH3x~UHM?bE$Q@;XJBCPV~WcY*6B(V(0H~`m=Cf>MY@@XvxYcSh<29v&0XMdGzcTw!dn&zaqH z2)w!IH`=R&jlh#246aVp6yn5+Fcd~8BW{S0_GTAgUU3G@Ln}FPC^+YMU5GrJ=*F*p zMdV96k<_6Bssq@v{ywaZ-(E3^b)_g@kYkLOkk&6M(Bl)vN{H%UJ5PU0S@+q#1zx!0 z(rmmu=?Cm75dCS_)!cx~!($l}1LCz5UImlTx%mDpp*x`+iIdF_fPegRE&^nCvp(X{ z^iHIu{`h5dQ`T?PWt*%tf=Xsnx-XK@UG)j9Lvgd)2*$P>yBIG7;^Sy6+G+hlz1RhpNN=CP=CdGBxvGZW(f!W!@w$Wr3jGK#1>3hAPi{ z4K%-;{~QI}`6Ka5jG>YUxEh4OXMcw;08`2htyVd}#G~LRF~!(K$=r^qa`EZA%y^#9 zUx|cJ)kuC$cL)3FROYL7!0;4;QL^sG49|1xr@jR-WL^=DM&4<~YdkuQpCSKQHXh?! ziNsu9vmZS*k z&s@6v?9)G42Maz0P$CWlWBg-|BWD)(vyS2-)n#Mu4Wz{=h8sdQ^;b%)tOI8J&|V+m zz*X&#uDifwJpIRmHS5EgsBy9JgPo{b9W|2ek*R6^xkAMISOR-fLr+r{OWt&x!SUOz z_o4rJQ=WALI`i7fzmSLxex^2HkIots)$tdz2hvp;0^E<#$Qk#)T1_%fmOPxXR;EqJ zMq3g-8%{szdP=`9jz>$K)%`5b^o{ARd-5}_<S`3<&c`TQb^?zJ>ak5hH0}2np)R0mRC|g(tNQg&$A3mt+WIv9@rOgk3dEVj!d#O*ppQ*K}C8d2SA^=oy2@GYP=Jdq$89#A(AvaILekK$_5VH9{hvp=wV0=3y}I!~=F?QX2jaBkR(@3N z_9=5RZ(d5CcFZO;7wvIUM{KAg`OQean1CvJE1%xm)4d38kGh?hGyF^IA6` z%mB6?zlJ91@sD}1=K+&Q>gv@84?lZ~7Wyd22UdE!FX|Cb2}w?0unb)JwDBGl>#-$t zr-8-2Z6#L-mLyrwQ_d8$Fbg!ZF7N+;F*R%%eF;aICRhe~Uc+NwO^rApc_+;?8aUn0 z7T2tPJD6z7%4{&jKPE~^EJ8b%xV$}=T0m-bWLh($q$|kBv%pi7-2`XEzTZL6#x`VF z&Lt{X{raaM{IBQrZue_~@NZ3nz4J1d*vH}{I7V8VvZ#kc0@)iL*#vrKo>@3{FuiZq zwIE0kO+ku=RHf*=^|=jv51qLnMOyt8uERTqL<5Ox0>ff9L@_V8r{`u{q=n*qyzJwh z9!mVbLI3aj%>BWoC35GjG3(t{K^sH-V|6I;@)%Un3ylAu(kASoXXx$dS-}Vi*o_5+ z4L4iVGn&1F`Qj{$hsA@E{IG=eDne+1*fK$Shdt^kSMFt8^{kpM=X zGx<0sL#JV~DT{ylb%gSmNN5_jjn^0eT7et@3N|)0h61D+0NZ^i6u@LkRq!Wp4Sm4E zHC5JCaeSuC>kY7tcEbM=7`0CL#~KNTxVUGpnErTdO>%}95)~rj^a0o<{Nh3b`D8&h~M_4&vtKev{a+UfRv^$HbIkaV7;x zV25Dy@?C}ZUQ0%x9(aKNp!T!I-dEBO7d9$!4ZfZbC}0ob3gVUhDVO=0$@s5P@MWj^ z|06*B(xB*6Qmt}TSyrrKF6(q=)fV6wa?l3&*^U&mCm%0nco-4bbKB&-vCn~kmk)hP z6tEpAo?FJ47fMfja`YILUoGv zK7-jB#bS^px7;Sv!viBMJ&3Q>RPv8&X7B-PYmne~n1?WY>)xhCL2!b+L_wsSPJ4(K z;Q!2AeNeaC=i* z5kW@-dNdmmYh6kd0V#MQgd6|VP@so)0I>TfIRiohs9|{Zr=L#J8ZSuuRVf`%s_AvrohZZCy6{ zw1WjWbx=I;S*2t?9o|Ndcr3+LaFrj0uxYk{|;hjKN4&Y-|*`j=nw%r24n4aYX zBHDjL3t1v+u~Gdeag!MY&>$X#AA%h&6#M`A5=@I1y?%rP`--S$ch*-yO25n6gmsaK zfOc)4Nh8Z08aC2`%ddZx(k#yh7{Czf` zg1)x>n+_!QdF_dzcd%cg*MK_-_$`tWwdi;#ctj6Fft^1H5JbcDM!pVwh4np1hD~Y zdc=c>`=YPj$LUH;|7YoS5^Ml%;+twU0Dzg=lcsPY`>HY4ZzZsNd5J4? zhL%Ck;=iXtRub24^j%jUjW@mK5~Mrv2qHm-YrjzhG7XYeRlvdQFuZXIo!crT|IP>z zP*Enc!daK%DY_`J7Y6MF@j;g-<*qPDZ)QQNVTA6Mh*o@rvd^mm$y9)^TsfZDaBVzfn6rq zfW-)oMGy$uwe4PyP@kiN%ElS!&3!-iY#F6W`u*<-vhastkhzav4v!SL!Wk}@PlB)b z3Tmh>Jg^x$L?)cuBW_RgX<#oa=n)ERqOv7)> zs_n|QOkocy@S0$9|BbQ`GZI(YA4(9B+OYRY{0#2T;r-)Ars1TK4FTc(PpQ_EtGybG z++^6Yg_3sTQFRWb8lO@U?64;40q?eWUlb-bC50I=HX@u&S-x0fKdCsy!SX`DiGqgd zG_;+*m-YPc6?q*rq4Wsl6<3yiR`kBS$lgCM{~HacFxFD2dHg$0-hxs<4z(?8(@$MT zw&6{noIAoG$eXoACJB-1MtHLEafe`CPk833o6OyFfn0ooU0)8xJP% z@hm-p+r286b?LqibpX{V7{zOBct-!;>jN-EiEBvnq;!t@inH#+#nC^Oe{#Wp1OWs) zWD5nYmp5o9ev% zR{5+G);RKL<&*LBB+Q0TTtG|)AkV;gY@HlqPN2cZ^I(qFfuu z9r^adXx0gg9}SdW>TWtT-Drh+0aUW!hi&0T0J)?t%56l)@^`p^8LE5Zf`NXw^ry?9 zxKCTiN&;r69IqTQrNr6X+n(OyVJ-nnbO!OU6ydyLpu1_2FpgzuE z38k*tc-%e1+V`}Qd@!U&9cq!J4oa50+!Kbgg&x-8+_JqKhCSZ2`Yh$11T`K8y^+Gd zdHv;!{s4_1t7&hdl93j+Uuff&D{b^Jk(ixETCkxl02&CAQ8EJd{&Iy*Jl96tF!7m! zz>82KB&-zPJ7|TL5WZ+tZld@>NRjS%9Yb?9IBWsawzn4~!>acuOmcBI8@<07WA`|C zXjI%u{eI!h@Y2`UVsDcM{y0`{?3bL7ONvUsYcpE#e1RQvsI~;VpI?^YU7s44 zBeJ_c|6uPuggk51r+A)9a)h6Zcc_l8u4?t}#$J#U&T=;TvYEa25ptUvdr7WAKzwAa zv-D*t?mmQ^6-$vkZkGHdEn4dTZL~8qZ~YqphG56Gf*q;5jS&EGpD1_dr%4>|3solx zG;^O>ck(fS*NIC>Ek~CgeF9hkcv7W}E}d81hZ$j!YHmrOGghp6JiBF8c7^`nS|X9~ zRH@2G+QD7X%@1f`+Q3bx7a)9dH$nl(3o3!NiC%>pp!hww*7cCE2Y-t|d&4M|VL?YT zIO>dleGrazP+1uyH&x8LrMPwe-qX@*uAfORK2{a0?Nut1R27*A#$4nj5C4$(HE0WU zLZX;?5&s}5RAhY9;xPWB?HyLdq^hmXsT%T>Gi_{!`}A|jq3Y-|sPt}@>j<`5&cb;_ z()ga;DoRMirJEY4l{A% zo=4!&Yhhhql_f=tbGE&IyTVJ$v@gr=HUtCX5-vsG4Mm1bnZqV-o=ok$*s9|i{rq!6 zg)(uD1!P|_3mE$mD%3wn03XfnLERW_vL-fUC{3@Ds1*OrnROM!Gu_H9VNmVNMN}FY zIu0M=M^7BMh+Ni%)`sDVnMAZuI|;f!+x&8^*0nOgM}glRo)$QxYY%cQ22bQ`Lq$XC zY^a`xYNLTkOQ23ShsYWo{5>G!)GT@&9^gjl@$$F zTU~LY7ueEO8`lrr&7{^iR@T7WK}Bn(ySRhy;wJ$!)*E^A1E55Qs~slH0G73lZqrJT zoYPj_Sau8c$xGiBt~3Gn(8H4@Kes5%>r2#i0mNhD-84IxF)}C-Lh=q&XRLhn0L*R~ zvnHeZBP5YuGoITO`HZh?NHF)Y$fxS7QQm;r!4ClcWJ>8(y~G{8!8uLYx#fh-bK?~1 z05!Gq4u!u}(2fs(RKFEc!j<(OwWhg*T+y~@Jv~!?r=xHnbjh&wlh>~nwV7fqBGcna=&K69B zT3{#}8aK9-=0gieIjJ+ax@zdqQvM9ZOv8EzyJNY3Ayya@5>#tV^D*WKw#;{hj(Iz= zryEr9Z&PZN7=F%Q{#zxEX?MvZ`lEo2l(Ah|QG>v{(Q^H+Z^7rogeA}a#bVxj=Vez) zU{AgEimXL>h)qtl1XUbS+)Iha)YmQYH-Aia8BEaKeY>`cq?MV|Oj2FhTKzbpgSTWg zkn}>{@^ODF3v@2D?Q>Xv)W!+Kgp?GI3Diio+7rK751t4Wt}(M4gam2_^bDmX$5Up` zg;>{(2l=xNMEN`NR}B{ANv;&+^8%wmA3fYfOntaw`g-wUy4_(V+{fb7y^@W+h=z;g zV9ib3YaVocJrl;E1j0RP3!%C7z;%+vU= z%7ZM}Yx_uY@@P$qLBO}T3KIm2g+rMPp*%&k?2}32Nb2$yVay#9gQ`bzFWo^a|uOqZ9{wQ~e*2-g{<&ApiVvR8MVy5kDVe&%Rez zxs_F^QBHxes~?N=>bEf==a#(=Yph(0(bQAPv~GX$;=pEX&r(km`nj51?>_!<&3CZL zGlR1~aq92u+3)#k{@|>AAvr$~oN1tDs1!m=5YgRT+Iq~X`k49wK;H+9&<+Uw`sY1J zadp%YpPIzZ=+Gx_p%X7B!7jys4z9X-Fcju_IXTmrfyJDb4xgJp)(#GGXG%~xI2?0M@gfvF|ytHsHjn9mG8A97%kn<)~_YC6wFcw%m8 zA+=9g-zR>~gkI-D0%Zu2q5H!3lgxRQeCB)Pw^^Ztk=2x_b9blZ?3^c_#NF}~2bsP$ zSNt0x5FL;dwhXzvnIgIGu5s6RXbkx4FnS&KeIQx%x1DKySJ|;MI*?Rj(truR1%OWbTcG;`a1J5;^ia9!HJu4ZX)5EkIp%x1n6LT(+x~h z0#!0xyLTB_o$n&PN4bk^_DK6)4q@Sfi-cy5t#_r4S3EJyCYx>)SrUhmIl4F$wR*ZE zZdnzA-QJxvFwf|oz!Mr(EJ6x1dI=19jKk@2hH3{r(>Zd>9cc6q$0g4`g3Wu5w9b#d za%+^J4f-{f)B5JiAUF+BUyl-=X7Lm-#qhcnGnqgA+Z}^}3lE$X$WAf0HaVB0f(hxe z73)3Xn6olJ#}Rk6F5M> zI$Eyyu*&B5M$XTSo$JEyrPwEX{EAZ{xUU8n6M8ZVCs%fsyd!3(-%ha3*%!7VJjs5R z(WdxnJ;*&%BteIH>#R@6cjodG^22ka&a~;NTq<>izUy<*h?RW|Pm;w(ZMwvT@U`q; zxl6*TLCn%K7iyg&>VH%BQbN}hS2A{87O~r1DN#;Je^c#q#cNu($hIYM@xbexH}|DJc~b{5&rZX+`FhwiGU7w^vQ#sD-y5Y)OK&X53-G255-Zi zW6-QX<2xTqp|V#(DsG#_`=4{GC{Q-jSM{DvLNa%a1tp3OK>*)kJODgxo|rFm>^DzH zz1QsNo-fPZ|E`lQRq-k3e1{P+XOB&J0{^Nj{AnV@NaX6I+04X2s9>x4y@t-S=EvnW zl=8=0B6O?f_3tf}w+*VxBi1y}ZO30a9kY{D5x-P_eiVG%1pYoL5^=LcZoiErQ(P{V7F6pe-Wlt8i}HfgR6Ov zaq+7wC4s5;)P|ggH9CsCH@-1UoHSlF^A z_(ifIsrMuA9ek+U9UDE9$-$#_N42TR`wRjriw#t#2tPfXq3aty0L-*{>8_I1(F7LXGgv*FV?A+s8mfqKWEPkm_jf6hRh~nK?ZGcHo z?twkCr0?Z0eAKVxN{n1$cu;F$^{bfqO;p#*Gsz;zpu?LH5h0qJuJi)pZe7zht8Lry@d3Q%FZqM~oovi}3M%vY)@PeQv~e#r1c=173ui-iCZ5ISp4rmgW8L zyRyakuXLmA4w!0nsE$nnwQL`^NUNh|<#D%05UQzG4jHzhwRc0wy!l*-zrt0kvv%}o z6LW*KHk;$NMKdkqD<&T`Irh#|Q+f0wc-mHMOZ+E7_aaybKV8N-iN<^*mAE0$^4}!P zabXM*An}+6;+J|zc8vr0^OTK7)#)y$U7p;x<0`H)H6Yx@bba;OZ{@{htiiWvdydi==lv6ZrKsWB~%&!^h{{XA-E+c7yZ;YbD|er z3$qU_F!P34F%`m8q}*+2#8i3BjE1Mf@81s5F^Rp773ez(8mq;Qh!hFjM2W`;v|kZ= zH?R@PeqX1c^W2;eLZef+ZahzlO6v$a?$|Kzvj5xTS%=iVD?1!>f(Sc8$UW$(Fm^~Y zQg^?bB7kOb_?K`ynmmodnjaOiva}4WsG>OK-Z1nvw+@ETUKi`3)%#h6%KK|&!Rb&@95;45`rx>O+~1FE6S+S(EouYx zNYPjBuB?%25|k7XL29$hu(RFgeSZ8v;t;{mb*3jkMIaaZ>#LmR7hy4V=spyuWmuoS z&i^eiF~hYQlEEQoxGEDnP|>d2DDagm<9ZvFW_%x+#XLF!HYBiIYoCUNqHe&4jo%It zq}iL0nbrh$l)Lw3q9LtQMGz5;EQ3kZwpS(I(qce4CKFl8P|EDKZk&nt#@h42U%yMP zNDiWGgkRLHYGwyAd*izIog>!c?}v5WI3a`@t$#0Uv91VMbx8HU{dE{;D6;?6FME0C zo$OyCbSv{amrSSQqaZ(61t}joLa^A;Pm!xr5&qVpa8bN&1FYAwNSr_gsW<7KUl|IW& zp}mnWqSSjUlqwC2Y#J+$l;AY^14Hc<-Cmv=iH_mLxzLfed8lS;f1g7%2{LH3Bmbf% zhgNuwb^ma5@o|e3>tv3w7McH9iKFtz71+0of1g=NByn9f|G}jAiD?zHTR(Thh=$H3 zT722mjIGK(-nT8AlP;!dJf!1VZ~1)9wb|WophoF!0v~cYQnnh4)fu+WD*RPoaR}%vJ-Vz$^=%eIyRpg zU$H4m)~dGwPq3B$49^Krd4L+GK#y8Yt6qTjQ`n8OHVuzxJAA+&3oouvS$=0{kpHxmEU z4>747s5tHDxeupOCN{6pZoYlT{kZO_$D2SG=}8rXc}po2LEP1#!R4Lbl6GHyu#j}; z)LD9FG1dh|`dSfG>LC-Y9{)Z!N4;mSS0y>+>L z+{K16Dc4E39ee$?ZVtsz;$%6I_Ju2pNj3Q?i2>F3h4zO#(|MWp5Z0+#XG%yO*pyAY z#O~y94}0Cp9&uh`a2?>1cfSR9sY}a#Q;G13i+_7ghE7os!{C2TD*yf1-BqKRE}YS> zhhTF5g+WL3dtd$szaEINiagnTRJrpqNUXk7b36TM+t9`Hh*nf3j6L0K8!aF|d8+#I zgipPkp2C?3{)wkY89&U9@8}(3UC)~%k3NXp6uFZfz7B}Wr&_hM-V@B-L_`M-gZ9|x zSbWYsO@7W-D55FyuEA8N_09Qc8GBp%vYO7#3+$IZ#_OZ%(phfEN7_GrnX`pJBUJSp<~Qz& z+#57pM|o@>W`;t-l{6Txm$gZ<>y+N}FSFyfPplCV%4mCtl_U+*d=t*o5NgNx$HmnN zOikgf@4{-DPG3@d&|wZW@t4V-Ir}p?kFxxXD7`Vqjiq1@EBu8^CImDJkS5+y(}MXy zZV|oBm`K8{pE|8h0U=*^N1f0IVQ}rYFjZ=E+4nI?XGd~JWG3yQvB1+jTndJO`)HH3 zW<0AL67yX!XV`%JOk^)cbKj9ENuorD%*FKyyHD4+23dX0*RLaB{x+-czXC-BcKKYiz(eI6*v|O6wQjPK;%f<8-IbEn?Dgll(L7(2 z{j``@WI_~coqn;_zEs^|qTW|_$dw?7gcMtkfJ)Q*o`Li%z#?&z_qgqUio+GKZpf@V zT^hw`sVv>aJ>HeB0DGO#@kN4u`kpfNK_fchJxk9A+H$MdYQF;+acjgGn$P~N-0zlU7E+)Q1jS0Ag*ftET`jrhvuU-Jz`7YiVKk000kk+k^7#Y(cK9!oigC|vD& zp_MLGdzFnv(72T%Hsa@1q^h&7uKn#pDvA2IJ8$Zt$ME2udK9Nj*8 z<>k2b>XW>^%ZF2IJJ-R$PRlOAu!pNMHub&u?5i{#$_$<xt7t;l!4OlmS8BOL>c3D)J%{MpJ- z1f8m{rRSX-njF!3+c?;ry}R|DuF>94i}@J_?EJvn&hJYQ;n%*J-l#_#ug?&zpubt3 zWjhKAm;5<;n$!~9X-v5jB1%_slW;EYb#TcPTjp8r(&!&^EB1dUETiN~8eK8Qd}Zo% zbzPZ~EY;^qzisJvN4DETmQF9nairE!_((4_a*9SKF1UGhMC#-8oIda~N)y<3$^t zRr+wFmq9iQW0x-T+`S^u3;G;!ndNR@EsY61(;zdaPsYQ}{tkMkVVd@9qLVMhK3|+p zh4M@MO2Os^e^`BG-;bQ%^K7u8OPKU?!%Xuso?bZ(J1amFF zdm3_jnt>;HwPS?N!{-RS=Irl7XLlHMqkl@HBkQg`WSPm9yKBXT>ExS>7fZ?@49qpG zy*XS(xC%$M`nL;}DuENp6csWlmqfL((ywtpR;7_0ix;up9CK%cL)=q=L6WyY;d1gR=82 z?W1OlLRAdFXG=E&yqB$4gpX4j?xYsdDe;ptdup>KhEmJyWEp7_Zt0~ zDetQ~$quEY;9CDj+4ZSA*8^4fw%ajKh=cbV$0Wvt*g>S^#+SxgM@S7a1Z;K1_-N*U zu2dqdooOBw_%wifXgPL|Zp^_n3EHnu{9vej(ZdwbBTW5%2At$XDVT|gL=CNFdGRG+ z0IjLd!Ih#m%h}^T_SN#H2)U_O^0((eik;K{A(D_1P_DMy}{J>hHZ_cdkXqi z>LfzO>k_MAh;N+pIb&dFFRU*R1LDBpbJu1ZSi#gIOdjH0M6%-I9m5>~%(xBpCgrg-D1(A{#=~1KKNK0*ujv*n45)#rOy#dln zgTUyR3`UIl9^RktegERV|5Ehv;GA>Sxz6=^y`E2>v>$&fEcPP!h{6?b-2^}`2(gK} zO9DI?KUjX?cirk4+lCZLZq1YA@$Xa*CDjDDNesGz0in$FX;Sx|g*)2)JiS**m2AhC zsR_LU7l6JzEyB}f7f~O8tl$q4@Qy<9HJ>|K8LcE!#;bO*H7jqGDvHk4r--D_^^)L3 zv4dsBj*f2+T9vgvJ2;i#1w)P1If#XUjM3;>47pDWJq$>Z|4C$aN|mAVm7Gz2;Pwou zGeJlR=}A;Du3z-L1Ah7n)KPIve@S2pd3BBF7TtJq6G>W5Ig} ze>fbXuqc=LO*6)Nj#n282vwGzaez3QHt-~5`9JJ@%Bbq-nyL*<$pO6etB}pG`QF2| zomyatvTc97{eS~i9 z%B90?Kuwfu1h>8WB@h&238pYyR)C7vo=VG>7i1mTUP3JIxMQF?w#@y+CPge@`Tz;C z|Dt5SDoDi(gy10RDX~1~gzcnnGV%Nj&d_6&kCRh5McrqvHR^RExncgE!obl_a}~X< zO~ZN$BwWgvz;HUHmdlb4|D1!wzTZ!VJ`p8T3p zqH}@yqB(WpKn23!_Re?iun6#m|qisq@r}^Mpo9j*EW@zm#5#y)Bq>V zr>6vsYu^pq@@y#b9>iYlqIP;4gO?k+2=KN1i1ttIys}q}}4~_R*8r%uTdZmWY81a?`TBrBuS-da>7eqL|k~(^owT zALio%k49KXA2`}QekvQ$+3EK?Kp>-8^Hri_?`ChZ)6hM0^g!(ZU)YmW4oUYA=GA(j zDw~*l4E-P}%-S$aD9+=!&LoyKvph!~9R5PdTH>A4LqoiPw}4xZ6%@HoZFK&Mhni>I zEbFUuv>3tRxdqSNxgJNJV0H{HziebwS3Mc67;mRX1KULUzOWN`jJDCDF0y5qzvIMU zWZ*(2l1%j-OO?>tMEDE=rE~MKTgIC7HaL*z#IY_5$JZxX7f18SK)La0d_=}hAET-g zDn=vjc#sPNuYOj##}b_cPQ7}97qw7w8)kS^S^3^kAPb>wMEPE;RC}5syCz;U&Gp7G zUNCAxo6bo06$N7nZcY=O5Z>}#jR_NXRfBUlQR_*V0i-K` zk>G*7Qbh(GIa)>9UbZ7jKE-Ki=qfnx@`U5`PQ%IaB}*Hr95qCKOQ`ZQ=HcH0BnvM9 z%RxkkWM^R&!}Az_p-5`s;gQN8Y89LiDz7~??LrX;CUZx|>EJvb&?}P4Gp7|Z7(?3w zSbp}FokA)XWP_6glCWFpc&`4+>CbDKNaox}e16Tl4ZbQ2BhgWpXT>~la(ss;4IMwx z?cRPvDumqraqiNW1Cy;%$6gpB(^aH*Pjb8av5ZD>z`(Mbh;noVPqFZuTDwZO7xEZe zretV!upa~^ifHK|6Yv8z@>{l$^(h5vHIgM^Vyvk`!#Kdtnf=nJP!qHO_l&cG4*96J zv_x3ku2q}!Q;pGK!Qy2bJti4VxuQAv)&c32+0w|BYwuqK;9?>%smG_ugA4)w`auqJ ztE~HI8P~GjIwpx;Vo+*u-O z^K4=ra&0`;H0i7o2-*a2C3s6FNLXy&MO~%!1$+Lf*({ie65V?`p5L?=(r1k8-t|uz z3`ItSdSD?5nRk>J;||KFKLHgHoGmO{-Y7=z0LCMhGk?*%ma9u)ZmKoHngXqyILcg)nHJ!6k5Dz zjPZkl%v=H}Z$5p-ieRE?#vMJJd(R2W9%6gW&!V!>Wx_3?Y*}cb8mvx)ck#D{o7JI5{;#Sy0lP66<{iX2f?3)|S8` z`}3yr4$Dj*wV$AR6AwM6)>r8%>xPzSqhFB~Vf_GcFU_?}e=NT<^j?Gv(TOQdJW72v zJg|)g_eb%xXMe3jTeZG3L_WaD)D#E~?{0O;1%D}C7w5F>+6uiA839{YJm2TBG6V4+ zV|~|$cRz866?m$TM>o5KiKj40;sN3kF|Vu;eSkQG!0+(-Rt%Pxa;)(Y=$hyV!?JRjS7)0`ht-xu&`pTy zDSY4{7vw9)s*yF;md_T$L7jkhxj~=M(e{bNGqv6vA2fweo}dM)hu3Mna-*N?PVx{F z5cIq4S;I6vR`Y@uqm~Vdm>^FFt%_A)+C~u(h)|1N2`^?>n1B-ZYGNmP-3IH$bp$4t z;-`U8{W(ZAa&-LJE+qWVI`JQGFO|K@)FA-OnWBuxX3xRKeY{u{3|6~wo9si^+>J$@ zO2h;#s)QBq6g06PpGic<2u+fm}NF#SUyt`O$2WJ?ks=#f*SCQGHWnIgWJ z1!kT5`-_wXr>zFW+G*X@T^Vk{$)B~^GJ5e;2eqcAsXL`LLS7@Z6$p@6=`&FoJ-I$l zGbRKE+Sb&`c9BxN&!!I{?DsrD8-;e+#aj)%yOh%aP3*4rFz=EuNum=z%2~`v7E163 zb2P81@quCd5pBWxifW&x7}6{g8qx|(VoaXtPn-!&H98|CDzs%yt->K%jLK{vY4T4dq8JP$%U>NTc^2nvGNEdL2t zH>y9V~>|M(e8>B&znCCbK;DYWOv>}k1)MnHRH}< z!oGaAV!9wR)AbvYb+Fh#$%E$12$Acca%YCir(Uz*qQsR{J|Fq`Ld@+7WFLs>xZ2E+ z6ZOdxg~E6&7%hLB5nfn5hn{{WBP3|(cm;MCi!gpJS${AvwRwP!{9tFQ?j4j7Gr)Z3 z%F$2@PSC=8-#Czw1eL=LR>Oliwv5zz2kOIW=%hdtk{YwZB2JN21LVF^K2thu#SNk) zy$R%}b#R1SDfP%uN^D<(em!!!KD+-}v%l(x)d7;H-FS7?+JmVvqI7wn$a6Rm9Wbmm zx$7UjqE51CT$j`-Q3C#CRN^V%S~-dITs?l)_(!V0(-=(Mm@&f!s}St^cyKFe+wAxW zIdjK#Hl8E_rp{(X1uzdBmrROFCZ7prt%ijD zm7Rn`#vWqF%%-?W|MV??#CjfKf>+9q7i$siKU*u8g4w_5SMxT^t<3t(5U z;}CO?H};}m`gIO%-uLotlOTG%WSXm%`SeKKl$npaY6Qw8JV{m?=`M~P)tlv3hvAHT3SDF=>~GwZ;2-dElT5g%*~jK`U^_lAKd zziGspxN{BLu(j9`?F2a?a?o;m>I;^PpQ6}kToIZf43A?8+bQrD=B7=HB)m{2{T-%G z>#D?9_mRg^(1~%nK|1HP#uJ)Y{-ik~0%i>&eLTNxYYA6UKH2}3jZl(f#Z62kl+8-D zPQ_&Q9zGVl2g3PUtFldAEB*-&K|W+O^qGwLOf!@k<*e`oE2Y%cF*Qz&6ezx;3nFZR zC&^foMdl|lIVb^#4CEfR-PS2#&e8!Vr;HrH=6>y0jDbBO8&P^=fDvqXao_X|2O?59q z?)A6l108=%3%P(BT?>35|dCZT#JibA$R_Af+w=pSPCV z%(aw>_&I4Y%egUmFCz60tzJ_Yq*E;U>9ofNpb_OxHDyWUxHb?8MlJ}a#JNCcZQVlQU z$CDyQ=!&U^cz!n1@GJt2I?(5?w7mT?g?y^~>mI(&G->B<`HHtBXI)_p@y}Z;sJR*y z+ned>BGcbuUR%{%xPv~Q3ZaPR-RW7H4<{YLcjp-^FP*+xe-F+1TiZdugJFDU<8OYC z4UOA8#9sD)`1hN6D|7{?!5Jppc$g=!{S)Q?)OzHJ2YV*-wXkPkQF>AL3N0-{s4@1b z^|eELPx!N0+(j(O(Pbqz;*#VV!BV-4@#BGOw4pjp0JdlY(!((FXeW-mm zR?mQU5UEH3Bg$7>4i$`bZradqtI{rhQ&Z{Q3=E@(4iz}xql~Qc@-Ld6Ss~T1A1~$v ztcUZNG`=L$^>;;M2;2q^hMF%JHvo64KYxWSc zY?c<6$Q)8b`8Hk(CLlKHZMK5AN=38}7B)MF8sHo+`}{Q6=!ndUP}i9Aj;+$d9RKw^ z&3|}$Z$N?RG$n=`i$$G%o3MzNtN8q-pXDP>nWkKi^z+WsjfDn5ftvbfoQzIW^Plo) zuvfAaRf#X{N3IHNsRO0>dJEBlH2&rayH_vyd~#n!J4UJbBMv+x09b;0Gec$a4cw3U zz49aIOQt8kiuU}VyjB~5#1Odc@R0{|Rzk=}X@_?BKRoS8`ZEZtM^I=h?%VL(+18x1 zS^v5S-?og)K01HK@tJ@pnKkZheIz<-BNy&vJH2ac`QU*1+$Y(u;vdA}A8`J|BLiD| zs+vD^KiT!}o`hvtlh$O!M*lfMOpNOU$mzzZd==M3D+Ln0Lz!6x$t+u3ydjf9xmJW5 zl8(Xew+8Y18mzwHK8F^}PjH1RQYGJvqEtw!Z9HGB-&lv(*y-^ zib%oAxr!3G&;Sp&?W}|LRV897%SlvWGZ*k-b(=MTVrRGl}DL`WQAb73@FcnuX0@;4x8fy#f4N}kB46HX%vcoeMt*E6{PE>z+VAuHK!yGqcPK7Kv&X6 zehA$aU+##Wj=}gs0p6;E;^~_Ur|th(_obioSP4+a)N2uzI;rX3-+eM@YgutSI2o&M zDgH&8GkYgfV?r@KGH0v9AjYjI&^zNCN4pksN#SuPGKMp`I_ySAMhaFFQdPqv>P5Yj zJjY&KZzRX3Dj=4ra9pXtb_RIP;$ufaegZGlIV$WyZI?9#?h#4?e{&cx?J=&wO~b2n zWmov)*P~B%#TSa=4%H#X2VXQ{Uz&XfO6c+hOxDNvBKlT-l+Y>ch-+rfCrbIe*AhiM zge1U`PZ>3A)c)~R*0MJr3u9lyD!}Ght=o1j6+4tPo8e(!WEtOxIfbM?FBC9s41+5b z9V5=s^LHf2{Ft|HpDGXGhWU)nw14;wm;@bbb0BY{GaD0+HSG4f>mP-m%$hf(X{}4i z)uq;UZU_W`ZmlZT>!l$~m^{YS!8WGp9t*ob@yc&qMjAf*VP5NgJ3?e;l$}Ro@91Tp zq-LtRGp?EM=Bu=Re2fI0G0>eFrLOLV$c!iF-6MDV_goWatG8AJGs6bST`yO`IY5Rf}*TgkYJ=}?mK1P(APU7>o~d8^`LqB9KBR#oC9f7c88LSz}fgEEu@?0Y?T@E1p;T!>Y^XsnL0 z_R|>eMF)V9Gzt23Yv^+XZEStfDMx=#V<=;2Dh^s2nJXD9C$#iE_@22|`D@=#k7mEI zB+XmC@cMeA)>)?MkaA1fCuP5~Fp0Y1ya4NK-v^y1f1<9eS`@|dygl^zVVRHiE?u4@ zi_#9wSOjZgp!?0d4}y|bQsE+bQOs8>A`9!Vf+5(ZVfD96AGSKh0^OwKSr5S$0c9T# z;WFZbEX@Ocv%9asN-x45TMJ84(pSbb1j1@w2H0pi(8$M7Jd5c>aNkfN|DCB7^DHwY zZD9zx!7|xs;+1}`^`paB`Q!vRaL!J5%W}#9`9Q=XI+s3vOmtE}U}db*y?+Xs?R@y% zETFy-v#gbjNmsc{$ldlG=I;s;Fsu_R^GS_jIUDBpqg;d&RFi*Ur+J-p*lidDT`1{c zWj`ypM;Lhp7)bGWgfmE8yMlQ0Na6{Dq^b<)nyKivoNi}_|q+Mv#o@7V-))Mdz7DCG^YW@nsijoQ#4WN)a*6*5N_Hy zHp4SDqV$eebtUInH55wQA;ehz{vY*h#lxS>96WwKk@>&+ano@e2g(ZJ?CZ>`31aqX zebHQHG(@9%);2Dts~XjFfX zC8o&J_~5~N5%{%#Iwo|2hA9GOH5DkKecXCHE?~Hq5WacUXiLX%sC!)kz>8dVRCV14 zIYL<+s3nW%isJX$16#`!QmhK=KbI**6V(OAcn%k`*j%=%T$tX&4lUImdOkH*59|eay{({T zFgDA6-o?(q$rcs2x)f06^v$`|wopBA^_k5}3KHE+2;~51eJ={XcHd?{0J6HoYv=Qs z>{Of@Iy}mpZ|Au8+}U$H{^k%Dns)|KNf+Sy;AjFk-jVpu6_1(r>dqSl;bf?9?n*q7 z`_@ggvF{bC%lnG8fE;`0QCE&vOa&ss7!Hiz{4zlEXM5;*onr-!FV{J@B6ZxIkN`t! zp2bqa`$vITp%jS5hTd;MmI>AG@-*$H%aHgRLoM!6*MO)sBSYO6~^IKqpzJ^4uz)qUQ0@2h!;d8$Tk7Hg0)>pRF*B-M>?6FDtY z(3V&>+!L7_=1=LUbmKu#mZ+UZaj?Z-o*eD>mbV71&ih+gto@g*{leuKTD=7*CM4|% zB{aX(dXgHuWYw}HF|6H?IFppr5y-F6UY%Xq;WwjF){GemV=>1prIx-@mf!UkKI83f z4B^@@@tya4E=_#K%1|l0s&SK%#aL6DJ)k>UG(rO@$Eea(i{-td9MHE|Zfp7HPI&VM z>-jKuP?~=c8xUz5P<;MYts`Y+gJQzh;eCocvgAx%At*!nsgQ)fw!vP!Vbl7{l9|Nb z!&MhX5$bF5(M6?aN?ppTz60ovwHJkx6LOR2zE!`Sx9c_h5uYVk6As|o_u}O@jtB$1 zSn@|Xf-ze-N%95=NhdHd1A;uz;!M)_S-t5w!`M9aWX@ZPRbQ2_GD-ogCkiM$d!PrY z`S)7(ds^cW;vCirsOMWnv#e_J8G7~7W-~p>GJUPFAm(2o^#Tn7#lx?e-|=H~sK~{$ zR`H>ac(~B`K$|0sn*1>43XwIwj!sHo6L)hSM02mfsv|mtOjAFmWu*7ntlNR@_mj&_lv_0Dhjh%91-lVURs}zt~mI0;+A~KYDHwM7I>6hGGHMFnQF(2y2GMsK7tzR>G zs@qAOUi|kd_HmI}YygQeOwr`uahE$0X%{0;;LU`e-mmv^5~sg9uA}9Lop(OoU`^RO$c<)r{m*of(jub%dCj_> z4EI)yF4HfLJ6U>k0dtf`>_>Z9Cxh7-Cs-mKb>>x)-nF46>#m0$;zl_^F?Li%3#``S zIlrbWr*<%@$#k#XkKcYho7|vXdZ7x-eDp^}GiCYD+kxN7gaTpQCt8kW?|t*2f8H@U z<>URV+@XLEzNdDk7q(Xi>xr)3o~oOR9Z6ApZ7=9UY-)ZpuOmBzw|=EC?6PH$r@N%h zN@SK)R)y%NJ6bN0pGrl~P70YdlRg~B;;q{j?i#r(%p}ZQ0YwBmCHQv>j61P8*38sE zxdQ@ z*r&UiPCQxVtmls4=+)$6Y)Xm9ntbU-n&^cmLf`{nDyG+v40is$obsO2z0_)cDb}x; zzh)j5j`K5nAyvUE4oZu0E7(e~LC;#?Kt2lEGorKsbpcQ*ZSoeZ#zOe=Uyr?Ib@f?X z=YIr9gULl>mxv=?mcigKNC}+`r(ZS)N z09|M~8#|x8@jRT(=8oje9R4n@5b1;CEVB(-18V3M@#jdPNJqYKjjwj~f=db273qzV zf2A%HGLE&cv7FOHTc121C%o62&^}WdmKb&lTXE|A+iw}ZB5Av1WID1T;HbuX?)3rj zX;Rfxym;4VCV70cND>tmN`Dxla2S$&j^Tv%^x`^s-or|H{Us-CaZ>`i@>&a8u3Do? z9^9Orsj(|4?B^5P6mnp!_@gL1T_iu9X~q01+xNR5k_+&?8Mbg!M{9kd%ip<^7#CGc zNPWZo)P4M2<6Xw=M-q-?K`4Ifxat&9g3jv;4~anz=kP(|&$%AOKvL~T#r@HTb*W4> z88?t}Hs8PK6eNLnpp^*QD_`Y*p{id75|XiYj_*s_ZrB`qF9aGx<1!tDLhBx3#+AEbj7vv; z59O31OTBp7N!+F%&ls{NAFG#Qc$yO3g`QJ1tWf~62n|j7U)gw~i#2wLNyd^|eWW%? zoP6`L(;qN_jn>xdAK!7u@`s$CJeW_vh_33mQy;y^o>aLjLex3i&y8N4t0H6L1L3M# z9KI{{JLwr%-R1Zq>NntIqwy-s-)BMKn|`dId9jF{*39aW zocg1g!WiqUt#*6mmbfu@_34?aR@?bRuj@AIj$TKgGO8I+eDlF3duV~!ry!g1uq}%| zeY3@!z<9k_vWURXj*iLW753f(st`6Z`c|+pG@yPAt}EST;4Q`hq-aQ8^0G248pxEY zn%cQ5>qL;wc-a8ztjrLTi4|rMid~yU4a}@VNiSFzQY(QSkXxo1N}9R=r^i~sk`nkM zoCm-6+;NHr3(z#m74h5=?KDqjrxx^C{?7h2;wv`frm1s?(T2Nt9VHe>WSCgtO!3Y2?njZNVRo)uY)3V z!o-O&j%0>4!%{`Pp~=W>4QESqJ^F^PsH9SQ>4D<`x8cHV711R6qI9J@`r+_#bqcEW zqlxESE;5A8N)1|CK^ye|ev_Ce4fQbgo-d*|P6Vyw6pFkz>@FIPbd_}_q$gOGG!LD9Y z4k5d>YKDvQ9TPbFElL>z9~hE~3c63*gC1mwYG=cPLme&ozAj>PNTxKXX=gU>N5j|T z1-aD09iekLSqsW)S(-xKZl$TF$(OTBLOFMr*KF9I_s_b6VF)ssN_9<65(od9P*FSl zb^*a}cvL(I3fK;Xc35f;b zZQ{DGq|}&-S)gpXXs(sgU=dISoCK0g^q_NJm(16DbCN4NvjOhv8L4mYOfo#1PB|%Hl&-_ zv8$M5h^=k(&{@9b#6&i)oQ?0g$xN1*b}j!_?-mx zyW_($_2X?13d{LXr5(d=<|aljHSnh#&Pc7KZQU<6w?{U>s*{e#j`C@Twd(iZ6$nUvBKS@(Him< z>ybM*=MB1UIK`xmvn1_zea7i3CB%14*)QS?V`){ZvmY5O2S01AY3bG~$2{tasXlPi zJ5-1zTRJCNPlThK{I3~xS-0_JIzqV?$MY2mi?bt_9vcyk;L4ELg*1&PJT8mDIb3?l z6M?KQ>7KJ!Kp6{l8MT8u);71^P0@9xC4Op=;J*1FW*~HGHJcu)KFflN!TS{dVz*U^ zqq9SEiHWG%SqqUHBSLZ*{2(FIjgFwkq}b;~bUOtriqQVekZIsWTqsOE#>u8{KJAa0 zC-sDU-OR~Bcp*g4#ph^7Zdf;FlMc1fsI9+ZpOMwY)YlmDLEo{nk;Ket)J27dD)~p;v*cEMPAu3k?c+VY3CP*Hd-mzAcUEv-M z{k^s!uc|?rXlCX+5#4P<$)5d-;%H@pSM;84P0LY6i+%K~Ql-R>Ma(WE8<-K}6JRgB zG+=PVE>M&?v6U0BKwNHs-#P*!KQq#J*?V@SydmI7Q76XXj!HRR*d+odtIp%7DmzHm zB5TkCoV=$9ZIr^g-)v=LPvV9PKuwy$$#r04l+k-qvPy9Gv?{_kHJZJ_x#o)^DOc5PsD}z@Dzf}%EREI&D^cN)yK0mey49vJ3k03Pkc!z_2C_8< zEES*ps}dV?j0tOOm~|%_9sTY$VT|unsgVnkKIO)gl1cRX(D3BIF&&GiZ>wLRvv*`A zS>)~q49q^IS;07`3GekQY4MBt%LAkeB5;Ti{eUn#WW=+poBXEfi~Fh#-CVC8T=%G!uyJ~ z_#v0wZz4$0!1l2PJFBHGe_uajF3MD7ZttIC)3J?j!p^pC#e#hM!`F_(FsU-VaMq(f zIO=Lt%%`SIl@x2{I7vQR&%IRGfYT0qw0qw2E39*4W>;!?tFv_d*rw|sD^3l}O%gj> z5Syf7m|gAC-|7<08-sck>Z-*JpZS&dF6?B3yUj?g^~X)9SX}3+5e7V2+JNzFo4$QL zBj@nzVsv6iDYkWwio)wX0(b5+s$$MbywMr(c9a+6ijR*WtqvX@x94$Mx)QqJ*23+) zdGfy2qxuo5O~k*42RY^b5nV728Tj8nIprlZR6iv}^#JbZwGaUJM~ZA+jDZv@kI=xej5J_|0R+02AoIu4Nqc0*K=EFX9JVxF+lN~_n#z(X8W?i`6w z5O#VL*(1n5h(;DV1;u-F(t?@^nLgvFju~x&&u0U9P2R zz)vJ;b!3)?@3;tEGtAph+la05 z1`@?a3Nyr2>z3bknUvw5mv;E5z@`F!3JDV%U;4_;Zy)8&|7yG&&|P`oy4b_tC8PH6 zc|-Ix+&kt>&`v@4I1n9gRtr|7qy%1FRMRBvsZoQ96o1w{niu=-R$5sQ|MtyZ_lZW zHO&gu!V`C#k1s0^Obl^XQB7s4%^Oe*lsbg`?5f>E#kjkJr*G_hc$or9*cD#gcq3N- zw%OIIN+vTKd3lFB?%;2IxsYsItiEsDp-kYo855o?WnPUuHP6?J`D-B^^&=u7|0Gc zp5dB$v~@wt>}Jq^{5@<}w6KK{pc!v<82zaFGo5%^`v&X49@NA_T)2OOIzoWv3F_dY z++Ehs*56o^h7Y;c`)ILwyMCkNtx-%!Lqw^nfSD?aVrE$B816cLxP(hW*W#jXO51#n zlL0)!t{rc$%76kg7sh*(KB~F=>E`bD7Pw-}+OYfTyL-EW>XgU3ok@r1aiRAxyMFI$ zTU^j{CBvqM)y2*GX0h%eZaPo*smaB&ahrVS?tCr*$60}-eVW;fmAY(K#}(4q(MI6Z z^?OqJX1g;wyR%|^>>@26PZ``>#MI8GO)8D<+*2ymcx@b3vbNN$>z6fc$7X_{db)mh ziXbO8Wuf$fUEBG&Inw@B4a=jl5wxq>!Rd)R7s)bk#w5`9XeMqpwdSW6Jvcf7z11C4 zxE0Qd-G6Gl56_EAx*u5hsuC0pPuk%lHegE;RBhaSa%Ch9dz$9x8&1C?fS4B4b0sbq8jDR_wV|!C^|rsx$k0ZM+2CR7zvBMl~UuZ^v~oxo>LfKkM3+d19tKO=n31yDJAYmnNmD zc|fz0T>~~2S?m{u4g*(>QdZ8~z5Y0Q-E@3}c2fP!pDjD%Q}L3M(C3r(s39;eaOJm` z^&9kl{c+9Lh~I0-qo%H{BPqYld|;>kUMh(6uXKpEut`YHbyq#(M@$P{kRVNzyOH^2>j*69_-^LT5WvJ)yaBn|GFJ zja__&6+HTCQ`b$4o}k7n7H`N!=_a@=EGBKXDcE+|WT$^dQ*QG+4Og#SyVvm(FTI`D zUT`*_#a5X)H&`*cWo~?6g)?3J9p#wAp==0!}#LypKvdpwYHhrY!zs9b^NbJz}=F$_exv60EmvTPh+<^ zRt$QD<6h_E{>t^i-!=pSocAr_=gv-EbmLnIY{~-i*#DF`R^vSt>Qv5Je#<#bu#R@| zI$W0ZLAjz04Z}NrG_HYoADHeyU1tbOmBf1QH@R}epd6((#p4|hNuBJwdbm!;WF_ydrP2ahg`>ds*_tCR|O&_NY9SiU*IAZvP{nqwEOJqQD>+H=hWrfZe2% z>G-#{@+W(x94r^rldt_&b^T29fr|+;FFbVdP74uBbHg;pO6grqilv;g69L}B{zE|hQgBc zjKjL4X4=RHa&G#J!v|CusH3^Gw*P**2}Ez_`IHx?_l-Mxf5^H0lPYmUBBZo5eqei@ zKi)qS0hHOwZIQ3>V!R$Px(vfVM6aZUAdhz`bZ+%2QFqV?gJq5eBf=(P*_pi}_E_C{ z^H~z*ft5=xf1vVmXyXUY$G4e#bD7=IDqICM7bUkLuD$v%7+2niLK8zM1p`<_ywXx} z0e1jWv(aKDauv`?fhl&pTvra?jSZPn)JHt{#F?^-?}vu zjQUUx&MMy28`x(n2>hVYOc2n`+&y#s0Nm)^Oq10nMd&_}@tlB=E!RUK7%4U^y$y~5 z88yw97JwH&JAr$lxO@$atAt)?PEHlQ-`KVJ>~bqSK+bOY^=sH=2kg^7Gla)v#K+dZ zZCz{7-Mz>a>rzK><$R2PT9c1pe^*!LD`=+04-~H_4jsl>BVf*-HU+P(! z5^x*0be?vMi}o=()CDWfl(l8^U!X zvx$ttyRB{o-N1fs)_6uNQ+Po8(C5wdlnP8vmPvo7O)x;9Zsh-0Ycel>ht`WODJ0nU zFQ0CKK+ORt3^Skge*yI<&LxhE5=!8zz{P*(u)n)Fz_*%k?}ha4wx`*lCvNq72nQCMb<0jP7>-cSpLBWYEq__n<*3_ z&5aq5#W!{WE{Z0|N~Zf{00gU+>kqE0mo6mS6ZPpm!QCOu-}If&Iws-Fii8ZUBsRlfWe*b9;Kd(ndnh*$N#lRtzAvwo4eQ za`#BAH)5|o@Fo==in1KDH;elultIh+1i&yz8tzPNgVF!O=TCN(Usg{jWB=w? zhNtgkpTwrc3;HyBXSoVp{D$F1V8<}ouzHV?S)RZAl{2o4PsX%1b2gs4+6BPLzXY^( zXG2&VePP1B#fR6hj`^(IZ1T2}Y4-s+A;QV9rkJVQ zFt7PcGaD6QpCiG4mA!tZ5*bl_&J+!b@G|pRN>W2iS+z9@9%UYy)zx;R8a2I#6!QM( zzWip_l zxVa^wT=W>@H^P5csz& z!dkmM!9IY8HP8qPp%~bnh}UeZBn7a=^7vj3X$#P zJNOoAtRWD-HrOxP9=-ObAC5`^ka7Q`3#-Xt{{T>KBI+vang!n z2P~tjB>{r49*m!KoXn5`EP!VF25(F41^&40OD%S#7g!7;DX!NfWGrgXOizv7E}*V? z{y!h9Em;vk8v))xOm6L-{OJ$990AA|?40;e$T>60nuSJfKo=GwYn<-mg&i3u)1n-KyHBL*pzH+|45ZJaGiJ4+iseIf&gn`>>VWjrgB)8EV0?0G|mnwg&_N zi7Q%tmHjebcLopbDZInWuFV^uNu!0O+Q;b*rAB<4k6kI}Rs&t4c(7%_Qqq~a3bP(J zu1(5tX*T+}yDtm$Cv8mvi@3>*tZK``|0b9beA`ICJ(J%XdP>q0>hIZfTgmbkWuK-3 z*GjDp%=bwd0Ly18ZcFKXQas-g2v7A^RKQ2F3+10m={av8P863oh|)wmaK4QE|Ez!i z8HDmbJL8 zq)Jf7d6@=pf~x5RTp_b(kIzqb!>oKpuS!N>vn89fK>uTy13-My1(n$d0+X`}#^u5V z+V)R(lyU8CG|nO69mLZz!JF^s1M^%Y^AY(117et(Fk>*#B8C!J;4kpMA3xvQs?-zO zR!fNf{GM^(IC#I;A{U}i&DOmCz0A$XynB=u=+4>u4>YGI++FP?1>|Gev}SRlW*G+m zHH%y!L)q5<${S9V*#Bdq99Ii9H`7||;{mm}jREhxK;M$!0Tlax%v7PYEr2_N0d+F? zucjl%V6rKeFZ_eyH$ppK2DXWB+=X2~ zYFg#Zpsayy*R2p(xI&HVfR)|=jT1ksW4tMq^+Z^}V6)%2b z& zzv}zG@n0X>d*&VA8>2O5<=xe|s9=C&j@jJb?fB<+UfGsp2uQDX%J7d7_)q!oYpeTdFpr2)}q~oy& z>iFiGZvn^MH=cvulueN1od;munNgGA=edg_YpMo#W zKj8aQ^t(*wWquZG6t2U$*=iyp?qGMHY)v|hk-A(WPB53ushfCmVIx}W8V;Lkw;HmI zSKC|MTjW5d2^(-yyTAt2H+O~qQNeTd9`!C>E~*(d{PhwTjr8>RohTr&UOH}a(VyI| z$$BjI#3R~EQcGuws%F>_!@Ktf8w(cKNAbO;O(>CS;7rPOGUn1qCMHweNQ-CatD zFc^c$VDQY}|9P`Fd$qW~=iJx1&h;t#X@t(hr71T{gAdKu-w6f=NRtnisd2kEvCOO4 zeyGfr&pn{@22T>s=pF3kc#k;=f+K_`hNu4fIv@{@N^=Ke&TJcwLLt3}nAO^lwc7CO zF*{;vW`-iB5t`!6WVO9pyLi4o(nn5u&UlyqYR!&>FXV7%Z+WCcH#=}v@o+3j<;&n@ z#c1yAc9}lTseR=%-ft?O`(4eO1~HXr3A(YZ_+0p-ET;0nqBFwdyN5XL9;~byHukjh zNmXmC2D1t-LLsD)5gr1oJ-QSjQW_Z!;Xc&GWuBTeZMqBpqj4WNH(l zPd&0tIV1W5fCG*ZhIgrP)KPtLPlcYnWxb%Olqe`JIFd0BnOf42adY(NgL~N3GWFmW z<`apOLacnpTo|S#Ea<<0|KG+q!^M}R;hIK$e;lX@n0#~kW(D?F+2{s`qzEbh`Hu_o z)4%&_)o?19J)))O&~!Y=Nb1$*KW+sReJAqmVBdDOBA~$)5xn^p_nr0JK%C8jg@!^?z+b) zdtRp3xb9b260GR5hN#=LlY{#68CX?wF~cAA4*k~f$$S*YUpuWk``d&KdF3sK_Wt>g zZ&-Z0hD>j6_+%KkQ^xVzwUzbd`m2FB zH_bW&j*ueQc#|8s{qG@c0MX}NRADM{36g0q-WA8;ezMuFdPqYja)RqvxnDjOL|XWd zzL*zvNAAg7|NXF^2ur=i5&vFaskA&%zI@(Vs)z4Sg6r&~@9axx_((RSytEp#{EZu0 ze0rctQJCv_S(njSk>oV}p}kz=$^99Qi~oQXg11P)QAl_v1=}Nlf)fDQK_>E#nCOA4 z-M~%YT2&&x9Y4S&75!8&P(#EhDc9$)jJ8?`w?Lms)48V--}2lPR5ajhmV5G7M2L%$ z+G%F9%(kPP#rO0-FwB3Zaxp%${g0Ub|Ck)1S6SmB#hvhE#HK>%GgP@D-6Rk7n$<}- zjVJ{nojmp4$!dcqL9H+L*hIP-Cu|hW{hRrb{^N9?)Fy%}_#0Tg63%Ufz(ZZN>e!WC z*=BxmT_Ik*w;X+QZif!lXlI7a2H@2{65{WFWQLP2bZ2$EQsYD$7-W#LCYqOR*o{4M zXTd=f5*s|b6ct@b0r0`6QoENpn9E5vZhq-HV4-fqn!S}bT9hDnbDCHUx_DK3C~gW7 zA;kY9zbV1A61`SPx|o#nY5_t`gZX%Zt7^ZMYr0Ko>k07)RQXVgV^2V2>io;^E47%=4;qO zN9g)RQ}c^Lk<$nMu>lFEZvrmrp{qp8VmLC*&uCdxe%jqXPkK?OcY+K9aX(VSsGkVR zMoFOMSNLm}1#O=I`Y)Lbv3wH&k>avxwt>k{&PA$XsyY1+dU}>HgnB%7z640aO6%P3 z>U+Ggu3{@5O4&sJSPmxStL^*j=I8AJQpdYzI0cA8OuXGL?<{AxVNi2}ibZSV*=B_V zQOTXln)~6E-UWHY$+gFhVHb|e1!e(H$Mwe^zCB}eH^x}Rq_oWSbaHp%F>qg?YXrvAw(tFowU|&2Fe5w3_Xp_jtB9vt*_tSpqG8s$D6!6cou9jMx0ep@ zi*JQ;2m$aTnQ|Pd%fS4L`9dvzK%OfWv+Kqy4Q6v*^~#L<%IJ3libr> z6l0&*7)>@T6`45SN4D8+4VX8|{Xo7|zr6Y>P%n;m*wKvn2?eIC`|jC$hwdHu)l_xX zSptQ$z1`j;ETjM4G~=i(MuMT^68F>&jbXbtCksaqehCxb^0t^2_gyn_GurpL7ayc= z-)A4l0SoI_7)N-tc{x|%%0)iuX@2;S4%dnUiB}Z z+?5NG%Y@Zk4GaqI?AE)F^2X`}h=b$fGd&&Z>qhJy#Ll1VmCN1ny1A-7R%=(WPNXv) zDGYqU&Pm?1Jo)J?)JJ;TLvG2VTb^XFu`T#=*m_MGxtM?pLCww%G5@hYz)INKF%qbm z-Q98vz1fE@`C{p`yv@n|oC&Mi{p&ruyF=Z|FT7(`|2))?F`?Ze`nI-$xY}D?_~j#% zyE1O*HEIOBX5<=Xti-9R4P<3o1ck!CksB%`yG7ziHYFo#Mz{hQa{B43lcZC{^_ zTPNh86HQP1pXD4DpgG1L*w%>}<=zd%wWfUkxQVzX)HXBWli;St@lGE{M$NSHHzC}m zs89S*mzDn?r~*%cGfp}#!lmW`0#`g8MTC{q`*@%5xH|Lk3_x;g1`>O#aX>(#ZjKsk$R6Uk*Lf4t=-zG6bVp`|DhE-M*D8 zG(@XD1MBO1Y<77{guc>Un_`)3Ov@Mq^xj9t#kHEBmum+EDTaxlFmFybOO!$>ZirP2`rPs}hmkV7u+_V2bHYp({_pT1i|AeY;AzsZPjmUdfJVLRQVGv_U> z(UUg&dS&IP*iD_O3MJ?+qiSnfSB#+s=Acc9j9qR&Y@cRQMQ!;}a(^R=ec$l|M^H%l z=@go7_&-w1_hWC`fa;$Zl|J~f(dKyvF1Okb*)70d*SF(&^SNIIZ`&x=4Hp-S+Ks~| zQ6iFVzcv@vndHdwt~1i_0le&7Jlv6$0$y(KCD%u>V5nwmu%#noAdZ72^N%-X50 zNPdjCa`(}fo|;a`3lxhoIHqZQy8mv?_M+xJd zg?41+?I)dzZtG$l8M6e1kNp{rxom%8|Iq->Sj9L!jse-VkwIX)oL+0l`aj3HYd#5o zf<*Sy-x*$d-OtyS!zCEU7IM4Vv)tAPTh{H|%A=GXjTekNgf_0~HzCPnvLX*cj*l8F z2=|vCMJ_C#+^l{7^Z$n*DZguJA0cPe#VEGQ`*6b8vU@A@h6;#mv`Agrt7oQgi|ar5L->0@->x`I_b2c^Yhe@>ICd&sOHdIV z3meeC&AnaXkmsu9yQlZ0aMbuSJa1C3b>(@HA{KUJ0DGV|En(m*)wRsf`n|WdMbcRT z6#g^|qjx#7kyheqh@!{hL9m#aTJ+x}8AM zq@tYTB(?KmCDZI3rzq(6##K*}RvcfgS*{Ux9<-b>P zXp9`Hw^+nh9iv3*YTXLTna`cA)Taz~Cn*o5Av$puT2WiMBcL9;yedh<`62BtI7u)Y z^DC+S`+z)nInb}W$rr2#jOKt8Uredym1Dux-S3o~%4xtH%Z=yGu^*>ozBs;;uA9k1 z!E4$KWRJdEb*SlXk2fCtT$cu7*P@>cMHuD2^$*NYa}}gYyqIxsa3`RR87M(MYZktU z7A_x#(^gA)7Kk1CPvJcI-*X4EcI~g){4&Ilh^>J0@5dc`fNg(%aLsFI4729dRrW}m zUf2^e^)DbbnSJO~P({bAqK}gj0$Bo{+rRvCkG1?jud7jonntN=+e6EK9zRXzOVzB^ zWdGMK~N+CLJ3Z^*eA@(y7E48*ZVs%pwO1#(q`QXzaKcP zI3bA&niTOSv!*89mhwb4ORcF{KOQ=G##y zc67OtFjKOyWZaE0B5Tk7gTN+f7CLx?|6>!}cFzqV28_P*dVWdZ_jpk2NUvHL?VCit zXUS4At0lylN%TaNngrqVz1Juf*7|170opIx1Ai7I);$K{@-&1zhY( z;TL!}l0_^Baz6D9TWKyl&KOUE+sklIAD+vC>=^~>ocbSC%)7o2Ajkc#WgP})LQs-P z@Y1SgbIiT~V`fJM``6k0bql2gD;(^Jr2CYidlE)2$x3+YqEgpqB$_ud4huy?+V=zm z!A`3JeYtU0i=7kJ{OwIYr~E{)HVM4{&uj*X`anN{Gl!QSv% z@3RU~32FpgggqIWRez6}pxoW8lUnTzfQpsnqVlt7PJzNehPJ$am@gpfI)o)nIjv z6e28b&^_tAa9NS$KHwbGTQeli1C|TnCz%iuiunz7rnR&E4Hye?P{xiSuE*XXk8!4R z%DZSG#ZEF4AzN3vf80N;LrWCiNvGpBD-WJkbMSTxfKvFev20qr=a)#O^XQmY_;DlL zXKNF2yCh1hHnAkIX#uLP!RQ9MEa}!YTw~ijs3RQ`G|mDmtNhlK`NiTqZ(<38AO9dx z)k|-i?pFoZ`xYzYlG(WIm2W?XTPWpfor<&;n)AnlymrX8=ZUVcee7oaM&q$gN-_f z5*TQ%9s62lCil}5)7r1`e6N9*1c2uV;{z*mR;jSV=FoDTt()E3YiOnGb_x1>>~50i z?Jv@suY=o%*0&g=Jm0m9K`4;@zF#pbV8i)!)9gQCLoyZG3oP1FYn z5%S$tqfhFS%QeA*h_6LT7HUznI#$TNP_SB;Wpsm)p8AIf$FDyVR3X6$!Ho|V^{a=- z*c6zO%f>@5>dXKB_+>_EUgvPtB3u+Qq%6x;h)G$MHr4+H;JWSonh7tdnuRW)hE!zP zt+$lgd`d8Cv;lKQSl!O{vA*>)?Jb#cMKWAAv$&loe&F}}VBWmMeabB*=-xPO42hC3 zgc!`W6-2<=b|`}g)GOx_R-~D5K1G@{awWo8w*X*8BR=L3%rw-^S0!PmuG$LTg`pf% zwQzCOD1o()x0HrpqJV_8X)2CbOIpUQ@thih+G3Jb1f365v_TB4T76Qa`fzk$cmWSI zip2$hh&eHE5VNUi2Zar&n)vi_WO+lqb+@bovByF7vp1bIc8Q@$ec~G`RrObDH>=eo zO;T&o4I{mcA&@Lc4GuxmW3|MZw~0ykC@_e}^heT;dJ2x+>cdv)SV@luKym{@4Z{ws zZmiL5^Oe-Jbw7I-oO2+E2&2V)Y?6Yj(+De+GldSnj_e@FFYDtq^1<>>ktoEZzhV_Bk&EFwmo&B$G<1h7%c3;=3k$C#J27>*xE%zt3& zCpjEIsO%2vQqQz^iIXy6b&CS3+ci`eluP|DE3 zhL3Vd>{Y+>d&mm#H?rU@4sQ+2IJwj%w(i_t>l;1Ou^_WY{w%ePd0Neh!NFs~R^n8n z3L>Q_iIm?*O2eete&LY5EMIQE=!qx1ps?x(smg9Ll?$YPv?ck#b1RgPVR1U_E*Y~o2fCO%9hi16?RnNO zPT-|vpU6W``^opGdEynEuc!1`wm!RjdH&scK1pZXz>ShwkfT;Qo1@e*P_IKZ#luwq zx`hjXHKt@rF{HGotO)RQ4F6N#v&^Bo3NPPG?aRKvMx;6=!`{V3gXQgXeO-d9dyx`C*n13q^R&^fyx^LjH3i#YH>a(_E& zpR52hW1`|0QcVi7SqaeP!hxqUa-Rb3)&JPPtlB?Ah*)xwnyT+Bx!9{E)g`eABEN=i zb+6a56URzn6^9*8+C!3@gzfIKxAV^(d3heWEwOH9`FnjQ8B~(~bq;Vkyak!EC^PPl zpDu8ool26{(p#qYufl`WkNH|0rPqeB5RXTHA|RFrJbCb{!sV+oVpiog$G3%$rNt*3 z?vkoEUYVuq3v3w8-n>BO%ZZyNMQ4#CmH|FHir&Oyaq*)iB|o%Hh~IMJg?Exp;Eau4 zatYJcr>??*tCnd+3z1!?q&m}7Kc-Mt z;UPazQz~EdVk&CjUB0kgL&zkdDeHO1=pXD%bDL0tiM~D;jN9JOLEp+%u{%i+YZPd~ z608zjBZIOTdlEX%cHsuYhTa`I!$vg-rN3oC9^LRs(o11SAKDL$F)@TUq%Xa+Hf%od6=BH3vM|W{o%rp%y)n8Nu>D^zq@6IYg^)#0YI5ELDujLU zo=!uJly1`aEQwp2O_e`CsG?2@x%PItX6g24-c44M=#?%CYH*I#(lx;4?LD&-d!ct? za94nck5eh`(PtmV()m$UqvPX;wVo%|xzdt!`2WloIjrh%DrSX_7}|*#o+v?gMoPH$ z0fmuLSVFTzpB7KwlaKJCWt;F*T@RMcL_B5|H%8ao}(;Vxma5Q-rfor-|fpwI~trZlef1+HGp?ugufkr!F+vS3jjCS z3dj^{+g53R`s&lCY$Oh$eH-W;jNJIK5MTlO>By@bs#Dq+*&%c@>kl`HNKkU3!%F-7 zBf9{RlOnX|a9r#hr})-9=tsU~ur2C+~VpGxh4jppa9}Q4} z^k+xXdlH+bR3RN~i^ni0e2>mSY~P8Cv{)h*Sbs_#-N%kmpZ~#-`5$7uSp>V*A3{Cu zarSkoy-ayD{^(QdA|Nb_TW#&!J4GRgmAO4nVcx;OK#t?=Ks_{|w2V?XlAE*qbVaRg zUkQnBYVLGZdHKzX(z!*Bz@Yo-`ck+{(rpBfIKxKj zeAk*0oi91V6@&opt_5(FOGpdhvE0keLz&_>&-Un@M0^5Q01`8HV?+TJ1V~mJ(SQry zjE;Rnzz>W`{lWVx5RUS2ATLvF?SxP0j}eezNVGg*y5utVG2W8HLgSpgH)BFMEQReE zj#IY$=O^DEfL3|+wdkb>X#?d|y5 z>aBTDn*ZgRC2Wlt%ui>z%rPEI)C6cOkQxRnMexGYPgdxyhuIf#_9TD~D#oe4Nk>8jOtE)fY zi!R)1@qlyXVSSaau2rqdl$>Qhd)Rgr-PGqdX)!QICL&!lGYQl9u~M>^ zqvQZYy-{YKvOXGi_p-7R1wcM!;D^-;Rp|(mKnm!LC4Lu&+I( zZR20q4>=KYwV(*!@UHB@z_zGnl9A%n(2XuvA5q`KtB(h7aR!4sr)shcooPODmW`}J zYMv#ERlCh=tB)7s4lg>-pIhE1+lrkeZoOUB1|D+=+z&|TGBxW}QtVo1z&i;4!N8xf z^Rvp4efH6X5bX%tP+@~)f%LJFCIwFW$AfkB6K=l`->L6URDndssX!jkE7k>Ygc3@qeYr;U6I3e@3oM9Rly-W>Q!Q zX-P-Al2>a3uIB&)2J@F6BV5C``Y2+1qy8S!WK9R1EIJgbJpIO=u6IDuDsSRUI20q< zs6NW}vh%O(+y>lGQ?WvLrJ9%@b?Kz6dmfQ>an^aolSuXas6w{z)fZ*%A|ySb<@u&a zay&Bky*GGqgOH-N__&bv@2JQY!)Ylt>A89~9=R8B9k-F<5Y+DKw2&AR-Ss3acA0Tr zTkad^8RZn`*EjmojafI5j76xvZZ9gX&lKNQ`aZwSf7|EIj{GpCSY~6ID$iOb=1O_! z0G~*id0S#C}(5Pp3?1V%#f7_6f^Fx*L=>|OYBq(7pS5)F0(Eoj?7BdZp}x5 zM*8>%R}1ytp2@PkOS8UU9k}{dmIJU#lZkkRX-A;&IJJXh;+OWE#>?y+Qzr*dwL z=$;>_*4I6p09gs|K4uA3fXYNs{Kmj+a0vQP&XcJqG~V3z<1a{ptWEs4>V9Oy`pSV` z5o$Xo(HE<=1as(CC=>wiicE1E+0ps$Yhhas@9$QY%bgdF2UAB%*!9Xxu??JyeU=J* z!^qv;kaP)3sZ}vDXd2}$lR9U2|J3SX0UA*Nu*78aUFbuXARPorhJ?w6~Zs^Ph9l}j<@3*?f0Kv2#i^_^D(p09NDuGJyOnxiS%aSST zxp8#Wx!mOwny2jz`RAhsGry^_HsLyg+oI4d(0UW=yz0H}XNAx{yQ%TYNO;%Zmhp}w zsWYGqkE$Fd8MEZOMyf$Nb*g=?UHz%!#BJq^-pKBb26Qmvb&&3{2J60ygQLLsQWtbM z&C(25I9K_5&{tS~JsBFx6hg){8dNDZ^v^iuD%57yx?-L>Fp3UHb`m;ZXgPq_mUD8P zJtfMSwV2S2l8Qm=d)$&953hd}kNO7#A9PDcMl|MsIe8lMK3IsrHmU`#2c(2gF zEr75a5>LfX_zcNTZH;G6^?E;v%V4g659e%(>Xo((c?ymdiG8_m-lyT1J3jq>q(Ic% zqpc)tTWbHPc_Fbkc{BAOTG93{&#mbM*HB%5{<>{^R)e?p`n}9L&Uo(P*7X-?qC~oY6U!|Zw~M>!lVM=YIN(nCPVY{ssZi0elE&F4EvZC{E94cH^;cG!I`<@>)dx@SETtshIpux4q{*P4ykRVA$BHAgz=|3~IDu(m=dJH7 zG(^^UdAHTDdpJR!>JLQ4vsS4!p{{$-HuhdIi#}{7F=)ty$Ywjx)EKUm*L+AD)9Y_i zq&JYOVSy@=3F5tC-XF#Q?UU{aBpRf8>%3|kth_eFQ2}Prw*iRXhx7zSIwp;IcABZ z4kKbBatdgP_0l~b(j;~ za#Naeb$QC5B{&B=W?ILzzKGaPrsGIl@-gaM+%~28 z4R*;H@t=Ew`)nz`6|Dn^GA$%~gis$E3p@)2jtC?0vHMoS<6zVBs2@Q0gW;V~Y#b-j z(qf|DKQN>m4bu&5LXF~IRsL@DpF7E__nVwd1v4<=MzK^u5FG$);L=0T8{^`0#iWdn zkK|Df)J^pq6|TylsTgWyaQ|eH<=o|@A-lG~H)xtPz}1>91My&_+Ljm|c#|nlJ=n*3 zvdj2Qk5J@G|2VIQW4;Fx5}A+2`zdn4bCOCWNci>dhMncyl|US#$AtSmf-jt}Pie*= z$Mg#*zcv1WM@5GTpZ>LXU+O*jx@YPh8m!zh>jf`9pHl%6(_*(99sBGx$;4Qx8u;EB z^Ay%b#Du?R)h27@`Lrz(sF^^q&{6o~014gXS;k~^Wz(I zkiN^MLJ|b3@Z=XZn zy^43mn%TSTCerNbGHM?nX)&U6Ut>5QTZeQGZ18Lm_ln%4q1h=mydIPRDox7RVYY*s zB>LaEgDO&N&r9|G2tS-D3%>f5(9YIZdz$rCaG8t?;1DPE*}dZYTF@O^oPhb1Opn{P zSh(9&sGZ!H;OpJFah0?4Tvy4gvOZ6bMeXg&3Ubbb0Ile_#1~n$xheDjnDKe-$BTA6 z-O(~qb!fUHawS0fmxI0aFy<*nUg$@*5N2FoQ5Fwg02^ z@BApjt(SaqA|-Sz5>B?qFM!ypTndjDRWvAa`+4gr7BZ7ZQ?V|SH-KeU?+2}8X-TcD zVEM2a|4rT%?18v<;BiJ%(`2 z?o2F*GI&eA%^1Y|fD?e&Z=}1DE0Pc_)X<6CF*i3~u#683LnOW4Y#P#jtmrZtG+l;LLIdUYxi}*lHW5@7%4nmL_OFOAI*~1G@r#gqS}EpTQY8N!!MS{B ziD{}Mn4ZXcGX#=&mQ%#=gX@y5Pi#@9|0~VtW~e<7IUBpi6^Lh>=|hXR+8bXeH}jy! zT|tbLt%v4^-#TyneFwouyAX@!@?K z(JDU7m7#3ltnN*74XIGfNbSOIZ}`i>050(s^2gWy?WvRbVQ%Z#A{#dyJT-NK#N7O23BzUG5t^$9<39vNzv}kRBHmT&p z702rc4!O^mLDe3G4f+nMem~hx@`8iASmjO%|&za$SmosAQY9+oIoyVlJ`9q$te?TJE&Ex20XHt5}=*fycA4Q4Ke+`PmFgXqN~ z^*hVVfbjDVCb^g~sVoOsgYK;~pJI?UfiYN%VXwV*VG?H#+x}c0zTe5}K9xA~r_zy< zU~ZvCLDY2<#pGM|=hHy#ke*)!x-rskVs|j#*NY7^grnesUo&hK<)B)v{REjOo$-SLCSmfqSQ6WiK%Xfaet@%ZqQXT7!Z z`J&qWw7rsvY;Eohu;}{uiGK_X3`~J%;JN$8OCAt&B6BL#HZQon%i2CzE}w`WJRc`C zsE8YCAHyzE(Va;=;mkE*;zSJ5w@NV8msMBv+s+D*V*kqRb;$8sTSG6atOpz_JyV_c zT<1=u6g63x+#_}reB4ri^Q`B_v;0JzN^2i-#j41R66Ueq)B63OxAVO@>DLd3nqsq1eeTWfP9IzO>D9s+~*ef;<4&%Z;y%Be}Bg*1k0Jx1=tR z05bHsz7L*V4ZTc!YG%=-y~%W)eTdt4J#^^45tCW1 zsl1n0=a}Po=9$~^5}g2KaNjz5Rg21PBiN@qymaIqSDV6M+d+D;n zK3(gs5RPB_25(ZN-&;{fl)^dTp&Y?085Zt}&W|tWRbh}6V-UBZGSh&bSIG6@l_ysU z@TuX5&5As0)8EHHqmCe6$Nl9QQNQ2{c)w{LZny*v{qSeqcU>OmwDSUI*L~KGcmLoN7_j`|za@`{?EZBG(aFTq zclLD>=|G2P>WHMjU5x&`Jz%?Ex{?NVuI6aldu@v63+wDhw z1I8RGRCggiVyQ15DK-$-u_nC!h{E%-%RR^{r#3bfkJ2~Wtp*YTsexOIp|PW{WjRWm zp2CZKxcd$(T}0y3w(3G(@yeE^r8I$u>_x_UYiRgA<52F$rh5aU zcx;Tdu$O|e%ceGfITKJ|6YERrHbeUh4&F<(EU|6f&Vg{}T9_DUNm>?G51>4(XxI+L zPP~{BA0m$8-k@swyP*6l>x{f?$+A1H5gj^k7OY)4^Po6|8k%}a9M|Gy_Ef>nGAvW* z`KtWFD>b!MJ$8JrElX1ufs^vJEA%mW;1juFfL8q;Ws7b$YqRA5F@=Q7nT>fl!h{Pa z5%I|F&3W>*HQd(g0@4vn*#KGMAGKYLFne&Zk^&#W_U>Uc`)MH8?)|K zK?Rqq_}?L8oJY@+Wf^S2%il<*Zf=@Cz2~sl4*Dwg1iIY66yv|5cRqfx;t`mciI#@ndNyzLCrhijv7JyGThQ>e{op<}M{(%m zY{_)+d!veEr%u|2TLlg1SZZkfn-TKYgh&%j6Q9s|f3R`=6veCs8BPgU^Lh$$Rqt}0 zxO&+1wQjHOc`=^LM{M{Xrl)Ljo0tz2)HM9l@FRm89&{(kCMch;)ThH_u{XHALh7S2){4yr>LDF zo3Ms!uDe6=;&VGN`4xL@{zE(dGM&cO)51=RlB1ENW3|Us&#ZxRsH{THK|A|mrcLHv z3xot*nnTh3sIyXdT2LEBIp?mSx3o5!EBn0~R&dU0_v7yvt$qo>(Az>h;;bQT=LRoX z`a5Q%9)5RI{U#2IC$$=pvoQ#Jv=K$>xemtp7vC0<+-|vtY%(lQ1_|72I@O?ewpPa` zpTXIU8nn~&lif-a~z#KO>!4mX!?82f*bPHJ^?cyrb%p}r9 z+BtX=#@UBkvR{p%+h=VpqU_$5(IA7a>_G57)qi-J>r$6Q2}_o*nhM1pTCW4;Ajh48 z*uOOMY7}a0t{L%+o4Kd49bTQb9k5w>9ICEf%_36Wx@$v;hT`k8dpXDqNI>~RYX97* z;5bo5me&;9h>pw|J#(>&UdU_GqT&JP#62CRc;#j(C#%iCikjumN~2_jb$e1DBD<{5 z?cvUr>z{j@hg=tc;(xI7@7Cf~zSojNPnLS4-i3=tkEkRtm@hT?Z_ZOKGc7;AJ3pU< zPLgvf-!!pvkfvo%4cwfkea*!O+f0VTicRE(eh~D(c%=UwQNjZA3Tm5#?9xV`#vkR4 z^vplT&oZY->2;_@(l1->@O!U569X;kYk36swOvjx5UC}FmaNq3W`yeXf&TPm1lS5` zNCgcEoP}=9dv1u&O(bz~S*r0;Jr?9s7D$Yui2pO@tn+J3WvAthiE-1LT;0*-n4QnU zEw1Q1plOI$Dikipo|CDuf1tm#pvkub{8LnTAX_1i=DpcESIDvv6znsIF z9R?LEY6xPwvCX%B?D9%c=qfc^AwsHmp!i_RT`|yu$orm3Jzt+kzaI~uZSR)_G#;_> ztNj>c)+C9H)> z-u?k%@T26ce>2nlsXsr4&uHX?KQ+(fU+!gFq zPkM(xu{Lv5#14=hG>z}VxcF;d$Sy!%bZ^q@Nq&yObVY&*))anU~yOd^@V9 z8+z;wb!I39I&P`a#SItbSECt-ArkC%z$rMr_E(x=;T3=yu~pI- zS!+*ZdiE^E2XiqT^?AL5$HLh{^5|K78F?&fmInMZeWOeeip?XwrA>Yxv~0gN{;1En zHY%@bp7d&@A@bD6;(y)$bHw@9BzLfc=ktYxKab!cCUFAB8*cr@-h6}PJ7)&!0#)L# zouFuH2qF%}es9 zPE1W`(JqJ!XoV?85q%vojbX2|O;UMi5|Mm&>XYvWgqKfRBcUn9`us9qTtOd&LkycH zWVhaoG(UY*;+|nw=$2Y2=weguX4CcV7uNiNcs^OvUnT+OqUCBvSwp;G2SP@i7J4QV zdi-svA2wjfIkuO<`ecKX4UxhI4rKSKPxE$zPO^0#Q-6j(Rg_Z(r;-IiO6EI{eRTp+ zL`T$w{ORR#w`X$e>Mb{Wp^&dZ>ST{+8*2n~7B{Te@qAM|H$yK%zgiq-AE;Y;Mc+M; z9O}R*x*U)mjGj|{UvW=3UI z68n}w0iZ}?!o*UFBJJ`P6V*TF!B1a?;Xue0-Aiu{R|CFXFBu2MD;!n0gOpx)tFCaP z7-#_U=W@pZarfvh8XtA`VIIaIP~`U7F1K^JP547flU>ihaH? zVbi;h%)XP1%p$83TgT48EHrY4guilJl9)FlM`E*qaOuxQujCK`15!&l2jU>Go+Rtg4>&O-HdYqFE6jn zZM_d<2Mc?5A>hIaPc5}>yrhcPQ9auAfa=`YM~nGYA`h>tWKeg#rp=x*U|~}NXR?f3 z8nV7R_C$RP8#SkDZdCu&tPYRo@Z|o-8NBh_EP)>!ikM_Vws^llSmw%FjyYWhk?(Oa z5TEBaK0mio=G?u;S@!!_hHKutg<3{MkMZvr_iS`i9-mDPIg&@ZU+7T&#oc$p$pAmY zd{YfHOH>%2N*xo{T_OAl)rOvj&6_gTu_@uzhisv?#qe6?k{5({ThfuHd_c=XDr61ca)*^^K-iWl)YLz-2g(>%wluUsis zHFlrdmq$!XjJmLbFIDA|`(CbGr61n{o5ke=rl;9Fcd6S`+2#yV-#mVLBACEhL+hI> zU89R68Xqn$9#p2kHh-zOWgr)GPE??PHQ!@1@G7c9+qDeVNzc2K%MS<(s>LJ;A0$RP zQ2$ysVR5~x_-LUtc@o0G8UC{vPh}K+z$yET=lwyp@VIrMECsalUk%_pf!%8U=2%m!5V5W>G9SHSE~zacOPGq317?vO_^kn(%ih%7ocx1wFi%eir$`r}}oQ6jRjo zEzkB`FK>74^`Y^zWyOYm^d}GB7`Po9n~h1bTpH zLt1F8OKdO)*hdbn#FKJp=}gjKz&O<>K8-Gx^u zXWuF9%#MSb1j;*nH}rB$;gk9dWSyVA%f^kH(Z2QH{7f|caHtqp4i7)P_Tu^wht&-Q zok94{a`1LvBQP^QveJ84%m?AEnKxTrD?EKmW^(d1L5X33``Z##iw+i5$xnoeV+K zTetG*&L^NZg}h8AG>#YB(*cpqGz@`#xzRL=Df!3^9SWz7Dkb!FkZ}Y%h**aXk{^#% z@UJAA2&uuI&ibsOW!S*09pjNU2>LhXj396_Ya_BO5uyQ96DdCb9V#HVM=uNdt1&-` z3mYN4UpQhy|Hj{F9nVydHUpQYr%CyChv0ceHlc+G{HFs-CmJDLW}%Zja65!Wudh20 zDIQ;6o?SQcIYMjzLSm-xAMj-6yk}J|O}u-!Q!A@&GpF)dfa>u)zUhp)$r-y_kL@X{ z%D=OmKc~Z1gJ%8#bt3d{A6NQZJgm7kG3MJ_{cCRKh(saxN#&k|e!e=TplQ4BTTzG- z^55H&B77_SE|$@ZSFO;i)^wVPgmwOl?aAKPH+*Bj%LExsWv4(@JC&AX*x z-C3*@4Fu#GHk$gP2x=@cA=9@{tjet+{>lrq+S%!DJoMxH1>w|OsDJKtf3LlqvT5;K zeV_C32ARl+D;j&kz>ddxWqFj^zCjk!3NJRp{9msraSsJoY5X5sZynZj+{J$zC?x_? zD#93Df`EW@3rGn_Nw-KzGhmx^cS}l2OE)vf5mUOSuo0tV^kDFe`@Vm_>$$Gy`D^3i zkBu+qbI$pkc)hQRj2Y#6jNGqf3>Pr*Htbqq`8bt}%^YCBN^J6vKM1hTD}SXawvq(4|7VTvqyR^CSGnNiluN~ zHuJ>6tz3YQ?noWWoV_bfVbw@@SRfHx1lW*@@}j32q(FcO1c1&sSNWcjM28^YgG0e1 zH3g31qd62o_qjvpIJwxv;LnM{>V4fhz;L1%{6J3!^Uc^qJYNriY4J0?0Yn|9U$40C zG`1id)T=|A3j*{r+JKVpdpmRz7>?$_Zcbx&gJDZo@zbHdi5%2tcyvpPHjDz_6g1g-J z4WQpntf$oJ;?;l%PI%W7c2R$K3Np_*4IkJ1wSU^^=MHyu{f3WhUVg(|lUiIcTr!cm zg-iDVfb1`NwerM-+%eF3b}tuge>*LoEV1q1nM;ytZ_l7c;vxhRbfwn(2wT(}*OH4h z=<2t-)@sgN1}*@gvHnsE$XcMn(T5m%@!zhPXmHy@Fc^nylA{7_vu zk~#S-h(-eNlh#u)pL%ETpI^^G-f(JyNh^m~2u`RjB@pL59j3tl&-H^k<7ZbbPItSM z=IrCdu@b2LC=2BT2)X%G*m0@L9kN4;(Q=-GbkEnRoK)={-nN~Hh@IfGOTM%+KTb{z zvIAk)S)5PJGDR2vkNaRN82J>$?CeRnciENf3|QC9-y`hU3pa{4kX36mt~Fminm+S- zxf0?xCK=LgF7&C~@=kR?b&M62136jxkd<*5LTl`thisL7y4;i=P1qz*Ob~B;$f?YK zNkvIS8cX-A|LGzBlcxetZD_cK$v*cL`NhIcA0+(vQJH#!?6>VrM(0BQt_RO}g1rOe zYD~OM2k}_JD^Io}6VD{M{dHBLuYZ)6hc)V+S(1kxl74o-S(ET&r$ntkvr?r}r2|`K z3y07jz;la>s)hOCJSz0l)o&F8oNmMPAw{SVq!%pU-`J~HrfO^hAv~Gm^6ZtvM1?7; zixR^~4BED*0VJ>-&`a{`kblZiJA_Q|E0{D2uj>lT~qpn6^n*~#ydn5V1#duw;m zhN|rX;2-{h$n)iy`5#%&mP@rF2J0lE>Ffdb_PLWUHg01d8FBXi<8-!D3k04vd8jdg z_LlC|bm*AAr#y=JV8xTuF*DG_A;J6fXcaf&SAQ#B6C0$gBUZh7_il1@ns9%h3B5_- z2)wFisEKIvFejejpk`eI#-))q43@wHuW-3NpD3t$GbDw?%LO63j%=q*t6Q!sg9}c6B8^| z&s;Mrs=5tk!la*HR&yOOI176zi}l8<_}C`s$h~@!_fRkk@sMUPu;1pE;V={~NP$LF z^j6FHI{kEA8$lKDEBw-c!aTlWAm~`@sH2#8rUrq#JQC6GWc6f)Beyl~%ZZbnGo5S1c3fQVCatqaZTyK(2uTl3NHfk9z*0rHQUM~;&LAg`vkNhMA5Xf0#d0zwM^`nHW4;fl9}I+5g=h(P3;PwbNF7Jd)}-e4dMDHz!IW$W_qS5%!#CTJn`V;;yu2oZLg=iq=|B zZMXl*)=??Z*J7Qm0pT2OH(GSV63M||`;Fi8YTUwAke_M%(LhtEuhW&jS#rMJ`|egg zFIzZ=;pK#i0~8is@8~a!e$Z4JNVE90l{pQ93-0Qi;j8CrvJaX+?qzp*4{K^{YRWz= z`sgmwQo4RgbWikVKmu;$_a+Z^#JF2Ic#HIiTO4?KSkTtJF%g{}j~f%ki%hIxz6X7V zRsw8aGAr3QDA|zhq+{FVdQjAiq&DRce64^m1Xo{7$zvJR-=E?>(MEKbPigbs2OE5DL8OwDd55_Ps7VzR}>q-XRZ5h*y+E@sch`oIkdIcGhF@Qb7`bn zzZQeemK;7Ie(HCz0uL3+D$MFhjU-YVF;16*4QF1eXro%Kgm6O{WnG;EStAij{4V%{ zKM}jS5rd&!C|#z21fz>}9rX7~4+g!7gBADrgQBE5xGJR+VO^mxGkB=3Cl8WanC+oZ zC|3cncdY3TA+Q*JManic)H!siPBZmlL^23F2(oE&B56(wgd$bJrhYrr0QTz2h*-8N zP|)XE5p}*yPmR8}MSacX;;o)uwoIC_ed2_G&W~ z*CVtcbx;VY>jrb_I1!%D(5Xi^gp~rJ^Lj2#E@N&YRXMs|VR-`(RdcY8Xoc`~YptGr zb^TQryMk4?Ed2{P)4$q9yfhQjoB?{potH9riXlEt|E)c79C7xWapOBTW3Z9=?$cb? zP?K`|QD**M6QM$q-`c@w3~`0CQksgtjn$o9FTFu^;uHM1AYPA6lcw;3yI+?Q3>TqE zlRPtTzElIe`t`SDt5LFkf3@VZ){_I^YBWS8!2#Fmi<}202o3;P4l|&;mKj&< z@<91=;7G;2FohHJeU_$D1l)Lg`F z=iub(66wxm1i7B?9OZ)7fUAenbeWQX9?s8w=sbUcVB%S`O@>EW20GhFOkK;pe*F ztuqw2#jRT}fB*L{LAQp72#=r2fqS9qFG;xoE1Azir(QS@r3IuOeVi*l4`@fc!jvZQ%n`AXxP1 zQPr_i_vSe%(ag+&!mmuAXJWc>bw92!yWam_ch4tz!+aTzF&nEw!9l4KW<4YI);dPS zzrcPS!Bi`Qg0Yhc4~9p7WNeF|%auWzTY^N1kPP7xw+s3z#}Y-73l^{ts>-*xdf;TU zsqUE3hS*gVwC}Pipq$yUy)@_2ffSwN*IwG;I%hQ!(XsOq_3}nSj~Q>{a|N#E*})RZ zlakpSij_=&B!^Ae}%fPND-&Wa9cu+6(+5UQHi@Y~zY?3ZoZw2+ly;E;iQBPK1qMA$ zl#+WoTOP3H_qC~!`01jpq>F*qcZC5Q+Uoohe&#=s+}8#4HV+|+#kqo4)Jh_5FfZ(f zG~Kk(sv;uGu9eiF??;P;Ue7I2ObW7$?fZS3XLL}MM$yGJjgsNTifgcqCv!22;7g4p z5Wjr?(vS-MP1$vStw`b91bY0lfN<8U0jyPe^E$6F3;LlW?bm#(*ycA^BUT;qmaQM* z)JRzYvTNY&`z6t5t_O#s%Ww}Ewk)V!e1}PsuJN!6 zkAdX)4*8L$4XWH6)?f!=>&eI6r$`k)T3XmKh~KY2G>iP1a5D?u7{s8t<%JvBJn1Y% zD8Z>DUNnz~xDNbIn^a%go>A4W#oLYZRx|p2#ej0K$$J4+M?sGD!(@?(Z!nC`!z~(ui~I+zyfc=sjK9EI={q&a8nfDqz`bOZii2yHt^?^+JQlRwv&;)`ci zpw75R!Y$Qg#3@*M1V6L7y!wOJ`FCpA&z7_1$^)V#TZvucYOCasgZdjQG>rRgv2>sW zSJ(<&-8(nEE~4m$cM5jB1#1f9GTRSdkmAu_?O*V2&&wqSE*=rl3x`lT_M{$Z*|m$7 z_t-BDmGc5ZaMW_c+5&wxc1!^&r!SQ-mjQ6>&ji@dvCxGwYUh{OSRG%o)Skwt-w z)-xjGH&qaF3sgb4&GPlR4Hf?ZE)XT-RH()G(bE+IAreZ0JznM?8lW6zd|8f8OX~a9 zb~2tf1nT@S3$)a{mB_D5qh#+IKPcq3gu#02*l_W#zZCJ_C4<0Jg2Fe7 zcOZuH)+-Lr(9+zGMz>VfzMNjOm0vo1h^J;?3soVPub zmlPWNFt7VjKLePfRz~%FX@A({Z+PTkaGPgzI!eGTm28F*2u~jBZ?&qsP~uW6A2r(r zwtb0OD5ERSsv}?79X3;aIfoink2zJ>`H1fI^ar{Z+G+Mz;z|;JE~`A5d|XNxRM+Ng z!yYFL?nGbs9+TrmeA9m0Jn@1u@4b_+t^tcr0P>+wx~xu8e-jzHBW6O7(IiI9W;|==s3T znELR!E~^*V=8z75{nj+(uQ4HsqSvJak)}&(-D;h4WfoXI<~0O9sj|>F*tl zeIkhClYSBS$8cj4{-He0`0%q{y0weL$|t!pfh{Vbd&%GO7HjU&EK3I$+e}Rm8>Rg9 zptfB@^7-*yGl#j&ps0t{w%af)A8b7ByK!ruIlLAkJ&G5DyEZ`AL<*cX=QK~u_ZXUA zXR+=+I0SBK^z7dWXzt&>b@06Sp0hUC*%WZ34x>b6P~`m}rEp zpCPXbis!llUj+e=r`mqKx^a-ImZTKa4ZS=la1V=arb_F)7SDO%5ml9b%5JIbFpxu9 zykizbkc;R8ldW9yFKT^#RC0*&1&w|T`UeB z7^espgYJtr`K_)0j34>E&LC)!iv+Fbc}=7EhXUh*r4}4fFZwtPYB`wGAVw&Bhp&E*|N1CNdivA#QE(%9{&}gds|(Y_qk&au z&0~Y-B{@t|+=j|P4}Yt!^AGLyobRsLY%K@w0U?8^sc9Tcz;b#W2Ltu}~i?hCO zU&KchzOi>!1u0r|cvqODHe#aP8R*d-t-^MdiToYxgR4>whCTn6BF%M!YwFH5!+LS> z3&r5I89gDkk=d0I=<)dV(|I@TP(DQ*GQUZ)CcJ@CBPoM&xNftHOeH6T3SsKFII326m!c2LTA!07m@X34FQI-TAEGM6wZk z-1n_WP|JC@`(*jfz?$P&uG8A4z~)lTJ<5VcRzQ7}pl5Z@%sZ7H?*E;O|9j;BASqWnRd*YQSiqD4A=B9GbEu63C`Yc1vz)A%<7iI#K( zvcd@xdSy^1%0E>8$BjeQBCGLdQpIJDGP$`owc7ueKmU4`A+C<*tC#oYlJQPHccPAG z0YSEbb1&}udMgql<;T|;34r)#TP)V}#=g?LyO3bUGz-}0V)m<+UWBwIW~3qsr5 zbvB<4fnHgwC+tBGfy*LC5#t{M&PM4R(Xr=yFc?18)sYGE_ec0ywus}WJ%(virl6&R zLC?b|YrbKJ0)*{W_ES-{g?$8L@@BAH?6lpo%d$P5h8_B3NaIMy znZHFpGYFO3$Kd$ zFPPfqBJpZpPWIdux}7+B`3rzk+UdNe&N|C}(=L#ep)r4JDsd~^>X&cN$t@TY*M7RJ zP3X*U39h*gyklHY5tjfB#=Ri<{pYPuvBgeERbv`3=K=grqssT1*5!Fwwy&dzT0dw| z%T5RtBA+8KlfbEV9k7tqgHGRw1dr*;%N)5OYw4mttL8yEs@=m?6pC4 zFa)JEvIC~YGFJnFo-}7Xyr8>?=I57$UnAx= zW#}LDifES91)=#CGvU~#MPPH(<@c{$Rv5IJrtvZub8{S!>(G>&#xKc{lXm# zkG?D~p2MzAuJZ9SS2)X@91*ZIs*NIgJ$`LwBOBE678br6%_Ivt>G!s z)6zdf-sd-24f?}kcx&njp_0^Q>eWm95$tlt5pepA__6w!XNwxvIks)#*=E<-sJokM z$P-m&>C4kqZJ=lf`4A3_JORglRdK_YUmX^FDvI~UGeTG0oA%5#NM|BcYr=+Y`Ky%H zt_U@T^(e<= zfw^{eU+rDtVI0Zt#zlg&<$`1d7|8O&X_L4ZdDYA_J|@v{IXlSf06 zmbUUADgJ7%#+pYSs(fP&n@S&zYVF=}-Fa*Wu)CHEand}^dE7K{pNpWKnBpoY9*mS*~2ST!9}X;sw(~xQ+N4(YbKd|M$dv) z`@Yywyt7JLIb}+DfA>TamyZ``SkHtn!I3)WEM7KSo;Uk;M7S*OolI^v=kon8pAoZS z*6_R#HTN-|XbPVlR6^wK+V{HFL~sM91!8|MZRLKZ$P4@K#W zKh?HNjYd*w5;7*}`Fj>P0}xKkVa!SJe1nVuH)KTlkA1ujje$-YUhD#>|`VCsP)n? zr;!?6Pi01YV(M-Dy6bvakWeiq`saSR<}McU1axb>Uf|Rjx>F~L#l*V@*;eT&ODNjY z0b#MySMdY`dBI7QCjDBVyAfllS@kXMAxo(KP4;i zOer)AHxf-2V|pJeQqAGw(TrxR3?CHb{IEnUi-B2tfA_^3DMBNAP?K%>K%Jo9J{44&3zow z=2srW&$k}S=Y+_3>)>}v*V4d^d;S{1gr13D#LPYQ<-YFrl~n22`+Gqtly?2&DYNR zqY7~ z(tHr!eX(ZyZ0HI2RkxdB+A&erVH{DsrcAkOt3ve*I&_Ps0L=k0PMtX{IK@x5Ks3&) zF^uOj6EZ=`3(W~rqR_v<#DHEOwdy8(0*m9{%nn<8r2N&j`w>;d5gfYTf;*67GW+c8 zdi>l==kJR-OPmI@!qw`lBr>sUaOfipXLR+xk;MpcwPJY@yY^N-Phno$;;A%h)aQDV z^%LK{OXp-h@Y#Jf$`A|sHo&B-+NRddhe@!y%wX*EsT0W}cgbDhHMC=<$wzd5;@k*nXBGVdp*qsBk8XRL9WQ;< z*3!0;+64{_pLG*3GET9bh-73)l*PR><)~+e`R}g;y`EqHdCP%%i_C8Dox1FvaAwo0 zn|&SmG9lZm74aOQH;I(D{S6-%CDW??Z2STJM@3h0QbPT~){Lw%^OnF&=Uq|;a_dbG zB^HX>hul90*@=KN6lQ9#Jz4+ZZX{j2V53Lt$R>wWr{fU=o`ZOUsPUK9sTqGF2zGeq z#Z6K|_`jTZ3U(S6)G#@-&DM~J`)A+)(xWHK>QYyo0}pr_<)d`w+HxNz-h8@8se2>8 z+-*|h7SK~^#X>c>==n*EHCCp^2yTa=Q{c(t&X-x_i%(N(pQ0{(14*9?4DsEWiG1*F zm&?&Goka%k2BQ z@qc{c%%u2k>&=Ty-mLO-g}X*OFEPl?@k0D?k435m=L8NF9mY5}$ck zd>!?WB)+y%;E&{$OjZ%V7D8Q z;s+XcYv+=kFt3BegY2=}23Q-p-Uj0UY?6?Q?-3(Q{oxzKquN?DU$Ctbi5nmBLB+qJ7R{iMl4Ke8QSbT+ka`L_Qpsxv?f$|5c!0$y^&&z)v+ zxt1cn+(r(;UeJmiD84x#+runZ(Alp$n&JI_3(7N36}Q`(BfsuOx#M~ST1KIT%lap~ z2JUcs*OKMj2(pSmD&@;BrcTamXDLwI?6^$9v*EjeNCD!_I*dHyEUhRaS$+I~pDVUn zNAdJrTW%XVqdG}OMahB3@4o$*cQAHjg)iU+-a7TfErB@v*EjcL(eLBv&gL2& z8FhCf{t)iUxrrr{xhu($30BJjpF!?SD?la%i|gw$%}1*6w5N8QeW8?2VWt-(oe>X? zWsnClqey{L^mR5fy=)szYyV03So~@Y=>pTu;^x|?yvReInIY$+Y$FH)x)cFL2OX4418eL&5Ma3WSm{!ly+|&4|KolaG zhBTG|YF=I^?xW3pllCgrtGED)eP%_b&5*CkYN)xMM9;nK-SmjAF9XI|PBbSCFDz8X zPrzy&^A>nekM99<>Ek4(O@=h%TB#BZQp$>Yvdz$16;6^TWi?dn^F!c#9ny83ZF0*~ zMK*(JbSt7Ya7r%L(T5#Ma>K`ZOg|rbvUh2&0nm+`yubeiOf50*XbM0AzaKra9T8~? z_B?~8livcrhN?<$>|um5yr@$8AI9H-?!C14;7IZfR5b?Of4Ai$V4)`m+_2rb1YG`v zzOzrD`%5d4Q77PH!SOP>MbYrEcR>NIpp(S6?gyUnBcO1X)Wla`GGfb}HIK5WJppPXC3uzC>e%kF^y!~FIV4>EbCVIA!_7B6E=U`yac*;bNtk}684|BV=T zV9KH+QSp{EF{l(az)Np(_GzZeflp%goL82x@|cyTdQ@y-&gVX#69=S`L9U3E7d&_~ z?Z2Wgp@5po3)*PKg$WN}08InBL*IGEr?J&N$q`8t3Ybfpf>UK@F0WLap*Y+pD?p2h z*$`sS_Nu#z(e;7nOcwUs9Y5{f+@X-=VAgpzdwXt8@hbH;M&86_ENghN%6y0+th&Rf zXF0$u8)bQ!W&nw+$K&p@zL6Rz!m*s`EJU!h2HTsFzZsb_pnb}MMDa1YEwDzY^jH0v znl{)hhp&q~;&k0@jO#q?3msb^{k1KLh~Xb{Aj+<`Fb~a|cEj&s*=3F@>SnY4Ef#;` zTa8ea6L*+uY%$pDHfW2dJh*8-v6ruL_po7$#ktLO-~*wMO1OEXlIOoN%J>=LR--gp|F}sXE^5$3~O}xO8+gIameac^4@ii#_i~mpceKu`LOiafv@}YmZMA^auCo!7To~Y&lM_Xa?O8tmGrA$WiN%jH&#sEs#noC zFCy-E(fI9S+xZ97AD^gOZh{vv2WllrE>(;74p+JGuc53AlYV>Cwg!0)G*LmX%Z$Uz zkUs>lEIA$+BbYBN07!foi1_t#u1gUU`FtCn1|S5l*qf`wp9|uFpcx}7^?Y~DfsnXTz7sRF(PXIY1nv1R6)s?mA$63SZ?ixm3JgLuQk`9+(io$~9@PeJ?2#HQVC( zI=`{P@6Ay*q;eUu?guF^-ID5VI0-BN{n?mykY+>>x(Re0yo9+xUpC zLl>XjGqJBnSBD~aMX26YEvX6Su}=<-Aoh67ucwi^5|dA8Yw>_Mxza2l%}jI6TFAlZ z;LtTWEHX=JGUl(9rW&GK8>BYVjjc{_Ubx&E+bPLmiLb*S{w~hv!viuc=3gxzQDP(c zCnB4|+ZZp!eHHUh8k{>KhWiJ2=Ss~Maxea?&iMiRD|t0mcFi6Da4@zUB?j}?>d&7) zZ1bvkVgmTvT59XcdY{X0^(S8$3hCV{Y!mDO5PY9O`!n7vw$<8W+>kEHBbE;ksp6<% z_4+ma>Mr;k+BE%chU8s3p^3k_9n*RM2YA$+?%>; zxUo8qDMmD77t*RA4^ke~{W6s*<}l~DPAl>8<1^i`Ajf@%kdNq$Abpfk`vyt95~<@^ z+1P*9V(m@m;dE6eZTp+IcXw0;!hV(KvOPEc$rC9{JFGoJ*ozoyyr?7w&CzB(NHYQC zbgU0@#)wTi1O+&AAv*t!u)>YDq7t6S z-9*t9Wh^H7_Sc;s*?Qg5aLnP2>P(u5+$^7qnpc6S!wM{$T0Or68t!3}ec8N5s<3qy zT__-zth8$TAzx1U)kVS8fnon?c0#Pc>g=f~v^1#JqfVb9Ke?sRDXkE(g?kBoyIOdb z343_{uoz3|} z0rOFrlo8<9yCtf$i`Ja z#+$a05@>ABVY7td8>H$-y^D#6t)#9Ejrn^wKi*T&e%F}v3+UVglPyC!hb{{f60@gz z-~BkxuFH=#uBAI@=IM|9IspC;`g7k8x&u7zCd!m{r;y|8^tP`>S3fX-tTY24i`6Vr zi;;2lz@&hqC+ty(-5J01Zm!TH4$upDPZ~>6zb8ov)in5{JQ|pqHXt&X0~z6C(@+ID zQu7vI<$UryC!>&Su4t=}lkXmrASJ&;Tz2c2>5093r3;4y#+!}xH^AL*;nU%Lc z1LRFi_9BT8YaK@UsiS{K3NSjK=0`*^Y~Q&>AgHcZ28NAWH!EHQpVa$hJ{ zb(oX4P|nZ*g{3XV{Nl?Nio4kp68tSS>D3H_RW1n*%O{$vO4&t3yTb0N>9tzlwD1<^ zIJpCb{G8F1%M%nISJ?F5XI`aUDbZ^ZuBAF$kkIp~<{tOlg~<=#d{W|c^Le3Kr~h_i z7yUPGwCkp6v<#8~5lG+bAS=zOhwj9WMes{Ts<}$}btx{4bw-ihUlg(nN!$Vd4`9JZ5 z_%3i4(lWw1Rl=VPCeILYvH2U5ssKx=bf-)9zDU{(H{0DQ<)V({`I2V%6{s2=@+2pN zAtTcQNZGCUMma0I+JLjcd>Ve)?jY>u1*&4LfPG|-$|D9qFf9v{2r+^4IG9?IaF2Bo2*KT z%wb=n?CX7xyTzi&i}Q^&EhnMNCp{<4Mrsa3L!HYmb8z#ef+q5=7!6Q_^kD4GB^AdaTAJfEF_`j*mzHU_G26 z@d2|jMw$3Md45%{3i`xSwWfJg;Lm~;psZ~?t8$%kUwrEtn)cgk#g{8*ckV){Cs$GE zDnM+*uXAL<<0xbFw=`P=Z_oGcx^wWnfd2OQ%s6sNirgEk5~%j`U?~%%5Fi$vJ_-h# z1K#-BQyA46{>RMQ>Zy(Rght5rZWMEz8N_B<X!I?kA0(vAbuWgf_C`FOD|H zLb}ex6Lsj<)_vD_ntrZ;#<<%1^6uaY-;JmcG10s2Si}}R*^4j++zWq~{p9KFdmlaS zT&mpNMY)IT`(BT4QV)B=@2EtkyaXM|pb0em?0vcK%0ocLl40DiXp}seUWmtWRH6e} zMHAKGzSR9R0J}N=z_iJ~zDz#F?5RTA0a{L?cH5z}QSNC_?N+*Jds0lo1utM+BE!fc z?8C+7-3q`f=@c7j39t0=H4a3Yw_SWgslX~PCtRpcSEfHbBEVMb%RP0$v$AjQl}1N( zLGX<8C1;yLP0p_k9h`(YP5K`YFR&S-e@14LVxe2CTag46eV+T$sbjp39vXA5z<)4h z$qyVTHjtv}+ys!2yGl2sDC(^n*~&R5ZMEgMjn<5n`@_lfIH$u0Gt&}%@>*Kvtj%f~ z^yFl4R2dl_ksrn#<7`XXga+R-eGmC&RXR(YyhQ2Y@!-~bqY~+b!vzEEx2$^gLdc=H zPi+_O=VWU&0$1lNvUYxL#E&u;4oiJ@@^AZugUD&Z7f-?p4^|dzG z#Z)2-QVataf!#uryw)O;D!S`X%b%X#wDY>qD;Upgux=`N@}|cXWwp4XrXhij-agrO zlG9>&NdUR+)g;G9|D1HjtQBJUYJ=nSubjO$8ACBOnKwzFlU6V?w&QV@_)R&9(MdVR zNsU1$;y}+AeQeeX6H3xIT^Qt>+5QBb=}=wTx*~xHi{=Pz8fBX{=ZU5UxrIJtX9;YM zO(n%d1twKfKltz>I7;ba{cu+d?<48Xg{=DOzj+X*E9Kd=UdX3389gx$55Covvk*58UWG`bqorttV$%u-9USPM1cB zkwAvx*@5tpO35+Nin>r^`1s`nZ^ln>@vRQn_$-cK%Nt4u92*l~HI!$E_<9kE| z;@f*DxzC$-HGlze0Q)`CC8y=oOw`hm45*#7)8%C;BE^UQX=`$5a`TL{%W+afvjc9P z_EB;2fC8%U0+9aXo`-;|YgxhjEd%^>(C>j}A01!j7K?LFfHDOAcvTsamM(WfB%3#_ zwOHLgCe10hzm?}aohJ)-dXz&4FTaN>5LV+l+lL9eOk7R$0~0AfT~Td4^1?|6Z`o6^}4ELr4kvhA|4n_K)pRCgrphE9|Hv*a3#f0{$1%10% zZ1?Kvv{BDje#wXJHXBK~_)ZKMf!hml{|UT3(X)||$J)0}|AJ}FmLO(a|BUocdsaFJ zLC>$qYNQcU^qS=zq2Y5qbn%G__|6zd^-Ncc%3lJ`$eJgdQhW-uv7LJaNUx%*dOxF? zIB_>82;0w_4s|@U1;x1hY0W`T^J**_n@Qxyr4{?L@|mE43wRyUHR13Do@sULTqnrm zsVgOu2beyE8YaSlSG3DL+Y8gq*c;uH2<~c`=pGC+8PAq&FYym= zh6ip&?67hvz*0SCd#fH%*D>)Dqr0r*{;!~$LTt*_f_O>(S7ehV$p`${i&y`oZC`it z5%VDpG6rPkO0?r9g4Shi=h~(s#M-G|YIFT_-pbwzGB20L2(cHOE&I>+>&ZIxT6%|k z+2N%Wc9_Tqs?5lUhiL)!rF|eZ=i`%1y6|R!Js{X?eD!c+LE;KPUxoW}IK;hse=t|e z^L2sNjovK@v2$$o0{4h}yO`aU$Iy4G?71v6)!ntZ_A;G=XLxVkJ@KhwLM-+Y%Q4M& zwlFsmpEKjq(VOP^EUhx=WmjgEAhwsL`zVeAyfm)_M#|t~r3dfY;m`^PfDmzXW~PrP zr>F0du|>EsXhAx9E72u{W%Ci^KSB3AHQ#1e@H`{~@7x0@J(~yJ-Iy{AWyC_vtkg$vSHI zuH6$2LwfTBZpN8GcerZETOizIlVySA5!sh56G`A)V2U&(F4=jtZ0;bRiIwG&OFVgO zi|S4q-BY=8JN=M{dXMD}KT|e)_ClSYi4?a`>?xL@bS8Bk`9cO2W_4}~)vvbM5AOt- z&lUYz%Yp@kZ7Wh;5X2Cz*hr{OFKB5d2XRDQZ`|iLw-W=o~ zoqRK$mm7{3xPJ3-Ym@>?eNnhtQS8UcRoe+nc1I)V{D;SJY5r*jy+pgfp3~~- zs#d{*upKgXy(+)+?ROxKws`;7p1IyUu*ai zq*72vhn%UbKoQ##ODG_|&tI<{i8$yGCiL)%+pD;q8Og{o-nNUTun{~n)CQ9|X)Py- zjDrWHmVc`uT-Wm;tN#kmR7VEVWA@m}J+Z}+4SjpZEk{r097e6dAtXi)p4rkZ>hQ|k z*z7h6YVS9MvBm9iOQj0{mv1&7TIjpcP+zZyiJxo#E2JtVZ)f%2kqTm&GlTz(Z~nEj ze%`Y?^Xut5uB{KjQe2sW5zY3dCB~sEu^W|*$*}NG$7ndh6sLRfi@3r3b_WHepjSdWqdBeLG;(YEjL~wf6 zzU~L4HlBJw)%4aP2R&OF2Y1f{O_H(O5N6CT&t2LWCbbnk3^xyYIMKK-&VgGYbo7h$ zdL=7nD2cd;Y>JaWWwyg_H`>Bc^8@NhQvfi}uZc&!?_i_#a%1a^Y*`k9pSxA)H><}A zs&DtkW~#mDjoBav-BGE$YVSXVTK8$%d*qx(UQGjCb1r}%y6#2Rvjt%z$32igc=2nB zSH*R1_lmo>*ATODLgnoFTuIrut`u=h-b@pLlQmz6DRW_ysg?9$>0gJRVm>I)AINJq zUcTU?2hU7)mR{rpKRnDvA>8MF%*?v5eT=bW6g}16&T|4fp6;4m;JGao zwtl?QPSmHQ;Rfnn2oc}$Ecg$YwQ1w#JyLhgLA#@5X|#ptiVw>bS{$vJ!VKLWXbPLKYaNIL5_{r}&b0>LjPW z$dN{WDDs2B_4>XC!1V>i6bED7*nZSmo)#evCL9$zEKluSi2NUOUY(j+(WF<$oH!-0 zemzT!=Mit{0@b;*3mBik>^u{`4ZWTD!vJr*vew~J|Az3`g+8q?#8&-v>kBmEV<7CC zY}PF7YYVC{mK*__ISSZ58Ui4ibpz&SGgAWm?c~Rehg0xWoplGZtGVr?0_E8W6#;j0 zYo>>$Rh)vm`Z@wL5syUnIN_hnJeVY(R0AcO%04%kO6 zj$~F+#@lj%LS>R&U^0~^^SqODa5TTkFNu~IL`D>$rU8;M3T7YYW}^Cvr`dlstHA0@ z#M~X6_?oCH^?7iLw84`&s&3)eLc7N6^n?;=a&EZS3G|*RDtE4He#^Vqv}3f?QGX(3 z?4*@EAHxpwiPoZAE{y0>Q4YS+gB!=IX+b0rG8C$ltG45rsaBXjf1!u zxF{H}!&)gvN2@sKx!cXmWpby;FsiWT(*Xk}uSrGuacyEbdcwJHnc2W$*O;NWo@{Ry zTIQI$bmSC~S@72V@hhIO(}MFiRa~vR(9&CE`lri(WXMHEl-ox&-f?m&m504zq;D72 zL8MdYvV9pGmOs#!pEz|bG4f5~;8gURSdTAT<1aB}v!2@{7d;g;yjFtwz^|*HFFGui zcJff|+PXs}@l%)?QsKOY z077cN0)C;dR%RF&-Q{y6CQDTQ==54#<>%P*$6{*?dnW=kgMft2Ol+?w&dR^^??Whb z+ph0bEJ8Q7gOwYb4q-eTKNE!JdqWUSsQG2R^%hAedmSJqbys> zS#Q#lvND-pjZNNbx?)E^^lV@O!|^0k{_4*rDOOtRJvFBvX0E>+FA7O9_P3j>)Vf^y z)^QQxZmr0mAwC?%$i0;W@ywDk6wlp)oT^M-BjR?RE0p`)b=PZVEg9r{P-KSMdYVOY z@$7x~M?^faBz|EzefYZK=FR1({yW>#e8MaV(f%yIRJRLTU zxztR4Sb?^ugt!cduDb0B=}fdsv8EvxY9ZR+A$z$vGG73=ujDPi1}Va5v=zzCdE)0r z^@HKc00BQd4B3toXf(^uJz8+pcpPFMO(A%Dve_2MZz{8Q#~7RGJ>%x2(zecE)wm{K zbse&#La)?*IJvU>o%5*(uh&OE}`pcT0SE$KS;)_fhLHByNlbEDSfkrvWN6N#U2yD2EI> zWGa?uQ++)qd}?sSA(_9U$}`~h`hXol^o{-{Da|*un(5nf-R8UTe(o(BX4|iv?Z`x* zC(ZjP{Zh}H$Q14?Kcgr~3`X}HVIhHhze+*wWz-a_1LsRWWuHhOUt+Ru1^sRW1-G8f zm6m73e?0ISOCX|q-=S#!YkGeKnlgHWn8NZ^4t?}+{e6X`HGOuf^*_p3k<`V%o0! z`kw{vh;C%ArH@0geXfGt5Z#x|b{0{hb*QkKsD1_nTU!8jbaU?`n%Egdf39seYY+nV z0TH9Wg*bbW$IjUxHEMu-8}KGH1@)uY(@2L`+y8~u607_D$t}5UU_h*G>*>JVkkgbp zJlU{%fP-UA8a!Jdcqg3chr!GUxjb2T+Q zYPZ)5BCjt@BfAAIME%*7<#YILJj5KI1a1V#zrslP$I3iVOW>K*{M~`0)AQn1kw5}! z7IO?|C_y}s`MlZ3AkboeZrj^ALpf@rtuMx7OJ)_!^n4*Q`^CbNkIn5d0Rt15>8pU_>u!@McM9Q z&8&xc050_DDZapYimF&Ltzld;=ZnZF(Zi2 z@#Q14>XN=JK)W$2!Cn&jT|B+keZ2AYn3aU=&LlDBvbgoMcnjioT{x9sMvLXADl@B8 z(-{j;UGq&ehGl~xBcw>Zlit#z;-IBft+np;;G2KkW+bi*p3S;GVf)B+_mu7 zN2sO=*%jcqeJDdZw0dby%XFx|ZF0bcT(;A;jHt6IC;M>4TcyrSMf%LC+zvjKj63~9 zd?nRrpFPW(pQTQ^dHFIR-Mm`ob{}v@`M&%8?;#%wt^ml?_V4Qp4^3i{D^J6}2~Q?= ztaBLrR2$WuO7&$tnHb&mr$x2btW-j~W3|dHz{HG0& z2CX~$;R~xg)ow_VA2yy6kHJMa@E*92oNxG6%1zpyHb3|pX4ez%cKXGz`IUWS=rJ6* zc-QBtm~!@AJz9+Q-t6ARu&`NfYJc`aF=j9L5YYn` zGK_Egmi^+Ojj{>f2+vaA)VFKEWqc!-x(6jHU5m;o@nr@p1>vdFYsd5~v=aSKM-pD1 zIgaW`Zdoqa?>^ugTj@|tly~pj^wWQjy)3Gja$8e|qL7x|?JI@5fQ{60sP?-}pMwBH zGX%ldhY>x4fJ3{$TJj%M(wLC;HD+f$x!p+J2g~9uAFlOwOWnh9$-${zml{q{BPMGP z9*c2hRhHigBCToi$6nkd&fGR`doqwZ)lBsO^5H=sCOOaX18ZlMVT)Pxs?}isHdhzp z3BP>GqbW9lL?mIH(F487gy%g~K=%U6E4;Kb?pZ4DK4y%rBvbhi`2yaT^^Me|Zuc09 zRXIo+_@sb45Xr$}ul#WR?URz0LY`LnLu>iTK60>RZu~$26G@5&`H?^E8;AvW#}_e@ zA}))&eA~^swrd@jkxzq85t~s_F{xiI4)HUG0Bo5qI@l1 z^0HX`yC{sLeL@zf>M7XLb*$6ZWO+^9z+5`C9y)B*D9!qPaIQ@yfN2Smy36D_Ce>bc z^zDHHF(4D!MA`_7&3a$ zh}%lzF|VNutVFIdj~aqd<$<36l2DRJe1TymvBqezD81==Mzzneck;=F$I{)wP(k9B)wJ@cA?;PWmiL8`^>uXG69RM9|Pt@&zp_`Y7S@bU*jgX0GM9QsX_f zb=N6~^zO3{MW&|(l(v0^ajAI&56TQLat}g$q`x#&ZIi4gX&+7B$n&XupB0O{^+g6P zex>Z?y~W;`1ah*i?mL(<5Bx!@gITM_UQVSVE>1k4kTa2aR4J_VdK|aPi+(a!tDGC? z9N!4-st{XBKL4`c9-C(+DKHnL;8L7zsW*lLer7c5Ic2tI=_dCHd5dT;96y|3`xZ&X z%EuRvQx4za|A1F`PcsUC73coy+o9;;(o<41Big}k0+z?gIr{KB8{$vJgMPA%NYBbf-Yn5=O(zy#lFd$h=ah zj`WNu{uoZrAE|uCVMlgtM&`0>nWNj1+a=;;KmPf+Cy}Nm`ChKRpkDeGpO3)w$6@mP zK9wNFYkCwsuItwmX}bh2e;z!F;^i9mPYGB~j&s*{>O(VhqUfuWY?3D$<`F@~Sf`ZWCpG zZvFs$9=fh#Y`l4Z=yuXrXT7-1^WL%YR7Bu5gM?s^sI0EVh*t!&^*s>nM__C~-t>p4 zgY3g?jSVLIsoc{Rz=hu|Do` zK<&w;yUtV9dsqE*=nr$75}TL}0`%04zU*YV0O{M3=@7M+iK0Wp+ro&(+OPKc0!uf# zt79KceW|V|mwMT}Qlp1t*jRHyLQgqbVGq@}G67HW9Sd?=E?j-+V^-6O+zU4OiJ|~I z^oF=^Q@8uH$iChIv*bgXXZqYwKegMuTC*(xqza+_Ts5yZG6X5FAh0|kivis_Nv?|_ zuvTx*%9~p{nfc@LF!`$aykA3X4H9OflcQn)dg-$60=>*UUkz+Ywp~|fn8@&1$HhNt z<;{}R$nsgy8%$kuXaQ!5XSJ}Cj_qiJg!geht;`legB(2W0R%b%pvS%llitEAD`@Xh z-C&OO@*F}I%ggwm+?EQJlyYX&)s%cIbB;-91$Fi?S$CQ;BNA`MQn9Shq53;~J~o^& z&sYns1PaYI(s_q>ReUt6CrET|7A@&Qh3M2Ct6iVD)0TE8gl*Zci!+YK z+OyC+_a)r3TyT43bi8@1T)(5evo9Xz@ydIoh%HuM=|wIx&7)6mwR3^{7cE(4Z2d=X zyQXwWqu*U^zf*2MP>g$C^`!c^PiUC2bo_|en&uQ#$t12RXYn}#A7~|Ljc*@IW{@;^ zB*dl6!y}1l{1Qv?Sl5?9P45e#c@#GeFP=p%CEK0l%A?_FdFydc$uHWw$%friJPM~N z(CudK_JHwi?oPHzo>a*=$EwFP_EqcFPF#qqW0tEW)y@VeJLt~vA7bSR8$2dr3|3q* zBacDr`Z=AbMnpk+U$GCmA3jyQ(NvSO@uTQmOVHqgt;kRoc%t8fv>x+IQb@%k#Pkna zxifxE*}@0O+xuq$Zi&}YW^`oxMS{tZ$=QVV`6a*be3Q$*?P)_2rtlgDjxel(yCE|C z4n3=ntehV(o=EdGNEay>`3P1b zW?0vE2eHVS6JjK~aw1ywe9FR!W8?J*aj|w0`eySSPkgvgOlwG5l^U3?71ZI zN#RZU?b9L@=rAE5^Q*B=H0K;1)u0R`FBuTX^^Rt? z#A;*2QO+fJX>fb`gXEV6Vd1s|H^c||&f#;bn`AkUrU;9^R?aU^MmrGgv_jo2rnBvM2kgH%V6Wnv{~ zZ#b=f_aUT4Sbj@xaz!P{f+Zz^$5C+|+CFyT`z*;!wm0*gj@~^VC}A<#RhnM@(zvvy z3!mzlow7z$RwCR>0E&qhmL z?R&)djRwJ^<3JM}?-|gk7tVp)MZnWmt>@K0JY9qWM|`v%D@wfd!K`0iJ|Nt`yijVn z^B_=GQQfBGL(A#aQiz|v6hR_E7CJnjNkP4@Q$@0@g{tJ08lq5!t>n~=rH+dp9Pacf zK>G-y8Awbow>6~?!8_~y{&ZO`?o?a)NG>rs{sO5|hJUmTijIGF+qQ#5k2a~2^l-W^ zGM##b{KJOedAr~Phspg5vvp7ThT7=!XQHh(anB|UKQZEE*nRScJQH&@TbDgq>(M|3h>b%l;dHS>!Bv`zn{L`~+ z;K*$-nNqQM=DYXevwm;4u~mP7kUUnXIkV4NMQR%a z@;(XN^-;Os%H>iqIC}b_z^}3*W(pDx?MTw<2lf`*s10BG<4eiUXu$CP%B z@cXDdNV!j8X8s-K`S`{CtvVX;cc2<$|IJ?QN=xfYC52m<-k;uIo?^w@K_!t+-0;t!y_!a1&lv{o;Sp`GbqhNAJ4QdX1i9ru0Us@Y^gKM{OQvHlF*==Acz)v~&K6SyoZzGc8|=@Up9 zhNBn9%3^dt^HMd`jpQnbJI}*ghu)PVcs4F_n8py-S}-V0N&4)(O`G_SmA0_P|Fozh7#kA-gBW^l*khGkx0sT;@g!Ly}jv6ThhLzd$bQ8G-17Be=U ziHP{Ulg7pS5UI%JX&N;)0;L}IR(zXUIuu7{-g)O1*@uIP@&Hc~&0eU8IIj+mGx1BG zX4hHaW$u;MN3D53PIfD-ouFrRsgJrNV<=BPnK1CqHbrzV?Bgr(a@M}9`+d3CfqzN4 zB$>{#v|V9i=~++epFX@=bdZTN<#FhJ7reTIjvkFVN(ZtjKh_(>Nq$DUuAl&V#4Wzw z;T&mD>`8qj0KbAtufxqa`#>VTi7~EQtsO5UL0XR+Z7)!&B?fxAt45YOnjx%sRh+u}6 z216sDWYzGTO+tIPXSl_l4Q^W6nfDwO@2Z!E1hT=Zs#GFb;TFg3w3+X`_QeBIu?gs% z(&%FsF?rWpyVxsJ)rL)r)Pt=B2Nj5Vf%6L>hc`{zdfa8J(bAWr@_t8Abx~-9Oa7!+ zR}AyZsIn-ftsLi7z?zoDtM5|A35>dKCr``te)n`8PvVkbhK|3n!L`~Id`V&ROHQNn zDR!L0E1(%(fo8PC5le)dnUvIyg#X$mSLkshPxhUngT^Hxr8*AZAdA2{S zK9UTL(LNWVSpHhY1Ay&h$d(p`9`?jPnf?8z8U05d?gcmC^iFZKteR2M@&jjz;q+bB zzUC4Vi@|t0)tTpq<33>o{4VxY7=?5;&l88M*KQNK16BF)U5!g*vTiLHZ9uks)7bnH z106Q=#j-nYS2Fzsk@bj=LE1MKeZS8U=OKA9lC<@d`oqJEJMX*(D^~3zdt6br zg{GyzpJ!xF5yK1>tH)s_P1};+>DK0l7gte0l^-&AC#>CZP4&fu5s|`gcU|%Q8xyoy z**>1+j-zlBe+fsuNl-lz%OoR-TrYs~0caz5Crz)|NNsxPG@ ziDsrPI9%;!OIiD@;cylfv+sw0Qnr34dw<8-fY`aR-o%5%)IbrH?A^NHG$inM1ua@+ zSdKTnWVJBUdvkRIQs#bMKKrODnIW-LE(=2V$q#*9erCU&?}uk?P2zci?D5jfQ;p{I zUgudPP@DUsss!%8>vMnV#oto+`;nFooxw7&DIBjMRa`Vs2%PTJs9~;~b=PWqTTGpX zB$YpRt>#1}o6hSzXT=)1!eVN~)=6?8Mg3lT7>QQ&ZsT@~NyX4f!u;M)lUp4Qt~KK4 zvD{0$5!%17Q~&X47xx*=#O1y%rDc(WZ+@lZ@L;H?JQ)N@W=ne5t`VODjIFF3S>MAi zZ*mHFF(>Ai#?bfCw1NVvdC2H)X5W&;HMci%3e}uyDc$k|kJD8QKmVy0{`OH~RyB9? zmePDLrQXf}e3%`LOJHe4t#^uNKi_OR1W-ETmn;!`_`Dc^iWJBkf3KR?O^OykU5j%i zBrsYB+{V?Dv&)rZG(N(DYx=drics-PWXpu^cXudBFi}tb_>a%|*VFw+@i!e|zaln+ zJaXfmTTl`1eht^lUg6kO1>ek9;uEHTIvd!oQ(?!2y~ri&1j_(tj# z)Olj-`Ldv08a$wNDNcIE7nNJsOq#LAKT6x{c*%m`RfhaqHn6FfKfm`7Xyg|XYzDlN z*L7pZE1qbBT<^ummE`juMf%aevemZb;0`zMYcMCt;7M+7K{EjF1-v_=M9O~^xLMgf z<2zgWd|Bwg;_7sYR_SA=E@nYg_pL8N=x?j}=d&E!SKr$(gW)k%;Zwq{s0e@qIP14J zxD^=a+=ATNQ>%;t4e7Zgm(@ILKX?hd-3gcJf-GVC(3__;BxOq0ziiWgKGqQ*HifGfXV|hR;aB-wFF>;LK5on!CPK^#p{!i5anjk=SfSPy1KQyD_4QFjkkPlNL|F()BS9$d ze}31WTlX_!bgpk~$A_~AdKC|w^GHMP00#f-#ZY|~mt-DNra%UOP{l%RWdi`PoR?Ef z*HW?{Fw9m^HQx^s65a)pT)FcLEdu7v54%BkW|Xsci?aG?lur(tD*C^q1d#MN!`o?Z zf5tQ}O^1G>l-g8FXeTL~zHJ&mD>zG(CP{0zG69=EG_ zdyBa6)N!^Zl;O|q!VT(r*M;^6`g?LAZQ1@k1MMLsbRw`RouHEtQaK4Mp$KK=b{oK8--I{R2)EvI1%)rmdDI0Pvp74B z$~Ty}5#=F)PiL$?@Y)*`NBBKewO(Nk&fccm<~{HJ_u>8PlK>|}=5-TBCRelm8ND|O zrndB+l7mv=w*SpX-T)kq1}b3o447AnAk-U*@?*PeM_W+s|8v7HLeRgG(-rhqwynuG za4`~Ot1EYcoK@;prm?0Pg)q}0?yJQ8x1=wy&L{gJ)1bTe4ZQ{I>2SdUxjySfURzsr z2zX2DMp>gRmY}A1M^X+T=W#-`DwevAPVtPB36>stjLM%le{VVq=7I>- zfb6ZZ+*m>oFZtKPI03)!zrOCxfwwsaLmku7=50XCv6W#)wF|)Gt|#LLV-@$>$A zMj2nt^|NcvkrQIH2cIK$ty}cppJTtb>ep0j1_mgWq92*@PWG8UtwmtSkOPZ$%gIgu z{iI)(%(`5_Gy}8&!_}tf&M3dZ%1e!*xxS>3m1G(3lYR?$i`KkO-r3LW9!xP`uUhzW z^(@um8E|N4O9#;POL1QMeo04n*Q$p=kV}?~OO$cue=pyNZ#_5F^9OC_u%?(Jiu9e4 z?~9WrF3n#!%$mI2PBH+XnKXd1;OwI{_dUU}(twAEcx=bZVARj#0ZODTjJbPN&}TH7 zA!N8adf;|B#E_1qKk0vb3!{(^xBYC*n;d;Z-s6{*!n@-)8}opDnuY>K0#FdMr84I` z9e~-K1vGm9nN%JUadUR9L#B4QS~Xn|#)x7MwVE2vmh2ckS*+}4j@HGz1y&*(a@I57 z9eMit>{D$B@tNXO|JzuBY0v(@o6vR=I$vL3HvaO(aAHJe#2FgUoEfq^Lmo`8XG^aI zWQrbZnPZX;<*{!;I7?*ZEC*y&20e&(J_;XxRXH`9E#<>?czYjgjs5QvAk{M1U~(O8 zaDNX6yb>@Vdz0Q&mFeJu(Bn%QgFjDH4 z<ckG!l%)aywpLyz*m^oqUV9W&XN$a$VU|im0--lB!^qzBWG#{M$Sz2w z6*%#^(Lr3l;d#w{@DV*)Hy}knAd{ahDP4UyEv~V%taSfU9aBaY)~;zYwK8p&=hW`{ zGu^&@6g4CIFE-Y{B--1O-C#+U7WtHWzsdcmAwE?yzA^RreC4n0Mx@b#X!5&`niU}7H`BiH*&)U+%nzHhMAbJ5psF7-N7~bYh$+(^ssGk_a%j(Mjz^2jwER~a$tg~ML z_^zWXnzq5tes=$45B00rFDV{*%x7=br5GnaL=fC$CPcR4(J4L)fKvB1ktGtC5x&tq z(tMA7EqKx$;wo|8JY<;vJNaB4JI1%LqkUwR>OJsQRkI6Y5AGG z=dYJfC$uH~Tyf?Shs1O2PmA|%7mmNig9tNCwm$wDO17Mh_63_-G2#8I-t=#IXeS|A z;B^&qL?fe`)c~d+);X!RVf{7oFG!G;!WIUI!yn^=ckqSY9FI z4}V&3*8Ah(hnGt+hn!x&ts{_l74DgoH}%NumvH zw?daINNQxflJ9iQJpKFr)VsY&(SE1nHwzt;30|N-0(aYHT?DX zBdxLXev3|(Pa)(he_tFBB^xZr zmpYq`mbac6u@pe3X;3kw7!N;|nsx6MC)i7b=58ppafAg-nj{m*d}`W!A!yA6H!b^M z(Y|)su6)2GU>+l0y9cCVw!dwZ(J1`b4Y!1T%6Jc2MLE{ow_Ar(E*I!xB#g?<&3`hG zN1Z0dGF-%6i`}5UV_~r;TQys}{qyfH6t)pgCyJq)@AAnewMW(uN9dM$xydYtH&qau zLc;k-6_~{l9A)gdlu9CaCvA3h;M^x~bOB>)F2sEFYY))-TJ8f<<9j5u0(>k~dC2Qy z?gInaUja*G{)9!Csa0sAgG8F`vR5f(LpN@bj>`N}Eyb4>S&hi}`%ZD2%7?P5x%(`* zCoB=Vnd6Wlcg^+iAeOh%Zv4cRmmmd2@YXB{?*sVKp=C`R@FLk9hbsyjJxQgdX8~au zi+@tx>!tXn3;O!q@f^|PB);x1SmbN4hrnCq72N9$^#ES#JWd+@GObZpW>*;)Tuh0Z zs0aj7P8o+4MTm681=gOjf&evd20W=&+V4`H0~UW*x)SseLe@9g5;F+hRpW0m?*XUr z#$J$#xlKVKcz4cY=HdVg;4g7gY6oSS6K5faC7AHdZ)| zrN`3Nc(et_)21OSk$2`w`TWi92ccf6D8S15NL3deM?8g?R?xAYnzac2!(UlIvX;5| zTK}rIuz>Lw|CeR@bzAJVZ%2UAT*_E+5=Me&aGzku9ib{ z#04&SX5#MA{kI{&D^wFjUbNwo-j}zanDwhiW@6+0YHX9Ip>Y!UNbr=qAg3|aN|;0~ zig1#m;vXlforJg^snV3vxg0gzlrdk^V7e`~YL2pFXe=$2t6 zz{RxA*f1N>$#|F&eMyubbk~ivqe-{KTPgqOBYMzI`)>irpBnbpyQLK0jh~0c1B!6% z&K^I&=&xAO#=S(I0O(@4wBbN%j};dcx9(@8y7qW2hiLf@xi<@c{>_IERq$RDCaK!lAdq` zE!LTXp&Wm1+)2p;V*7io3jpo7t!7vNVA@{(J9&YGmj_;K^lSIp0JhwA*RA2nZ-Vqc z*3y-A=TV#s+S*ch=Cao1tX5^%B?OD}uiy+koVJ6aI~M@wP0Il@y3t1s<~8FS31jiV zwQUC13E$r~Oq}KG4Pg`E*Uv8OPLhfHK``(?9d-f*I~DlxO8qf&{VGgW1a2Nyf2;NN z+88_9Ab2!Oa~pad#nlja9?5kAxM+#<10zrc34C-Dkm<3->M05Zf% zmt#x;%Emx1{Xh50(qcZvIG&?NEudldMG0`f~eAV@;^9AE+YDTPHE6%zzY$1doJ^(L48`N!Ws{>k9IEb!b8H&`YO z*_6?=sv)pbnE6UDa80xJXf)~kUc_x5;GFTzl>&k2Nzwd*k{CrB*fkc%|C}ca?Z@wT8)qv-c)0X3xWC6rZhZtyU6OcLu zGW87GvA8Tm7?@@CT?FHHd;!L5wC(e7gUN0nQ`Vjakc8PuIFtqR_EyOIB>{U96JVJB z--1RZ#@^D?VA>0oK=7kTrX|2(+M?Sy$QW@lKPp|bNHO+geRbd(w_14Qr1_iVV%W)?n}EE-JG{z!%G`L z|HG4-t}a%xT%6Gz*o0o)6|l{ICz#*quLd-bF*S7i5#fG*yarbQe6D5-cRfX}B*J%K zU;;z7D5NZW2z0`(AWe{O`eBsk$UNvD>JIzl3L;qrw9`;j6GyKhgAD>m&S>d!x6Nsi z0+BC$Kp0bN65vt!(IKF5{eT@3LWAKDyGC^FFK37CU5yrErMFqF>C@DCLeT;^_J7}^ z-#?jf$n}yh7l4vc^fpVyYFe1Ce)GdBRN?ozsJ5IVfGoRI)DhWX?m`V;3L;=mM*tBh z3EJQW%zv$O_AFUfZrU#i0Z1F*;Z-N%Z3EFD%rNnP$eIKp-Sc5-1afd$^G{LB1dg(W zORml_XSX53y`SIEZ4?+V0LdkBQ(f#R?plq}Pi=&$9bq?dd~|5y z(Tdl3)y9&0A-JxJPSBAQ$OiNE62LCL+^phcnJcYv5IU3th0t9Hjjgid|H(Z1TPDI% zi%)XH7*!gY=!F`zi3}XjX?)P2YF907{p24|3fUKPELHlj` zkt-mEAlyA@^X7|aP_B1AP*|CZia89DHnge0@%p<%fE7PW-uvIsrllo&e~fvfyrsSy z66|USOx~^Fw4=fj{<)@G>aA2;jBcP!@x|TVyPxi_brotsXZ&$-kAJtRzs1`qoQG!!B)QGirq1Xx2x_>(s_HHvn>8C?J$rGy6kj)tEe-ev z0>usn!NF@leE`TTd3C*PDs-dP$nD7$hy!e)kDwHomtR3V9&J#jSkeQe4uP3Mdzk+< zTEHp62ukJ*na_p_2`M@6xsG-J+VoFC0NsPt*i`l~Kp4$FYTDCa4nfnD@73r6XYz%W zfLHRcU#)lR-jFm3$R*^;0(sN^0r$H0dpR0sZL`CRt4+Mxa^|T4|3lD=?~^T&OfJn` z4PtyB44E@HaP+#31KB!{#=|rd=QY4e)tgBApYF>{B4v*`SVm_{OFTlV=;#i;xS+g% z=tXp*fKdCxDFyH*#pp+tP4NSP4JN-UReyV0yJ~0fthC%Wa9w+5_UIuN6+azq0f1Hv zfegmVNhlWtjZM5U)D7xKfze%-H)p4{dwjvXTOBX);g_A_{n&~HJpASZ;S0dCO^=-# zl2k+`GwBP849o0kyYR^Aex$bI?Ejy3>5QFO69xn~Lxp`z3US=D9M#pph>uF3o&(&m zZ!jS*(Vrixr&UWDE_0n&=qj%_$q?PZz;cHHq6hdZOW^iVfXk2}3AQ*B1X1%NnT4ES zmb(mFFsX%&)a5`OGPH|S0Z#pq5cA&}*fC6KlFR(W9W`yU2KAfIhZ-z3TVR-htk7m8 z3UKfxu;WXv7J*yYgHm(;@FSRV83r^|+6QEpC@lDX*YW^k&&jZQ4rH)Q%sY}~o@)qX zejdPQD$>RAN^ATdMT5=gL?)MjUblfB$IBjiJCPr%j!Oj|7g*)cW_M;jKQs!0bO7mY zTfSawHjSp<11`xys&jPNg#ZSsY%cHNlFE!|4`IXfH00pDYzxdAtVcXBy^vv-Yg~1F z-^N?++p~;6X}Euj8!EWUgxhjqJ;>86UCi{V@Qc@VRTQRC@yz;0DS|+HZc92)C;n>S z6QEGyxa~BCTKkLiKhX&KPWRIc%boENtO7V)*1qySQ$ma~eX5wdKi}g9)Bhn#E%UFs z&Q_C~jMF%3tslu@Mo0wO&dz-5c(G>@a5zCHj$fh4ri~J^t&4syPDt*~MW38_@15_j zjbjT!=s94{&lU_w*MwPM(h^qRGo!<_b#El+zRTHtWX{b*y~wUEKY(gs`G@OKCYX>3 z+1lFrYWTk5Ry&Wb_cBmavvb69im#*cd_+@q&>jVt8>r+ z+b;8e75av9+}CL7p6dgeX}*GiUK;UOqKWTEE`RUo zIfhhx=e%(fmbMN322TB4_N6Q{Dl#&oC_Owf^7H2ahxg%eYtX2*h}>}WdPY=6*(wI_ z%a__+{E%4k=hloSVss?dCipm^F$p80T!kM7W~&2ur|OIwJcUy;D4u-WznEXmma*IZ zI%V##YtsG9VJy>r;on=+d^MNUb@hMq|2EaH`lqr04Pze*(rrnDJz{*a7Z6zasg zMGCvK6n&HtyH7MpXB{JCYvp&8BV^r>Fhw>OCvVX~XD#ql0`-3R9T$7T(nE!ct+uv@ z1#Hjw#3|o@$T{&G%Z9vrff7n-F-jGoOl9sYJ%HBKLSm!*K*@-!x^#h@H zp_>Nln-fo5rmG4=)OFHy*?WZ^3i2BGe7nY8=qNzehZ@W>L@ke@Ffl?art_i1G2+fp zqgqGfZ>d(jNnC3~H!fFL=XGu%YJqozVP6zR=dr86q$Viij#QWM-g)lBnzT29SbbY- zDKwYxP{7I9N|I8*vZO$x?Kact8(&IY}=rE;Qsj-{L3AfY6S%<-S~gTB7mn z5S4bF%Lsf<#xx_NX5S-pRc>ngRFQHVr%ThI$BXXGH$wVhyPZVT(y6><_3WmQPvdmJix!>`$oURuyq#BwN3AUcQpE63{e860i zGfG={MJK|H=d&jROh6;vG6r*ysq>B$_Y*-A6BDWE;!KE@2c_}Dc|oAAeEBC{BB$Qg z{b~HxR4}85VPnh$ufEz2X4=HG1YQN)aLb_t1K}|R^*PnL&QEFe>AJ_xAETUmzw6-Z zoqs7V(~M_yX%g~VRbn9q%1bU3Lk%k78Vt74-cEn5Bk;NyW>EbuV4O7AkF^Awsd9m# zK|4T(os01%-VnBmy8xv8UO9Tj25qXrkCP}@2h*c`Jk0K*@m?zg+g&fFX7Bx#P~))# zi&ajvu5*8TWh|+p?*wn6?+5-ze@y3(!-I{f@EmFIWU*m6nl(Ytq_(7whWBY=y~ssu z9a$PLMPFljF{6h8orQM${d#g(Bt_s~YvA9ilhNTjj8&#Rb5s5sJ^cDvdD^^>Y}dh(@YX)~z*EK708m*kCNDt3KspRIGP`TnaZz-k|EE)+#sm#uGa>u$Tu)D-pQ zBCY%GOeHg^$l6_Mhjm@=i}Ivm-vB%x16*8OQFcjgH#>vf;>n>0FYv5~g82;SOwu>@ z>eoJIB}oi9l8UHe=WuxMd?)B21t!u}xFA{{=ylL_7W71Z{?A=_DX|(Bk^b{}iCLqV zA8>8wCctS8Ojq`JkcVx&TG;JoaiQ>Le>LONWs_viZ{eN`sxpRI2D{z(d2(MNxhPeQ z(w(Z|QfFc9c=B5l2ReD>1WKXHAW}cp^HG)yz50v8U*r6efoFArjLbf(@AosMX;UWB zK6?y5VEF4C4%|diy=jX)zL;?+=MsMd3>dn1-kW!>LD7G89DgMNNe%>RWIFx%pcbI4 zu=X_vN5Cbep0es;Zb{qd^+JU_8Hl@f4PPP>4-DPraRb#Z{;G3v!TGlACI?GyN^SDE zIg1lSM?{DOiu-o&CiODPJr;*Fy?Cjjgoj0_hxD3nZ;2)=!u1FC=au5FB*3Dq30Fvv zJkix5;_}xHF#qf0ZcloLMNg#E|DR7=W(+4PC@usYLfJ;CD?@JXx2v+5(_wKjVG)Kn zEk$T-j3gJC7&Y&LHn_HFuvo>R zXV)2jzSSQ6aOburL~?cK1q28l?}oxO z8-Yy$dA^#+cXoG9$)K-aHx1Ouv>uB$+$wN2Cr^l;t7q@|>I?!cXb0|?`Xq`-SRSG0 zgr#&NrGXi2NR8WWJ7Uy7;yQC!`C9bOW9dni-EW*y8=5Av{~lqt16;Edn6uiO3cQ%& z3C<3|31V`ag)jX}R|BtJ9lfcTID37BwsK-Q$n{dXbx(SsdE4J*?5%6n!z%?rVJLqr zxPH^X8_+8$%sDgxzjzAeT<0HB8b{9&OFk&{DDdY(B!TB$JAabTBr-Z|aoy8cA zopZF%lgP0EWP~3{u8#zH)9JUT>n-X`2SB}eV=|P&Z*!S3=wd!gew;E!=#*EQ5LtV^ z;@tg~tTZ}aFhtCGV})}CDHx=aH)N7S| z@nk;bC=vY#^|+UpI~}Cqd6)a2*1n1`F#aWarc&>S-V>11pZ~OuFMR3l{)1eCXWRV& z8-ccY3v%88AKc%-8T2@=nkW(JE_Ge<-E$P*nd`n&-_Kg7ESh4?Gq+W4s^*%?o+uwi z3UF-~TFM|_wbJe}?!A_Kvr+7T@fvy<$w)VneXUnYE9u4!;HK00l1=^a%|s`Izm~>Y zfWN{_fiUpmVCuI*{lH}obW-y~P%Mn6@j?ZS4a>aaNR_wlfzW&&R(C(R^vjuEBHw@{ zBc*payW#Y%PZB48)|#vl^OD}WRmioh0be=ZU9yI&bs4Vu(ow_s6SCZTGi=7|*^6CYDf(lXu_;|K}Uq`J4k{l`br zqisYU+yYPr&j@re!r5j$%ChK(W`B(6%dOnxOcME-aR*t;;QVRf!!eA}h1=KZ{{NpZ zC^Mcq1^;CqZ#gH6J)S#zFC;I#K}La+FA02i@q@-#x`)nyl+K3-5TO&$tEi_LZTe&y zLmqLQ+xH&aP0dN=Ql@Z@12wc&tBfLx4N8+lwZy+S3f#-irKZV4)Q;JIYyZjdEZa@# zhPAoJ6PwBKVZNmQkc!Ob9nvx~*%&sf#^~{DEn|cq4mn#(G=jZ{z(uXy5uqU=Ax@>= z==t)@Zj>b533N{9fwOM*H!P#JcY7!vzd^G&9YLj;l4U5% zbaXaUbU5rlhk?qN4*IK??z&r76!q8O=3mM|O^Td?>Ol3Ry@#n%#_?wb8#SObEmXpq zXBs^0j2gY1G6&!90PRE8brkOSW7p!^ZMl}2m-gF}Wwx}XAHdVaoT~j``MPiZV*>~Z zX>9DSCvO?tv+sAqc@guKSZv{VGEc&deL9Hr=FQjqYPWgb)IM_cR>-+yz@Wb;a>Jae zXM;N()Cjmqfa9#M^;mGr;Indx_S*GvM^366>)&&cwm7dT_IpSElDid*q7>OA?|hpE zWtSTs#+H?v(B$@A3|Dt54h=B2G?(;_)DOZaPISz-TAcP|Elmdz0GvNl+T&hmio@YC zUD~#@AO1WJMfZ(YX7?xCTyA616AHh)^%f3Q_%BZZo;ly zRLOj}sqKv7EP1Z<_eA9XFBb5xy9zWt-FW8xi;Q=CTwU)s!)|A*y9a-R@|&dLv=_wX zP=Hm<8l(E-DrRe)ekIKjqj|E#U2GdP*&)_-`N$kr7n&xs9nSEHV#Dx@vm=l$hC?kb zB^bQ-ED@t zd+T*`c@nR2p;`PkQgvT;4$&}um8%98AZEPQH+YE+Gc{p{&w_9|Z1e9*2ymrtD!C>Y$XK#}rZ`p_y7_ zskpef`SAzfM3C}B)k5X?$oQiB#?HJi{^jjTmHvN=_4|J@X7#&HPb8RLi1^=L6UlO1 zUS7g+dIKI|bX$xGx$RgO$e%4#nSM@ad!M^CP+p^l@``~7lo+;BKWv(5bhpQrHJ60K zdm{7DvfeEs;^j`+Gzc1rrT-C??my8pxv6Oq1PmNnARUe0QK<`4{*7Agqo|}%c5<={ zu_*=Gg=z47c-2R3jl9nL96!s_WJ1OGWvE&v0KBrdE6xc1iLqqSp4WHEZ= zuXO@C(||72l7Z+9*t*e&egXG&Lia{c-S&;%htJFZ^O!t2LI&V&maf6Z_UW9b4;Q@H zm7QoE*C$GKDKO$MZLQ;vQ=v|s_UNc~w_F&}V#NnZY;{h)c4tFFL+u(1)xvSU58J&w zUz?44hyEKVf#j70vw*wF87Hji`J~f^>3Y2qJPRjg{VIWZOOnECTjaho6%&M_WHXT8 z-$S+{TM2UV79BdC(rXt~EYEzm?pSE{o{d>@RhlEOdhO+lLL$^K%ZZ+4xOc{^{?DJ! z2yOQmPqg-o4V5q)Y(dI?W(>QUsx;oL1}c_FtnsJ@UXrDgkf#(yzE`KYLr5tDP-8p` z4pLKNob^fn_tDFf?e8|1SND}OE@&XPX5|JA1s?i#JSjlg0(+kLf>BcNn?m5^|;Qx6JH`Z|6ss<>9_;^!+pq`XXTZP%2(dz`r zQ$62x1AxeYPn(W@x>EvY)AEw%J}UT@?{K7;!kgO)d{R~50F`m`@bEBV3zzFX&xAbG zi$N9~X{4t8*|#a#lX3b_m34p23E?M5u-Vabs^^fn0s$?|)s5k4Cx*w4=9$7+>ln`{ z#h>5;jjAk6C|kwg?CcDJPlXY0!)HL;wh}on=Vg4fHUJd(56Vfhez^m-BYtX$axdhR4rtr&;&OMA`pXYbl+F%d($i^Vn18^be#x>G{yjFPo?Qc!!mlV8z6x(|%SI+nq<74lme@5C8=Jl0e-Rm>u{2f4 za@?uj=GvzmYKkM>ejsz{vV5|vD&o-)1@a`0dU6^RD=G59ZeQjf1?id|Qx+n2R}Mce z4ZXOnt&Qkgq>K`gz@LwS^Vna05H`l^H@Y!YS^=mLbD$oYr>xwZjz=WqRhk8=q=XL# z{=*sQnt!)_!WdiBCveYe$Nz=%=;R&UVRuq8h6Eq}A4q6Xh1H?MkF${2rLldgCzJXw zDV1=e!~d*|e3y?KjBbzqEwqGH#9_mQvW!Jv*BhUmUPaF{-^{+EXzh!`5kYJX#5&X6QY=nBW8U& z?h(7P9|-Zx8@;qq!C%$c8&CRs``$hUq5gvih2N@A)!dBzPJ1v#E%rLK4eG}-x z)zkq?(AX8(g{jh*Kz7 z1iufKi8n9q4qM9o$>5!J%0(KhE;*k2%)OlAiT`OP;nK{uB$-b!cB8?j?8vHx<3%86vDf1h{(287JVK0pKrBPy%_3dZp;Bosbruf8YU%PXkF-}JKjd88|BC1QVJ zVd3~)AQ3K1o1m0G3E#!Dl#8R*!_|r@sOxBZ*nd~$$L*F!y8uD^-O)Yee&_}Ec*x{ zN?=vhU1i-PzM#OtZiOb-? ze-~g$eUCihJ=XILG7HHrHd(*$5%L}x7#Fqk%t;e`0VHd+WhK!Cpa5e*PhitglgbosjzZHU4leArk~+b%PkA*JJEzCBLALbA zq)Fa|YRF7#B$ch_bzkMrc@oNT%r%yMqt_Z9_H)-?87j*TQ&DThnjSle&h2BZsfq1f z#p&E<#nd}N3!d~JRwC1eHzOg%ODxwN%4?yIowpBq&Yp%0%|$WY4q~s?P@!EkF5c|j zOa1xu9J`1&eU(0j%@I5o(4iJ`i-0vB02F$L6sCz>v6?4nJFh172=A}R4xP1c6@f+G zUmSUF`#Nl6o|k#_>SRoNSp*38&Uvt`a0jy;=O+hsQ8bbMTMGV`a7iQ19^5O^R`+&5 z14?m^N*|vA!#}EfoP6BycqA7TXYCZ)f;=tv-m8b)!vKZN9<*QXZ>^W*xkn?D6Yloh zTPdgk+0S?jpdPj*g4tg$FHxG3@=8BRFJHMQ5glo5{TY(WB8@2al9l0kM%kRVu{w8*ah`Rb zZ6A1_rBV|2ZX0ic51RFH;I25To9j3Opanq2CCilKOI%w1%i%K6Z>g;w+wm}%)By9I zU=P>kc$wVO8_yd1(rNB~ z=(0JrWPcgdz#^9_3Vp&j@|VA}&oHS^dBv0cMURz8S^hl3q~mYVr~8Xyte{)S=N4}A z*^ImE^?C-j?`e$B4FJGsJj4oTA^Ry$;SpemE9L|E%*pSb0z(CGX6m5>jN4ibl-b%yIfkHG&$zKyvOHXotG=;^k!t~+fPd(gEC0*6?a=qUX+-|IxtEy++R!xkK<}C(F zM)f}EaQ>*kg)%(S&9K!=Z=j$@r0BtfRJWL8Y_f>-zsYgUNwLm9$ z?9=g-FK1H0#b;Z05ol5|H~U#c0fOVwy4am33&u!H6+W43sCY7_syp66%N~(Oqwk=? z_&Y{75ekBM`F(E@bYsk^p8J`W)K5aih>iX!wtXXhirq`M*#(b+r!^V#1X8_*DZK4R zH}?ZP{N=EDR78HM014hGEXs^#L%Mg#Ig0r5u{ZH`-86NeK+F+k%KT-4ek+&xPm@_g z-${=0=Csqx`7Q3`7Oe#|vu6B;?~4(A_I^hxOObpF7QK0q=P6!X2Fc*1w_FqJJ|O?w z!@Zy+g+=ne;}z*=k4U{lRiqOimX7tH5~D1|(sS*7`Y%1(v*1pAS?Uu^&`_mWp`#L_ zPln3=IC0eOaVF4%nbSB06a66bRR`5h~*g2dF9)9o+vF=DQ-)3L`f!ivHQw2PRGGW;ZMIvcNn zT%qVj&HA=P2EXqeHC>FGw2zbt=RPIb{Nf;4n~s4ti(#O^(B+fYNwGZJ2Yi8yv+vXF zsMq5-{T)3&_YH;~F>kAr7*`Ng>V%*KaJf0A-y}obvUpizzix40!A1#YrVWG$JewNS zo^7id_O4`BmI$h9K@GR$l2@JBBt}acPZ#}2SI(-t{pH4L%n=r^OSt^q?Va0f zt!*~zy2DXPX#R=U(YByJ*(%Wob${7`+tHY9VwROT*3C5W+Fjna`2B5e*a8K2hE`wEX&LJFSsl%)4 zx@W;>^ZW!0?&@SBGodRh8h+3&pMk2F3O8~i!&7mwA(vQf&}#-fl6gA?PvnI zWELQha!h&JPOsX~6zYnlx^~QYN+KL43Q?mNfw#25AIBs>GoZQZ&nDQQQqJa2msv7i zs>8^JV3ZFjgh;de@I77Y6sLQht9!U{Gb2us^NQoIwh_8tZ$3~=y~mG_Okx>HMm)eO zZgks~o82*!2JO3T4e=JTmXjCnV4Rp5#rd)mrdfeLPHO49YN~6^q?|&kQck8gt_Hjm z)GFq@7mm`$TdK5{M1U`)_1<6SZN`yl$O-mauJKPTxn(wqf^>MTs9sh}!)g1OpoT;0 ztHIh0`h@(J@kp=3Z@~4wSQDr+bE?`NL7%#pj#7>4j3>*3ETUR!3f-7}6&$87u_T)< z<+IufDSW$wp3JVEc(_v!N1sPjw{#m#zs)%Bgp67%61_0{O;R&(Zyy_7|0q+jao>Wo z_IK&N1r3AZtG_cG2Z3z=7c;f+)vyJNnQ%rv3i&WCN$d{t21s&@V~q^et@5BOLuPdq|z|;K%l<*>FGoK*dvtv z?V5ew9BQ%fF|r7|O@1z{TBc`DZr$x~EHJsCH}`dWm3F4-#bzgeW2Q}a-lchUpvFY=U6&cu zCv{0r@`dG7QVjraA5&*x1Y(bb1kcMAk;O|eNqeY_4jMsh>Ws{Y!?Y=FbN{ zd~?WUE`tl+Czw-UuB4vfN$P}feSb=z=6711p}49}O$d&l=#~6vsw<559$#O;odr(b z26x2|V5X^zJqg&boLK1`G`}a@b2HO=vwAL6>rca62>F;3P08~!F30oX!*aghPIc1T zH{Z-spp@Jn4tA=Ccx&l*Xk1P8Pa|){9r8-G4X$LpLE79cu59tHzmb-n>(I>@_F3GC z4~9&S=#oXVIGQHr(VEueqgKnBPiI`7fZ)aK(~yaLzos?qH^{ubpYC({VmF)MB5R$k z(xCD<$%`7Cm4Ma@)bAErqcKJWgNlq)LNbcD4wMyP?>7y;n z!f&N%l5rj5!r4l3fjHS4#xeJZGkgE5SxbpGvHxB8V3Q_Vwar1Dp|XmHoMpOWI5p|1 z)g1cZp6w+T1L^$F9+Vo$EcmN16IG5zpjYY2Q%tFNTUenRLt{r|u~pDCDc}e}dtGG? zlTcl8UC3>BT__Zsvg_(6aAA89g1?EEL^Q2~jjS?U`gzrFE=mYoQ{cx5GsUz(Np%wh z{u;XH#K?@KhncUk)6#O9c#<4_U9n@tQqK=9b`T6-hQ3;ORZ90le?Y%6$R!_!a0-shDpVW@ zj7-Eu)5_Hdtpv&}hEjVmNvX&unKlB8qCDmmq1YRf8}`SyQ`P1jUJUsPn{*t{Rp&b9 zWn5niiWWt#O4=6ZE3PNqpHI!%a&Gk1<4aQS?fiWcm6KPdP&E4NzGMAbFZDiuVEP%1 z&k~~Zq*8R7U}-aan^Dg=g76Sd-X@X$l(t%1z@5-SP>E>w?5I_FH@hi{^UXD0~ z-mIipVe}_b2k6} z?-0Skg91-(gB;CAhqw^8y6#5K`ZOm2&vygFUp@A%?gxf?f~rSKB+Ss5BfQNS01D6i z93|z#KP~1N`Loh#wKx3p?vF~7;y|)rUV1ssF{P2cb`1N_Dm|?3eh}U?vZDkUVjGb9 z2=Db~G{7|K$heu#U~sH_nnjsKwftxCdSEe!X;h7A5GrA%$)#}zh{zf=^IC&w5WhE(Q>IW*Qu)=_ac9~sOPuwnAgLo%z4c#nl37f; zK{8OK%h?ADbK{cR;P-9d7Tfn{}e(UqOTncS^l`Yh2D>_VW_CV^>kBC2gqI3@aM=6U6s%u}z zmrfaaJDoxbtU=Yu(Dxa5{TFbfJNzckjD{kyDGX2&z7;br9eE6is?OE)fMN5z^bU=! zBvE6DBEM2Az_814dppkK`cTxE;L+=^yAG2*buA~Q8X*L}Y7oYDDZc*M@i&c&e;289m!`L_}q)xsR9) z&C7kX-Ygauv+LI4IT_p!W~XWKJyi<|t5l|JwCjrKYb%^uPFh*eonZU5?_O482Z4j= zf?hCmSVQKa8dvvDS_h>5hoq5zCZ0|-0)$QY$54lm2Z^}HX5_ZgB!o7GpqVbT&v7yQ zt+gg?DnIrCFuC?Sq{F#<2y5|nHOt{zgSbq`O>oY0ev=74xwM(M9+J`g64JkoAjJA$ z78r)`lqV#xvS#oi!#%+WgtW68G5Rv4P_c=6M1G6-7`gA-pb5G1ppO9AfE?(3oZt!P z17K_rV=N>raq1(jwmS7n;G~BRcNG65h?<_CJ{{E3J4QK$yRf3yUxhNo5Ha^IS+m1n z$T#&K55OAfq45`nHOCcWdV?zQVfcO1EsQu*7^ks^eOC&?75u(5$GiyYY^P#JlXOeCIM-932U8wO4$*;iJ?J`TALTRaJM`(UfUUs=HpR$Vg$eQ=wOXQ%ZV_1M;9;YZV`I?vTtV~TlaV{ zo<%V+H|rO&vR|?npAuzMy5Jv~P%v~9L6){o`X|Dd8nY@q^#quMK?RD(b%gl^l2k1Y zM~4-wI6du+ljBP%LmAIF_jV(VE&>nIS(QUuuRF!H{arEd7I8z`?<#FoJuJm!ADHiz zkl7XoB@LJQ_vl9Z^+-IzC!veOSmsil?pNzQ4Q#}yP>Q*0D)Hhsx7s=Qx{BB1sYr#V zjIROXUu!Btyb(3-|KcDa!LhE_sna>-RPw9wYiQPbaQAaGw!i|YUkOwjSC=TOK%o3c z;EGwyVN3hclOAcMe{tGp*h&O9Q=eJ+t)!_7Plh`0zC2Mn(~O}n^y=`- zc{vu-X(X=+K$egJ=lkuDnvU@jeAhIGoozjWx~0)Gk;*e9U?i;a=i7%3n|q3OGz2;6 z7~D)O&`8}D>oh1d#>te$JM}E`m8RNoSNs?C`(JUJl%V8ouTP=?Ncgb{VNM#m zmQ~r!YnV{x6BOeHCx`hZ3Pe{6SNXQ}A zvky1%l1sSuV&oHxl(@V6bnYf!yt>_B(REQLK>_NTNv&4kYeXnUN85_#Ij%B0Hs^i{ zA;|y^H}(nLI@_ROYV3yYrP{(U;cNe4F%)Ye!?S&7f;<{#998`7_|r6upS;teL*oH2 zq2yn+ANmslbIUh54BYQ@ipAC9ZC%tnMYtOo`eU$|OxL2qd9_P8)YJI%3%!}T~J z<=_zdZ}Lw#W9 zV?l%SN!2G?zwD%bwdxTM`XN`$=wF#Jf*vY>bS!OYuh2SQfL`WfFEbf|RXZugau2{U zO>?JRc)1~XZK1^#d*NeC{!Q+*K9_l!4qF3ww}(9xo7q}$` zJcZeZ1-qcE+U$-1UvvGLxA-VRPgsUMqg7(nvv*3a*=046L91@Qu9L5mQ$F5Edvt?B zU11=tab6ORse>1ruf2x>H36jPT0%Q1?n-+>=S6(W5xDN}5y)pLr8%8MS>fPR)px}j zs*5sx_%@j6)iI$s_fd$D@)>}utCD$MHWDFH0s$pMxOlq3%5>mr@7Gt>TY!_C2DA&S zlM%bmjrBX%XAm~DvY;@BvueObLPCvC{?Rn)dwG?G?w7b<@&tVus7jy%Cq9?SW&9 z*?>Z(4J?!CV&cyZlh#ZIf?%C$3;7YXis7fPWhnwo`4?-qeR!AjM#eB3#V$vp;XQ&9 zWlf{;Y-&UOO)R}>u0$7u+%5CkH7e^)Vy5<>!{h>&i42=81Y)lbdNpO2c(^b&rD9UI zht0JAk)?SKN2n70=BJl}AFba}uJ^p_WG>>bZt{^;VOAZM9+6pzpzd2KthbBI8oXDM z84BO`r#Mg2vj0Qg)01(rH9$9H%@UUR^u{otV0&E88W^Adj=`Q)FYB58$^%e086a?B zHFHB$-R|4or-vV!rplMI6I2GfL=ZyEdVaZ5IVSPDZ%3xS zJ-8$TkrI0L!eZn5P4Pir9=xmbnLnwsQC+xNILrG5++8_Cql?cb6_%Q2M^q287hon7 z>93W+lQnr+OI$Us;@_8@x(WmJYy~+p?jP}Gv1}jKgGPxT4$b4F&cS5fc$n$BlD_l> z-C0Bmz^WC-@ie3SYYDtSNn=WU^DgQHh-9W!k(tk{Cyzf3U?P|p_N>JifJ!`{0Z=gp?ipUQ}j0l7%&s zQ0`G*w(~ZmQtlT(k$f276ZGeR4IZ0OEqg5R1_2j;y>ZbUNrB&+vXcQQJIXX(u=b|J zf4(K)D5_Rj&@Sco%ulu*UxB*PlQ3hGRXPc-g0krNYuTx0s3%BqT=cC_*Xi|3)fm3(me&w}2%gZfMk55KU7R?Gq{0>#w=_?e=#3)VKNGO2G_$%c$0^APoPi7drwx zkp2waP@LSGNvSH$KS9hR%i%3mj*U8_!=eZ8?U6f^kp00O>vjekvE<`QcCLoG!Fgz~n)x zFCJ9Ih+{$yx^s?%=me`MZ@o@=FbhI^cYCEBFl+~f9_4~E<6bI{H(wWBq+ZgVCd_`g zyIA;Qp>X9;GG2;lBdVq0S_jTXMXoz5dZi9wG4$q?-jAbd40l);%{^f1=AhC|)-5Ms zR1kDOp2XUyYKZwcbn7JjAmgz4R~q_rEw&!$liki;{ck~;V`&yCTcTDGT%SdMw`33f z7W*$p$vHq)R&eF}l}w9UsoXk2*yJv{ef}Bd3uFW3XJhy)J8g^b>qQpw)a~ zbI%fjqFmN}y|2V%>u{2>F(!TGD z)HolfqE~$&M(y|AUg`WKX2D^yfpE$H>Kx{9!>*dNK-zwH)gO4Ux=tD#n=h1U-=#%ESBx_0A>PV*he-osu(>GYZjNUTn=CDb1)S8 z$M5!HUHzEjWQ7~fu;4apyJ;V^dEbg-)j+A}+tswv#ZWQPJiH8;l~70ZwR@ya!+)n# z946gY8II=-xDN1)?D~}Xs`Ch~J9h5^y5!Ws#Tp8xOtAk-F?)s^*7P{BvMstdsBD^a zLYl}7{t@Mw!F1fX*TCv4Fv5H9L#Ljlk`Q0fe>*}m9^fRvG4*V>n|s*aMxJ_;pm&Hg zPC6~wMSR?BS)7pA24f_JH&eu}`D6kdr0!2u-B29PdpEZ(ns}LX^JS6ms&BojbVQ-< zdVw?Opri%j$mE;vgBiQqcyCX!c_z+x_n)Eh8dY9*s9plA0A?fpU&F$~6WxynFm~yz zpq6R7vvm*=bRt;;yW$j?sH}8hgbsOyE<$#|3Q7y=g(a0#8wKD&bC-eJD(GCIpPv!e zCKEHs)v&{Hpf8}31kV=$qa0?PMUy_jXi2&Q5C@NWkSEIUjMZ|IQ2uoGlGxZCUt0HG z54t--NM&-SJB#|b=ql=>D8L5n)`+m!wns0oV(3o)`#22i)9 z6LR&@xg3cua}!1?z<;fz9I>tcj`rO#E{p*Dujtd9&vvF`yU|9PF@FH)hI}_}hAL+- z$SAF{EAjC%q_0YUhX-%#71mF`Dv|leuTzH2Ftag7;|5O8(dut6x7E||kjW2dm#I|a z8$fI7T07F`PIN5f_z9waL{4J`Szi}yg}zYm^)!q*3F{eO-^|ZY>f^fTbiy19>$|_x z4lW)kf@S2ETv4-&YuG{^?L__^tDV zi!$wK-dx2Ev4#BIP45{QchX;0xeDE0wGD2w`LNU0G`%>QXPC>FOF)-}?(*M3&X-T+ z>O7uJwTedKmUZyL5iN8t*QQgYKBqKX^lryVp07`Vpq<(6B!xu(mZ_C2j^C6mz5v5* z(h)pFd#>z+T^1$-B};6TC(^d!eI6Wu>P?$;Z6e!OnbzKXm;SQ5pM(fe%B0%aR4d+f zzUJ(cf48@P%Yl_qRhEPq^z&YmBD*8`B37J{0;`sRt~x@yS(`QvSE}^3r+nb)-BmW- zT!j8A3WR< z#FHf_OfAp}@#-y44N6|T7<7j}h%5--#dhCQvG>e#21!R~>Q7-PZH=d4dZ1e;NQe0X zIL6nPVYKqc-+lV(X9I4=(Vw_3`7&ZnVRXI+^-VLDdvdNP$=HUdTmtrUjH>BpI%oI# zyR9PEli)aryt)g&(Tl5K%#?jzxhxS7+sMo~IozKz1F0`Z7Phy{zl>6qj zS`yB~+CB|!(xZP+42x4;-!eX7*d9)-dY);>lp>m`Wb94b+B zwvn@l*DWr6E#@iXp)L7OlTE(ulYgEcWG`a9$vI@|OC#ttz56~y_&s>95<_rHWv143ME{ zmm(h=jC7{;{y|W9R37y2{w4}4#|hq~er%!vc6`l+F@ow<_sAc|@#Q`Y>__YVx9iMwhJ4zjZJT0u>vyT3xA;FBq+(dJ zbkavjCBTUhQ=k7L;o}qLx+Fu;^zTO+9mxC!%TzHq$|Q{n%O?|RJn zGg^LZTV^^_(c}k%e5K{{&82fP*8xmTNDk)l5o`u`3IBDu?y<-|UB43?=@a6Mjz0qJ z8BI(rHqn$Y1i7el7_s$@-8MyguwFe$doMroWeD%}iYJ#XAzd=z3kinRLu%JQzR>rB z<*7BVn9(p1bIBh)!aw$VzJ>Um1`Oes5GS)=;j|Fp8BAkzdQQaB4)8L6GVOKC7TsZ8 zw*!5}U%5bwQRdwqd9%aU?oc}`x7#%Z<-b|uYfXl_Y{{7j%w|5bN&IC~BCJiLABPj zU9b!a`{twdp%u?(^R5&2hhd{R^=0c{sg<1!2o=*^TjXB5HTOPfFzriVDc&$LE8r&Y7n^F$@q;8ENI?6KFqi*0aDnR(56W%$|lzB(%OT);2(dA46><0Xlc< z-4*!F4G>H0sv7@HeIb6N+74a5ce9s6BdZc7l`^9X3*E7*5U)H$CL| zUDM}{i5?E-JZg93E4K;~gRO^Up*r)FiETOB2oJ9)7SG>=*O+=H#1l(j`JVVUiXKyd z*p`jb6%-_x*P-YdLGRODt-lD@7UG<|FTq=i#AF+J=l3UULKxYW>vi;egwmQAI^~pt zxi?;~uNA4-Y1j)jS^o@OciBSldJfyoJS!**J1t_3eFzm%XT0k160YXxOWf`)PHSY= zR;E52|5R(L16mU(I~VqE%SBBocfLp06aSefQoubjy@OGX@2#28X+wR?fcCP7zRyy4 zm|EBx?ZNAJScVKv)c_R475$*^sD2O^8aKvaC7(WRIAN+=!+N8p6jd{l^o`mOxY+sm z!<3W0#)nLyH$NqUSBEA@!r$eW6rRp^DS>VN%{N;^LF+km(~(*A+L4n7Uiv-O`(Lf+ z3;HxrU}4(6sDAlc-PzBTI(-|IRS)a+x4U&s|Cx|JJe~TA};ql9;jo|S|K$5F- zTwE>AVX8bt&}Z9hWcD~rmJ*DqCydt(PLgfn2zCl$*dZ|?4oE|nU^5~#qHSkJqsoef z)~b0(T(j7dF8yp(dCTf7=*mz2{f=~DBPg?jPR~<{YEM^;=oGhe`iFq)wU1m^F6MEp zq~?!X0Ie|I4wW=F5Vmj93FX7U%Nbgf703S$pg`F$pAH5#Cu(@4T;mNsA^wL~_!453 zB{p2WXPd>fX&At%aT;GlRZm`)6z34^3aW^84r&?tem^XEJ6^%Yr{P~}=%vKEth;Tg z=UU9VT|sHYbiy9&p!4+5y}qTFX}kA4flnwyzCcrd);R41xiXL=PQE)JC^%>#cKP-O zGUUo|rlD&^5vS?riSkR{h;^FrEyQ|3-Ou4Xhmz_|g!EK(6XOP%TsKrQsm?mj$v5Ra znTe={{^6X>*}1B^>o#sWENFQ!Q!uxVsO_phPWzh>Z>1Uh){=^*kPx^MF@_sEhR~oR`uP3$wnc5UQ3v zM|P%Zmg%KEe&Y$(0$PXar|2MbmTq1Qc&AY3uUZwQPNseO5c&$b)IQzRvsF&E%%Y@R zIZ!;5&69ik2B)p5;B$I-)@hyJOU}8oJ%D_D)3M}#bAEUbTK6>SuP9~B-_){q0G6>; z;iTz9{Hi72a4(4T>=Z1Yn0{2Fn+v&sCPzSY6PkKv_xpMpW(DWJrB-EVo|<&3PQxDr z7T0EmZNMgCf0o!LASfDtYxY%$=$IhN@|v6NwiCCNa?5@mf@MQl-84X1;z^JMK4QUEn8`a?8#H$ufS zlhkl5QEeUicMMYA&6t02c9Ng_|)@E^3r@jjXJ zdN5W`^Lpd9B%MI0!!$i@ki3RkrzyE5bBvm5hj8yNM~lyHx!MeiTR7xW&!BKz*P?@? zX6N?1n`Wl60Rig0cVYx(&361nejhXr)3G(uPL{VIxWNaVoZcOa_J(8z3u1iz+mRBc zuAcqz@t_lzq&+agrLNfGYV$zp_le%D1J~^2%c!Y=%5Y`D9znybDfj^Z&47QzDGM+2 zCop=1RPV2Nepap%;RK&j{H@&JR;P0k#&{2LWw9PWEDPQ0Wb3X=2?0)>ln4m{;MR-3 zl!u`3zF10;6wITV_qknfj$BV!Ec4Nu+7l`L6P_V9fZWjc*q6vJq8~}R^Izg0D zfjNH>-aLi6E5I6>4lCgzQj7}Ai>N2)%_Ks}ZO7Tg#Z-r_#1)#ra+^DX182UZ5`K>B z@&8V{L$iV9UM@tw;Jd1#&DHu`ahW`?dyYEGqWEBLPhbhK9;5NAw^x!|o1PUI>PB{_ z(#zRgexkXiRkXSHrrErT3UefIKb#;{KOZ^tR4v zaW5S|KflaJ_iV{Wsz|_kqqx#>)i?0ZdqDqZoWNRutQuwg-qgQ_MZssM@VMH62y+|I zGUR^Y&e{xPlJ*>`Th}Uu!$kL?hnc;3TWsTmfDX}&4)eX-0!G-(6fJFuqe_C?;}w{x zZSkY&Iwlll043e6#7s$?%C@I0(>|7%hpFHBG=+1PxPf$~w->jr;5H@y1XifhaZVMf z!}9_Wbu%*(6mqTas4zcW&hLP4!Na)0IULg|$~{V%G4$}cKfozQBkX~uJ72$}{d+-; z7gXVK8f!>27lCQNBW24vr;Xvvm!#WMrSBntc+iUvNqfj5YPIQx_^vmBOO|lN=f-OC zKq|(t&Q3qR`eR;>DP~Auj;ZR45?GlOlCY=(gwUOqj!b?fgHGQ|1Q2LBhYUMkJyT;4 zWD|+&JBjyE2}~t8sO0E*8c4Bi$hQ6Hw}-*L3>kG{`=NUC(gAD)oVwoLln!u=X=L|U z2k*?yK)WUh_p@)0-S3m5ho#6o%0{lW+$JA{FQTK}A$*ftdL4wr zEc%h(_{!E-7Kvd(fVxt5cdov=+fK?PMtHh*qZte*RYgMM+>x^BBpbV>_`UcTc$lyP zW8*I(h%u+C$Opl#=8jFZ^Wn(@aS{TAlEU&($>RiEfgWk*gg!y3Wq#aSGxBFfIwe09 z4*%NQ9RKhz`iQ@L_0eL#N$>T8hU!PG#8iDKs>CEa8vDQh#(=)V=h9VxH0|1)moAk$QuoBl%Rp4XJrdjk3EufNpBfpa`nRQB$B z5g_Puf9Ky3@-)(}*>!v3IF%kj@s;(K%Q63_U9dSpa{RYBdQ`y9TM4?rx}CSr7s-sx zxWlnAx_LkX`1!&_#ARVlBHB@B#A&^bpHv3TO+EjfV%%#DRl`8IfNt^E>-a+b{4qw5~s82&xo4vV{m`IRLmam{O3{vk6daO>`}Z~m*04X0Y(+Zix7-0^K!|6-BK<>%c&$x|Km70Z=3m>O zpXiH_EJ5!f{UpYZdjB+T@*=kf zyaChbmeOfnV5`Z^X22CL&lGD*)MZ0uG#hw?ofMvI`eW4d7~$4Y{)T_2IBq?;opw|17KN(hVKnMc}_h zKK1z3;5<>h{Rrb(j%gUUC)Q`7$y0W5;fJH~?*Y5VzQ1wt{nMWT_&^F&J8$9fe1i<8 z?|Gvb=My;Ze%diW_DV+TM;b?(rQEd0zw3@r6via+(K{%$!!Tdg)LAr*s^ zj-o7_wz$wSs6AxhbYnzf3l#nD_SAvRIO+4p1I-r3bm{v?et_WB>iv6K_G+7HE+?xA zdU|@<_YlW?5CYJygD%KpkQOUc!EvFfP0nL^ismQy|1t|Pkn9 zfEgvTA%sC9fX~>QxrF(%KY!jIEdXc^B2yN8K}H35#=Hf4da++QzU;OO7DtUHngXn* z-+;t%Y#TVlMs;vedU$GQnvPz%>dwQ*85o)S9&l^O|9q+>*-_}1zC_B*R0|Xk@J#AJ zz@cmhu0QD^0>t&E&@^3`5Ni+$VDM;6e_&vrEVekHR9!f)tWX1-?R?JesP|Eo`fW>40q%zbUVz-g zxbq0G7=wsV;HNz}1iIA_AcagGj-1E3hezhb6FE^oMunTCvXtcK=f4FDs910ypR_t^ z@)TcdMlsf{hX3;sN!2gk$PX?c5x;|4oxizx`2z9cCF`=R((>Rd>j`jUyywx{Zz#MR z$eN6f`N#V;_zZjF6awK*GnK|Y0N-5;V=9?~pNU?Atx=FR= z$^7E+kMIguQh>sP(h!3wsfqbYX>$9SVn<|@lwe15%{Oqumn}wJ;XuNUTFOl{*m9Ad zLpYand2~;zjf`yr?LXeb(BvXL50E@})%=h1W^NnL6FtC`%!Jnj=xELffX8WcDg)v(SJas3S zGlKt-^*zG0@R|o7rc(B|h+40EJhxWwO~OzLALq^cbTNOwch4H*feS|R7e_Ycpm4&S z$oXEN_9+6^Q45+5sFq6?YAn^|W|AKtGRib*(XC!HnFmxcI!ao$v3-Hy{YnJHhg1Si zIR=2w!MkV~6jR)!jZIC#yB1_2YTB-A3s}^CE^w4CQ2!>mQ!C27WBR{Pod;u_#5LY& zmKQ_)1*o&Y``g}3zIgDp2==OJ*R}k8VT4a$4IB_Xezv&(Gsuahlq+I_ z^F5q09e2+eY@6TYa|Vp>q(Xr#DgbX{b_QbSCiz)hN$@(Ts3BX6g@;pnef?!?Ki~zl zs9g#*H2}^J2q76oG&FMG*)LJ7ynq7E7E;=@d<-oqKa+4m_y7Fg22K&9VZ@c$gCTut0_yPC>Q~-QSF2D)3 zE>J~@WCYOLH|ev05T;q_eJ7kgz$;PKHCRI-5$WmaL?dejwS5@gUxtLoX-H9I{eE_v zQCxmu2RDP!_=AtBY0TG+j;C{bSEmvPdPV&H6)O5o+ zNDW?f-Ba z|Mv4eV34iAvq`cX`%x-gn^*F2(xv-RZ1Ua3PCsjuL4+7;SZjCHbVd-=a|EDB@!ke% z&P?y5@>yHAi(+6z5aYw~D*#5MGw{MwWRY$3;(P}F+93zFgcY_%WwnWqqKdn|r3{ns_&f^gC-_e6n~s zQUAlh`g#~zrR&uhv=@}dzC*XR!JYmoR|<(hG`V&X0Hdfp z&AGL{W=b}L9XS2F%Qt%G>RAmWsb;T=MoNgLt;*~oIfx~r-c=ST#R>s!k$^`#`n}&i zDIe}A4R-@kcSZsX86FMOrP-2OF9tkXABx{m+PL^ITkTzQeg@@ zMbB-X3J~X&Oj9{FF#O};47?MG_G4j)ETU;~%ENBMUMVIdkaUneGkFfgFI8U=n zxk$EMhcmdh1oHQ!uck+e(6^;#H*dXx{DU8{B2lY}5eFz}w}$XMG9ZOt_6p#i6|=^9 z`Ck8rx#H$lSn^j3y=< zPb3FiAFPC%>+T$Y$$|at7b={NaDtY{_$~MCKC_ZR6d-v_ei?$FB53^~IJFZ>M)bMu z#Hful0H7_3E5zT0Fxh?B1zzwUX-VI>pHGhjBeK-9WOkaIP+u8Y7#cqqx` z(D<}`0hp0rkN&Z#DErSW)R9^<3QC&U z)M+)wd2el%W)c^35_k3fJ!Zn;mLnS@&$$rN4IY0bUUBjd=(Oho6Np_IUwF z*nYY7kR2tdreH=G8}r^{1>_lE4PdYKv(9T-0GM0hmGXrR2JI>TjGUIYKZk(=e`}wK zQGI%;dtDPzC0`t!CQCozZ11`PxSXWou^15V@}CDD7tj@oD}uG@kwhB4fC;B~O$vy~ zEfQCv1%@l&Y_~zLD1;>y=;-{~zE7H&v2R5tKgqE%NA*y9;@7F{HiBi(&S(aI#Oeml z=`cI6-$+aBAkQc&kU41<=nwL?t?@h4m^{sRdo#QJHqbk}zll8k6BwN_SnV)!wh5I; zv9g{2gd8*dYdk^PkNbUU>|kHhzAbE*ml9xwEDe}|0YZyt=}^D;7`Ba(Sr6VwW~qqM z0UB0v_R`d+uquLmd)&c)7_NT32+Y`*0lGgvf#k9FtG3SZo3{k^%zY30SbmBg0hHE97y1J=$!^>|?e4?( z*J}v=EtebT`h*sSVmJ8T*nA{@wn4PX9@K5sf?rlWkyX(br0m-G(K+HR$p~A`aNEy! zH=2_-U7-F_R>Y)BIATX10Aff(^jOEV59Ihs2fJ&y?-2&}uhj~)QwT7{YY*%~=x+BT ze)g@tGrM{PVywiJ9gJVQH^488!u>!z3bHcsG*lHPh59;~EGE$~x$!4Wm znN0cw@L8T%NDi4-6SVN;nO!MdLARx3oO^lsN(!$n8FXK*$anWeQLsV zP|t9mW%;VIxxQekk68>FsE!3`Bt0;?;iF{_b-)-XeZ1u*#Grx8hQZZ1?`Dj5^ZdNtl6Ool2G4?)@15365Fq^=Q`}pb^zD zYQi^T6HrR1gCFfrqxIG(Mwn*1sZ<^vZR?+pZxG?ew3gDtzP*k8^hAe}`L50NR-mpJ z=ftzw&hMRn)@;99Niy4&ON-FB%dd*4VU^s|-DL7*zvE?h){$nhTh%*w?RfUq(L^ko zueK*{LWe4ISH!kHZ-!0~f0h{XzxSH2z%p3k;=@sWOXo#a)=e+*Avglqt0$VsIN_I` zUxDzDbiWL&4|ll%9=BNw*A0KTZk5|6>g}gPgw<%mYxtHm59B`m#%{arg^woKJ~%Kh z9nRY$CC54ML{jmi2C2cmuPVnH}#<;xFjeEt7H62z)5K>_4Wc_;E#ER zJFApx^By!itMk4Z_FdW#SJ`k6;9lA_BTHF*;Fic>|Hrf)IB)IgFyL+QYsFD-K)Aw8olAv~WqAXu+{42TH>K!12GP|xX;QC1VuiK0;+)nS4 zwDRkfs1Of3uPsF=*DM-8*wrLil6+*%P2sB7nY{ypAtT#ZgZG*MnPIZO z?&3SlP#5(KemmJ_J#I`EQ_I>MXtm=C5~{l;AE-+tc}}+X`+tNDUF^vJcPJPj*ZGx@#b?gk zeO|v=X8)WUHLuLXZqo*|rL-M2>QnQ42i-JtbHCMH8D2Sh{-~AnuA%;B9h&O6*lJSv z(pWGFF5alU6cQAvowuW6dVB7b8qFs_h${tCg0bP0{9~-D8tV3|hrQI~^g}(6BE!5c zPmFVX!=?bSH&n)ooYGe0W4dHO-9b21%tJuehm62lAHQ{Rwhld=`Lftl*({HMPIJunlwlC~l{Y_=UmTRE z%>pFpjK7*$d?T-JzBttb%n+Y(X^8_qPdY8eM}c}Qr^Mpe{faH|_xeQL*-u{jRJ-M= z*M$>8bX4+*w|{urtLY?@-`7y*o$jE){SJPD`^L(PiG*el>(N!W&0YDXOU03%iR}aoXMkqd4I*u+00-Cmb6UNJyM2e$bmO}KqQT|qHYiRlIMYRQR^UR` zB(<@~6lWY~ux#KQh=A-V<8&cBc^-(u_5bi)L+ajT#*Mz@?1n#zxhHOG#PdAcddIb; zk|6qP-;~s-5_KrDk8c=?pUg0y!2Fi3Xcqged>0h5>#e++G(=^(v2&mJ1!t+PwPC4q zOES@qrshzKbfK|^6%li}t#~C`75|P{2#WLub$LmIa-u=JTTlX%H@!m~sJ1?w$jerL z>Z*j;gR&N+h_Xlkkd#liO$8r!#QQ%YsL!MUD9evl{$z?M^~alDWQksW!y*Y3bJ?sb zb83d(pKG|U)N+0X?(lfzWk+ivm{5}!Vu+$B#(GMp<8Ex=mw(SO#~TwjQwL^t!q|Cw z7_Wv8j~^_o)nZ)b_pu}HLwGmu4~N#O7_m;CVJtqVm2~)izGPs@=}eouYo}y7@L}8U zB=6t1so#$iDmVv{4iVG*3C5EnD?nv#=Mykez>p^i<0mMt8=ETcEoP1=D;6%Wd{t*=G1#rp!N!Q0kGX=)f~X7nsW=u2 zdklLKsfg_DP>>$;g6Y%UY$zpG28)C$_cTQgDo&X=k&q?EeycBiB|DA%gF1Zu`)~@|U-$>Px&eJ{z}4)cX2<6( z$eY+|l@C$9D3_4R;Y%DRbwQnnL?xhpH87a*@#ot}cWZl>GWz2mD4551aMj$VEsO9B^YL zEZHzYeWbW$xctd){Bdi<2y}&$*9F?WS$FdVmpiXM%iDeGIxjNbnmc{=@(N>jP^7?- z`YLm~7<)hmN2&F+R-}lrvIUDz}{7soA4n>JAk4YWw@H2uvgePJt z=ncPI&(&txIt8m{0hjhhlK59Q#t1`{SYpZ@h*_`Mjd}|~-2c&JN(uFJQ8Ke_pgGM} zYwvW~z%pNP(Fxh z%!;c#ba4}C9#=nQ;iVoR4e0_bB@))}3+Gkc+9`N`H<n&%kXWvw8Wd)&FFQK`N235Iv z2irKjVheHOdLB6Mo4zovW-72Et1JG?63pF#tyzmRC053x&I8Crds! zu{N2}HUp6Zd#!)r5KOtyfp7u5!Wi<7TJWNVF)U8PO=wx4T$t5}H!+ zEW%03cgNVf>KT<%{>%h#QL{EuM`_RT(HIOFa3<(01kr+$pZs?#iP1mE0&(ipOc?|D z)PjI$8WhB%Sek-(Jk-h{R#~@hAXEhpf!kj+4D&kYaf_~U=n|W-8mt;o57_X2u4{Bj z@+R(}yW!&#TO|!awmY7!wJ=i+)wT;&~n_4qSH)5}E^x6VNf^1+asx3vIxs!IO{t7I< z#7jnZU2-d1aiO5(~ zczaNWm+%rlV> z$62{5^uuPyY_$%iwU5^cXM&WSA=;ZSX9&DwEmAFl(Q)V~hfQjE%42e36C}U{qJ6!y zx+-mVUG*0!pTVc|33lkV+1Upv_?Ol6+ZC#_x_0so-xG-S7`RT|xPR@*l90LG#4VV; z3R_K6#g)(l`w0JW=B{i=?IkbpAbIgt;j~MsTTy2{7*FS-!IG~G&X}_06rD!N8V~L% z50m5G5UoPS`&hXuDC}*?nXcrTs&y$+%Ybcsps%1fhI~~tVIH@N*YYA@bD}(jm4q%( z7$uaSktX;EXPpj81(oi43?(pSlIguq6O4@!qAZW)+n*fEyUoO@M>bIHfc0u3g}&gU zqkY6BlO=NH=^7!amLNBy03*^b#xQ4X+GYl$Jpm>QmzE&>%3R`hyL$b(*LW-Jp1`k5 zrdnm?B9jfZt?&({zq(Sh2}!sE^krr{t68-y4oZR&S0STOxr1FHTZMi1yLiI=Vs7gRb?t2M`=J4}Rm=Z{?;RVD4d4rO8>4t^HaOGKV^|reSZ|2Iz{ZuhGT;Cd8o89u_eyxF=?Cd1`DbQBH=Ej zEBNdxGHA#}8$$m3YyQQwbU(8AnLqsXApf!Cdj9E0%p4U#n~WU-wA^mTq56yte$Ve| z#53js=#Cx7mv)Y=qiza+odx1*pyc265!azeTtkHX zmjdI|=IQc9x^1EZuD`5Fc$tUod&Eyioypp-`SYUdx;%U5OIPy57|{$G6xx9Z@hKV^HT1DjsI2_^YF?0FIvbb`bKtRsko^8 zOG&+d7zeRAe`uD%4T+zB#3)K&B3ux2#x7uMSKsmIAYc5ZJ=~(@by-QA(Dw&e@2*?4fDC3~{qLbE%X;P$KT%qJ10sH>bPXhd&jTWM$%Sg8H?=Jmj}HD#q7L z67(I6^!@sB0aKB33ih&H^5eG3RIU5gc29{=@CX<{Vwwa+%`bHb`H_u_eetr-FXCoN-yh z-SGv2?XK=}t^LsB%^YugIWEty93jPhi)Lys3GgGYBl47PTr}*oV2D!9p)~17g(bUx zP^HHEfk$CNd4EI+==XJnX^zI@`RxL?0MHI2%h>4-`N?Ee>5*Qeme_(S2U`qHOkg?B zLCZV@)G+{CJUuAzP%!+`m%A$<+Yv@)fvD5r#+->f0_Afjk{iU@VruF?k7gRyie^GR z**d+MZj3E1O@V<#J#BtH+^Cj!P z!0qpe?Z|f+uqL_gXJQy~#*@|fn4yd~DxPLMSN%Bl0b*ok5zT&MZV=3;eN?s^)JgOsU0n;YKR}3O{ zRVr{D5N)z5GG2?aHs7 zEYB;yZyvaMP=-}T&S6&^*ITmTjK$u?f+fMMWkuOKYO6P+N7NO$v?htgo@`?XAs{Rw zc{333(;8VE48P`lwN4 zIaL~4g*a25hjMjll5BX3mWFb*J<$Qecjf0r=%|*_h9>T%QTC>ULCzXm?Buu_T<4i) z?U(s3`}rsQ*@T}?m`C3-%H-U-WS4F?LKr;@7Eiwc0qFpYu8};dj7iw(ODczd*Av%r%Syi$0$^$@b7I zVBIUhiyKpHr97Wy`lW`evHg@8igblapQx5ipB!q$|BxaDXT4TH|p5(yi)L_G#)Fvt+?oP_f)r`Y0mA;+^7#Z?zJ0Fwl5^Io(3pOR8!y zni)QuiES&7aPwc}6H+^tEVRh7&}#HKmK6LYwK`TOAn>oQ%bi)_eF16LyTfC2+TyP2 z9PTx0@}yp(Z_Hj5&^{=Vh_|!9v}y$mjt$6lwx!Nj4eb00oqKn53n2 zrLmvXF~5g~*YZ1n>-y*9v6ZIz$8^e47ddr#6TmWbc`K@ztpIz2zCf| zWxx6}xl_-G-$ws#@Q zV@2~2rJ!3K@tSBxJ3}+MNpE6td$onzEhB~aw{nKbN)Cb#YXh21k%i58A_xx~#b&cl zq4@@sH4a3iwSSK>CTRTr{Nzc``-5`73i6l=pgb8CO7+gzps<{C%hokAecQ1D4RL$# zsvTI*g8vILA2#XeP`cXwQxstxq5<0@lX={u84euazk7INe>{y|tew(it15>KxYMR? zi0IGSfnJh5+qEkC5%KR$OvTk19$3{t1Z3@!Y+oC-uxDvB=O41xGnLg~_i z1rs(AAY<^5o&bQ#Q>q{$x%WL75;CJ6FbJdGEI#siG$%l1WREcw_fDwXnH zK8E?de9REa5S$Qp#xG%d3PXJNifS9M~57t3+<{;Kf{_f z<>tY@uGx&6kdAl4Q4ercXXp$i+-vmNCS@)2Vp3(UH-l!H#q)aJq2eaXeYOI1bq9RWX^zN1lV9^po~HC>4`v6Slh zAPs2FPX0#J)E3+4OrX`anV-!=<;q}O?Ek;d2k^G~L<@LJpPDQ0%W`pJ4->*Kv2QD) zW}M5TUtXN<&Tj9hWiO=23~vL8YsP%<6Kc#soR7A4NR8Zd>bwtDni_JyyiiFi8HklI zgNj%Jw-}TBoH}Kdc4@)~Nf3U|c+W=i!^$%004U&@Hytm^#qnnxYQ$T*OPd5>pZMfvp8D zNnOv04$|*Y^1Rj6DZ_Ew5hfNgkgRlq8RPtp6nuFM@MfFoQ*m-N2rpPem0vQ?S@`Fx zR%J8D>FbwYW3&+$)(w4qx0YDbj9h3cB<>U99NTC1m*uK`pmNOv9;vlc6{~W z?C&%*{Nw_`HJP$5#aIZ6?FZo3HdlTq8Z2+_`kt6mS#vM(cq>CKh&~~i?0Uq(o`}e# zR9vjs3gl$@S$w~$w}Ed=%E{z~c?)iKbw#yhn(DYCo2915JRW>YrJ0C%sMQsb|BWc} znJ7>}TqciDazqD?-fGknOrLsp@St_qHBTnR83=0@qjgYRv#(bYsWJU>T-0>}_bQjD zm2VmcegnV}Z?V7e_w+rLcJMG5sNbo2&Mj#Al>PXv`q*-L_8&n|s#i3LO7_w3XXG~B zCRtx=Sq(EhYmBA=sZY%C6P#6OVtuHQFJ9>3=YiPkz52l+`>Vh1aEk>rnFAmlzb zl)zSG3tdYr$@)wEw%@J+2^Q95s*E4@VIpu|A0Uhs;_}EPnOJ>OnBxsuUMQa5XcIH@T-S`KN_hf}Vt52@fd5qZRU{?3}Fqh;CG? z^z+%SLT{ez%93!7<*AnSof@Xpt-6Krp~Ng=1Bx9s@RE%IZ}7=m?+)1`U_4xk_D(x-cTVmzD;Ap zTdU(<`=g!=jFOETdHHN2h?3C)5akj)k77w#x!N)Tion_jIX|)2x}X_uoA*(l?Ozgd zTyV8ZIq%q3YswW~=fUq4IhOS_M%?TO^%d}y@754d+dPf1iTdqeB)Zx1BJyzF-e<;( zsx~$-i>co?lOuiK4;cC5)1K4Iwix9LaqmV_clpHZVF|m@D%hAosDL;Xx++dens%8W|PM`D0v%;_c4!j#-`67oS$K-zQK3^JZ)zIflNO zh7F}4Icg;m8TGjH&$dt~M%U~Jkd&vwi>&9!zuzOd{xHlUp|FaY=sgYrl!`qh&J6?e zK|Bmc%s!&`Wmszs?k_Y0(Zjr$UZv9q@i@USR#|AKE=m60U7IygIXbR`--cKOe6CB@ z0qGElZwKix=TI)EG+fJGDREw_-~ME@2jTF%Paz!Px z0aecDM#8@bNIBi4ypG(5(lF=pK?sdlA{k$h@S%foDZ8o}1DMDg5{)dehl-7rjaeLk~RSOV|z)*5K)dx9J<^ zRFTeB_Vf#ZTY5RFE0B(L>FZAEk77t^e%6}J( zZZeX!m2(j58S^Gu&3s3=N|b3wb(vyOsQ%PfYLu^Ch_aqq4QEMJ@bgh7#K`@6BM%Mp z{BcGuC*D&Faeg*TI7|r;rd<3497XAjBNxv9Ep!G+s?}63+iAXV0s7oB{S~KcF>H@t z#zz?0t8d0}3x5ks5XN;MPA5v(ElkJymCZ*oe%}VDRGu8h^aoO^?^{Ppj~r@ZN@yR% z;gg$_P^3^2(?YvzYz8S9<|zaOSi6(%@T_oloE99pb3==-^lCU=*j=Ay9hqKJ`Aj({=?9uUVIDs&3gaMD^K2WjNk|{dJtnZV@I`w|8Q5=MXhw4?T6g!s#t$c z+!v9rZ8#a&5jL^y=Xw5=TD5!&Jv(>+TnWd17v?qpMD|MJY|d>{Ew*kwO6T@+9PV#3 z$WVPqxr?$X)1n%4JSS%&WOQfh0{WV~$?MP7__Hsr0HeH?d3u;8V}+>clv#4YmrbQp z8p0ChN+8QBPGwwzk@2LM6Uz>PI}>w4Buq(QPxX$PHuo0E+9RdSg5c{?hPqzw8^+ds z^h4O7n9)h-1UT20KH@|-u9#*M520~!5-Y4Y{FOjt&miaMp@Xs!AH#Q{O=1))`UC6L zo%=5aZzitEMbj=S5hTx+;gOb^t0fv>>&9#c<+O4QJ~Eod_f_ou1%1#%ygTfz?JoX@ z39-d}Z#e5%+PpSDSyX76Zr-Zn-EdBATKoo&0y$t2J>VZxL0KpU%A_y)IGaTf($126;R! zJk6-1rSQ{@woqEs^Vh>@B7`^uF47aQ=a!@9-_ym64n7iJ5#9bda>tUrj6UTWDfqeh z-!NU>izF2pe#n=zCgUKuoxZ(&e)X+i&gaog^1P}_%R_Y_{y~E0ejBL@t#4uSWWh_j zp)a0c+*ktKw)Cdz&*vTvDd^1JGnse{YF;HO2s5uU0z`<6E+E-QI@s~}RZsF_67f3l z7%?$1SZFkfVZJz6Wd+n&png&8IPch_b{jx3NAj)T2df=!1wdL^2~D0WQZDxi5x$HR z*G1*_N_XKUC!RCq-9PwqH~-DYl^e+cVt_y5N@H;)D}difb}@EohA}7@9f__)bp5Pz z5+OLf&qr{8Glrl~MSF*x1Ep8ED^Y0itH2pmqM07t0qQ1vc1&)5Q%4Kru>@3*B*pW@vIiFZMif*MchU6$oHKq<09Ar5_8;{X+_BRD3vsx(vQ96N=TR127kBh@6+x@0mfq(zbqVsG-F5c)jx z$#wYx=kAA0bD-lk=CA&=Nhb~PacK#irdd&#;nY~YucrP&ghSNN7Q6dNs|W3OjNws~ zMfrD!XPq9)e`5W*lYhnrqCSPa7qBT5;E~C==jOO9jJNV^H;>hELYDf*>F;h^p%t?7 z#YQIV^`MBggx@lrnQ~9*PHf3lO6zf(#*?7NobSGqN zeQo9)Tb1vEkxs#0knOR@!R5?q=E;cj2R246pJ}r}3^5jQa*M#&uGUiM>JmMBQ}2f1 zv6k7nN_^AHC#d5X0wB~|i*^KN;{2~KC)={OdN0<^ZU+L>PSslfz7Zl@`UcUA5VTcYE<#Fc9RJFqF`eJ2pqavnKT7ZYYyDO2AEU;SYB07h^SW; z*>`C$ISRCbt_pABHzMhWK0dfYjB+_S&t%`&wEX;Ycr}^Q*uU(3d6?2RH&++oQ%oG5p-D0%2H2|BA4PC{)@&)+gKr;g#E?_9j3mivmDy{x z$ZT{)RvK%~#cQ^Vo*#J!AGQr9M`0+)@ri!#=3BvD`Dy7?n8n7rOiw-lqko)`R!bN8 zaCIfSWTx$UpiOc?yRTE|bW%HSPbQ*j(MNl_1wf-yw zmOx!iYn5~*%7Z`nnuU@cDeJkEVAa~yq^z!Do7K$jO&65QEW(#iODz~7 znVv_$uoqIaHQK7A2C6D9HQ^RM$-)6XDIr^0v!9+&2&>|&il$$)otaJ0L)-qA2A+IL z;Jv)tn^bhVcUiaLzWFuT9%u9 zPwn>{b`>al(KHXC=MT3B1dL|$G+TIbob%l1)=5uutZdb2j$ku)AbdTORC{{eCjV=G zJ4N?ra-!yEp#)N?5L(pO*u`>kx=%EgeK+J zH6%pBO!+)O*TR}!jDa;6OnzP+sSW-VW}}%4dXC~y8~aNA7dp|$ejhBe-#AQebtnk6 zHY7mmY{|*tTP2UBt?`3M2y;XN`*aJHCHy`}3;kg**k5~S-^Rt5a-}Fb9;_^Lo6K5# z`li{hy>iiB?W#v$t(ptI68X^aDqpMc)(R{=Fp8+gOf8uoVYviZ?YvIRhhtpYO7N9` zYlT=&Iejh8-(h)WCY^k~#}Jul>$ltNH#<-@yZ16qOHld|VbUhcS8;;dJs}9QpN>{t zcj}T3!Y6mWr-sPfuugWCK~A-$K%(D9Qp%>S#A3Q=_fie#`OQF;isf9 zj@sgUb7Q|dQU172mQu{7wp_)1@296=Hp$x1)N%xuxv14X{iSJ7od!{)9uL`9CMIy4T^!>lX%+6V=AcGljMJ&vR{Vxw5$_R{rd! zf1hKGE2f)YU7l<5cxSS3Lg`weDbdGaB5{Z@w(8JK3M1zxmELQ#?X9oSk35bh7y9(L z>xs$!*YMKUT0k#I0wmymy~RiK4j=bfA>G#<;u&Plk!_pVt%+66_CYcR6kEX~`HDAw z(q~gc+_W)2IwuFK04IH&P@u2}4cCeF&@IZF7c z(}ncSPiYPFGQWAE^hUKHy2Po?(Ge(LJ(mY?9DQu68(F_Bi-Cmp9>@qv1gx|ai}S#K zE7IE>J{n-{I)fwBq!Q=jPxJf{*(H1P2nCH5JRsQKb}(H|q{hZPBGsaU9%1`^}rX@i|5K7CRA(P5O%) z%?|_J0Ld)w!lWL1wDB3uaG}$P$JIJIHO-zfGP7Ha4kN+VptX$!l)}<^K@qq<6On?a zY8tw)QQ0ARHkTOYdMK-$x|Y_gyJ6u8=5qD`91tdBZ+Jjf!!~0YJ9qtBEY;)v5a-9q0m7 zq6FUB*Zkq6hMHk=-4`Ui8D?l>XLx3VfPc|iurhe58lRG#-73MhQK5&Mq6-~@~m~eraO=EX-&i*c|QDN>0h^?bsdw2sPxO}SP7HI&JN#Aej4*2S3v`FFe3d z!f|4uexdnTW62nwcHw^v)hf9hXr+pllou@!TW*ScZNz?7ejjO9jA{9X^NxN)6j=lk z$kzQhmqDoD>QfdyP0{wf^g52^g7Vob01XfW7yzq`467|w^i-EGbk(}o&7*A(dAFzv zto+v;qWNkb9Jud$xp{yr}JUOY!R>s!l39uB$A-;`lUJg8jfQsbrW% zCySLiKUsM$?Zx$TeFo%!V|UwS44-wf`N6-PrMo6Nd4l~{eqDg9|66>~A+kS4Dn_3x z=6$qxmE(NVaSPx$kNlVgWNNF*HI{KOBhOU1QdmUUf$3Mt?p-*m=Ef!9r_~Dvw8V5E z92+H)mn)hw)FY&hVP60KBmcfF@%^?(4ue||ay}8OSyO&W!4lH}9F#V1uxat>q@n|| zj%_6XqB^>obZoU$zx=ZgBDS{d(Cx4(ak0b|>_e$QTZ@Saz$UuCf0qIBc>oTNZNxVg zR|7xdB-;Nz;J=w*O;BAKHqqMd2sSo$A_Vx^7SlR=L#}12<|HBS1oFV5wNaGSD?_j5 zB&p8_CxB7B4^x%D5THr!=-1}mxp{4N^k>2gw%*xPAO7bc{8#gYQdhQHjH$~sQTrjF zvueY%HDzZ481b{-Z%>rlVEGI<7AE??VwX&u*L(OoX#SC*OhiOP>CM#{OLG$7o-D@1 ze)#Y??+Dvq*1iWj6`h8NU)BHl;uHeh)kb~#92zopGRMC!y5PhXb86KVs-U|-TsC4a z{erO-)j1%oF{Mx3kO6lltc+NNmh|kn9&pya$x7&)ewX}WMUy>-NAwFR=>LB4%qNYB z?qf+~xru}bZA2$rqA!+KGF_ritvTuIyGcllFJPJ1zyOS(zuh)x*|cnw$T&c~7xa;< zeQ1TqmL2`6d-1DkDcny0yvv)hd<6|=1O2=`AoS$5(((V7mJD2fNGR4}85au+vE*nT zaQWe2o|m7-%GH3hM3cmgnzlA+``J1!K%(>M;&FL1?Gy1Y;(6Lb7wf^;v?WO`0_;1a zo*V#L#IbPtl`*-MsWv6M0$4PbTEWy#3E&U?|5wX8;knh4N8PF8u^na|+CsfTJ)}uC znWNfhy2?Tcz*{+50VEiYx>vv&0s`j`o78966eKH<*#NNy$bAJ(BsC<)01>O4{+d=L zB&_Mw14#``Ne}*)Mf=aAl*qi*??FD01V$D}bF@L)XQzSz!};s_1gyOhv&m{>Ltq=y zgT$|{qF3WhUOaV6T4+%(6gI%Qyt*p2&(j5vA5V7l7d-7K%juW5TT+I`)dI=?&x){? ztL9z6dQGmgI2HD?JAY^!ATxZF|6)+ZcoV~N0qh??_;t25Wf!4|90%bL%IZ2M*3;LfD@V`}u%Wy6x^_dgN(k^k8Al!SG&@A%>;<6rmAN$AR zA(;FS+@lhn>uy*rqZGK`4y1|CQoX(9CiX(b6rQcAWK%L#D_ONUGVf6z{hz-}@iW{q zLiao#ZR+}=HIqg&fkjlz3z*{1N7B0#$IW{yC!CiEE%XI&_Zelbn=|NFc<#-mnf{q? z7Jgmd5n0C6Od4KDfRA6)2Ef-NC8TPZJP;5s@c;RUR9rU3-i)p++hG~}p%w!LaN~}C zzUI7LN+^rZc>Ojo=&5}Bi`^$)Sb28Oa!g#AmE5})pwcq<)RmbuldVZkms5*4)0Zos zi^0VWbNUGvXctbId0epm?}w=9h$Pk?ndk%5nUW{a1oAqzlWMDhw;->**?OnBYFyA4 zB}x;vM{C~-c`LfI2!EZB;Bs5h5|EF3O7s!XN>Hq-AlB7L`hN0r8UcCFaWAK(Aruze zPyEzi|7}jGX+Z`2F^#J#{LepeSsa*bjbJ&^iF=kYCP;iEdH}#u3J{V-^uWKCZQ=kd zz<~XBY(2SAP7w`T&Q7$JMQ9eIY-$DH@zIA8GK-*Mp>A@oj!@ure@C z1D9pR=un0?S$xdyc2Ro^un-74v|^vD(C7Rljy=2RybEhjr3!T4@9cCl{r52hE(yfr z+KJV)KAUzcjJQ~Q+1rnB>DR1Y!~&v_)q)$N(ohAHGYy0VW*3t;BS>ducmh^6dc5+nyGs70vICR^HwXVy>&oml>+B z8}qxeWWse$YubH#JULK| zvl|@59;1r)+iY3hZv`?Yt|l{aO39uSp5fE{2@^&W#B_$bb@ctr64Z-L#lOw_mx&O~ z_gju}?cHsrm?YUCB@myG`DlO~KjN2vap&jm;!Y2a-ydwecyPWbR$Y_mtVuGRatK>{ zCg5K%u3Hs|7svz0t;go0JU@VKGaV{F{-qSCj9vg36{C9mZ{o*iXD<)jE;3l&P-i4j zADhG+fY)RGfsX2E6(bs4`NhqEH=HQ|QWgmT*ggx~K<(zptp9nCa|UPi5P^FN5w=$3 zNAA9;c;zZKSNGf7dt|GfS9-YCEn|iQ`*(yeV(`iRVqZmKZ;(zlmIvPdGIF>jA`f>~ zp;?j#W3y=~Exlh5lV63+{3Yxgwk!=A_ubF?_QCMg!DYJO zxOMv+Qq*rldM8b0ob^gTTyvbctDioy20*vcrXSk`-vu6Y&Tl{LxJ+W{TyAIInW|yE z7K=nHX`P1s?96iCN3W5kXi7`?IsNa}6@|-BW==Z!wKxL(b-Rz9I%={qR6c8efg?8J z*@4(uHWeZqh)!os=6MvM<=&CGK!oi_DWFC%k_LXXw#h5yb7(0UczgZ(hFT?CYQWbk zMEP)GVc{*1$c^=(_zV}MM3v$%@36WB!$)aPkZrU@?~+))ats;O;|#KLz2va1vF-caVv2K1H)T* z%k(c1)2(~4j>*W>PsX^dyiU?RFHB0*r!Yo1@A#dT=Vd|<7aPhJG11^6OSlLTjrg+V zBZN?B!!S|d`*va|>R(^RzzEJ2_opXgA!BKz#oLO(EbLkJ_d%4^mssW6-!W(V$6dk} zUd3f!47<`Y>x&$CK2^73ju18DsnPk9qI4yjBK;Wd`eCU6hZjLC` z9)>(rwjnLK@_tyd^;rJbYiPT5 zRErM*G_N`WL!{N^qfF*{g9}yDGW)l={enqM`~UUxk;?XRE%7^(amF}sGQZcg&-lT3dGQz}+a=1^P6HN4iKXZ=!k0UeuPm#L+1ZZPZyjX4a)OgcPxFnJ%I0%{` zTqr>dYhyYH!O+MGe&-umVQ= z(VZ45MG%Jebry9BTk8#-!kES6V3y%3Ca@2S#h}u@KS}OC0`et_+OA~ew(CFnPex$! znLBaN;Q#yI2@3!lCjEH|D82glxsw&sp(DB?s5o=EY3e0Dt6nOYZLTmzgM`62>5!vsNkieR#8;MtA0k6_4i z4d?VFUJ^De2a}wnJ+C$h4+63>$b(}`Hx}R!Q#Wd= z>&RFT{|!&XCPucKqG3E(Zm`z@c3?1Ncx_1x@-ckKm$G0?R^hdj+FGq&Lts53E9Y)0 zmZ4F_lsl;Q!rR~Fe;ou{7~dwfmVWSQO$h1(`eA%Vo?!rE*)_)iAA^h4+XN1sQirFQ za9=Qc`aj$Db%8OKHt)l43u&g9=uQA*;`NeImC?6RUlE0&T=KADT;!;$PNlLJ8?DuU2*iYa?Xmd;?Gsgt5rR*bV1n3?4?HCQC)!j>-nwZ5i3Ck)Yz~Y# z)D{{-t%b6`%EZg(w9C;#FVdS{v+JrSW&W4sSA;R9FbrW3a8uTp5Wkn_ola#6!?pwoaHK3jq7?n!5ZK|BwNhSAx z7CMRiT<6KRqk?Mgx~B`33TMeHc#Uu~K5#oA%pgw5{XhO4q3fPDW@FZEWlu_G%gfx{ znJK=EAo0sB&TF5;9E`S#^e}E=lNL9t1`Jc~-BXW5-@*7zj9~Cpm3&MDGfIZA`Tu3S zlDHy|9j~WLG^3Aq&Vb=Z~-PY zi--P9cgQk7-<`>u1d>f_Y#L0TBFEKs#ge`jYTR=^b5omq`@h@je~TcMOfZQht(_4c zu`@s%)cl%5Ke66As7-n{=a>=09i~!!Z!HL6(e;mF_62kVb49L(^?0jb@`!|X;%Uv< z@#f<*`9=dp#0IXW<=ck-2<~q~Q9yx?yZ?`_l0vjFK(RYnkdHG)xbR$d_Wh0!^MS*3 zJ_gwQv)H+)76%WGX?HzgtSYrrjtKg~DE~|?T%^Weq1lz;*Ou*CPEHOWLKGtlVDu+D zQJAmFe||{%IR_zKDdc0Hy9zihMw)4V5fMR2mtn$`65J#){m?(h6UdEVhPCM(VWzj| z;eG$pYWv^I)+AP2(iciD6Ke^#C+$;kjBi3oVzWEEGeA*lD1LKC`pl_Z+iiVJ&RpvB z06~*@!L#iWU8vCe8Z#%!Fy-pUVnk1njbooU zWHi1W~6iZ%Ts3@SEq6tHy~0ZrzliJQ%caoL$LjROWntF zeCKG9qIYc$9oqZn7{iMkPe)Dt@9M-r2MchX2uWQp9k4i~!;lIm+{X$36OUj17mou4 z^NAm=!r@#wm^>(+QYG`gFlv=Ps2#k5XNC;PI$Ubv`k9A;XEpXDU_OA6f;lD}F$ zOQOR_w7QizzF=sELej{@rgm)9aLVUqE2!b!GpLA{+bmbTJ^ra<(nX7kFn9QLDUpZ4HVd8K7#P8oiOEy&DM3G{8#h*+28mhn98oRL=Y zrKm}=^L@EI=zQe+o`h}^mLpr_&FaR)-)WD$-Xz9dv3C|wJRvXzbKr4I*#;!7FBJvp z)Lo;>^vT=DvC58S-%7YR{K{~~$0ppZR}!XT`l>#_pLuDZW{l|w9WDwjqVPIYJ`LY= zQw-Pw38hL?@4jsLENCq{js&-Qw5mpA5zNiXgt!x)!A^D>p!Ro*4|(O$h{2&dC*jbX zyKg@kf6$h@v8Uup@3(ep=d8S^6t=eL0QDWlB|O}UKto+oDb0V@1;^TGf~tZWM;L@Y zyd0W<1g9$=F%o%rd*f$r@_ri$!?~9m$I#5r?b?S%ds7S{wq(8@4|kWRX)7A|W(=5j zob5vC&6?p&j!Vg!1!4nE1=@9v^bzuJEj;|9rFKy*9@e=Y^5(}1Zcrgd7~ZDGS!Gwb zhPbC%UHfIB-1|{31xFJVW8ZR<*-jqzD(5%gF{56$>@wC~VPYGHLD*hQ5G3H62B%OH ziw=i#+;b#3;@498=57CTp$CZg^?;h6Wv@11V||03mE0|!jDntOQ$BS6^^XIbo0XMt z6JQJ7PBT4D^Of7Ki?B?yko+(EKn=~>$k76A>Fhe+k4syiyV%>x zituXp)!#}t^7@UxQOesC2+Mm&dq(daw!QXiDKQ^`<-pVtGl8v9c3o3!{vYKYBL$K4 z6dn)hlc?`rMXRf^z&iy99uh7j9%azw7WynXyprJ%DIDWo3dZduys5?AWKEQ(Q%HYs z5$GnQ)Hg8TxCBVhfhlnwL7kpE`puMTUAz+#a@Abxw9DMgBlS87#W2)maS%6aZ*nO6 zGq{xdUArGqF?fK~#9*kULBPQX_dnQ17S>Faq|L|z8RA~%!6JB-)^+{ zrcNX$blq%8`r4mXN6;wNFVtk~D}9y-G?vshG;DlKU7^RhBAyT_^LT&RAihF`t=2D4 z#cO{i#@^ZU^Go+J47CEOeyBjZ^mQzimekmL_0A>prNh?tztaJ>As$U;1hrqIiLsyG z-;kDNZaXzVP@@bQ=(b{I)p7&@x8H7Mi=ub4FUmYKAc_KuWA}WQ?H$6RsY$oG5B^YWkm+mn=VVAdRjuJh zEUkL+h``b0psh|;+i;qeNKA}YLU)Qy#EVGD-d)v6Z-NEM!MSt2n}V!2I;03IXyF## z^NiW>37C%n$=@@IJ&YY&jNBAh6w!z-vtbhgkeN8{+IERwfwn`s>c51!1<4FDf|Cnb z^n6hPhEP{k?#X{BAiY{Y*o%Y;gB8|rjwPZLG_9bezs5{YvV#r3_+lXwF|gt3&inH! zs4br_rO@Jou0n3M%&RfB zp#X2bVW!u~mby#uwcQ-6susiA&7^K6}tr-`)vdTC|EGbTV5}9=ld$lU{2*i}lDL z%p{=AfZvI#v3nK9`bW?l*RsSG;3UhITOVt3}T!g+nvS5c~(;r8S!B+sBj z25IUCy|Pf=PTnM18PA3WnJ;MxDapx}&-_Yd{q#&e#IWmE1bC?m>z_837AALsM2V0- zNS+$TumK^c+n1o`?hg0v<)GVxQvMc&pLY-UGo06}Y2u{mk<2wF0nOfE9b7Z!$*iqH z$g-PO9^xf#&yJ7#pt5kUuXW01ni566pow#{R!Amf#a+m`Xd`?5zo#vQweI zVi+#jU=iEy>hcFhHPQJpPkqZ%{(Xuad+9}}bi2sR?xoEe`6=c;xvgU)cKP8=laEKE*Jyc~xJX)d+;hcPxCFvPsKS#2)*WY=VVsdwfia{?R-wMl&UT?aptW5;hV z)(6ZGmp8(YTja%-+{{~iMYps~oRinOAGIH`Az1V_Y!UHA)v2vg@u0IoPhMPziUso= z>8}(#MM#p?QBVYNTk%{m(U%mwch*P>xMT0Fm|>_@;hS4s?_N#2Jz6uS`0y~i<&_!$ zQatbrjBIZyp;zme=IYn?m~LdtaV_sx>~nosE%o0(cAR9GT>%#Es? zEW|brK@V)>@5E~SHGZB1V6%nFj!fiKXeWeufN~P-l&qNF&UCvif3~vZsjsfuy4Dv< z{A|NFwQAUzj{vT$(=f#XecJ*d?E~Fus-CA7Cl#PaXJa3=| zdsma=!7QtSDg15T(Sk*_a%)<0_y$-7AYzTfSLy(bt$<{H=-;~ znK|>dUnB@w=1@<^4!?m@6+F~DKrTOGH}@<)+mFOE$j zqhW@|ENZr))MXm51<}u*HKZpWU5l+?mu7)@^`D{7vFCrkWq3^cT|P&WAtp~hpY%`! zYd78sZf(kpxK(v+P1Zu%6tJZ;$VrJ(`HE*_TvWa$wCX7ZjHenRMPi@<9814qJS(2n z&Q1HA2h{ExzA2*ea-5O3O<-PHm7kbCkX@U1EzfyVPQ~b(VKbXX9c?E&Ny!nvKq2;U zlTDR>&}liNtOk!qU2cmTUQilP@N@?_wbv3AC4AUm`Vl?lZV(CW#2TR{9@oRsLg#d^D?-Zhp;(%ogo2w+IG_fJc;SX>P&wHeFry05V$u=zivI)n zJrlNBJTz<6yLBD=*)$&= z*EGhMp%wCMx%{1H+fQyR$L{px@yU3AD-r)=nbCBu=#cTP$(ZfI7tfBvHX;@JvMaQ@ zvI0yg*gGuaWIdRyhCzXEmP03=S(!?ty*q6siWfWFRTLiw?@VNkQ%him-8T|D@|di< z)jEV(1`^05pB`RMD5^PThgQF3D#O3INjUMg5qnRrYouOx^O%6|&2JtBS~qT)$cx>3 zS4gotT@5QVzZhDUpCHIzREGW@H*D(N45p|_e6Sg!ndxuo^G<4=s>~`d^p81>HT~4G zyFST?vTRi46WC(MTexVFNPOQiKqyi;kt2y;#XDcB*D-|Rws@qS5Ntci-6)bA_d#PJ z=ECP#ukR6w9?ze=_NxJ!=~J1MV8<9lneTS9O-He8Xp9nvpCMturJ0o)jZmeBZ`f?Q zgILz~Z*|V{Jo88_}%vv2&%!0Vkp+{@Pp+oLpUB*y(LLV-Y+v&%Wa$8$U8(j+m3ZJ1%Nje2!;dug-XQpQFS_TZP0FYl20*Cm>#U$lG02 z1t)^E#zlB@vbE9Pd9)Jo(|XgYzZR>b>T)Tfx<(rn(g(2~;T%eOO}_uT^L8MvzG16e zv_&I^X3b?JXMN8OHX!rZ{qcwNi?Pb!IDwF2E~hS?TGN!5HsyRf)?cb zu2TqH^g0NbDNW{9X-zvv6Q)mq94E8T9nWmLbg&Y#tiny)W#U_!kh5d zeJ~DL)6>%-G%d2=;b$TU%=bRm*z_o@Y;R^vG0BZi@iL_GuQj0#1pU=dFaJbHM{-f_ zD|@}|s8V}di?#1EPh_C^YKJAG_%%b40CH1wn5T^DcxHTxl@i5fd%tXI z-uPRG_7+!#Oh3oJoez_d?>2MYl^VrJ9wzignfXKbU8Eo)ev;j}*JDOsc_jV@i)k&? zf4shbVZipyLRvm~oEVp1smr>*|Hb0kC~RZ=+`#Qi@*fkBhThMpcHjOgxG1~xHpZf% z2KESOc`a1su_xq(MCHStD}84DP3Az`EzVo>qiO5DvNXJ27WJ3;CEu_|;FyWwWbKRL zNN0kf1z!4=FF%v<$KR*N9sNxRIGIhxwBc$oBH7vXgXY{{y|VO@Up0p3B7jzR*-14; zU3OGJFnYzs3YMHhqR6S=X{8i-(h^(0=(JDiz0(Nv%p6`xEllvK0sDQ4~9^>9`JhW@GW&(sC_HrR5 zS+dWB{u(6-cx26Xd}I{yVh-+a{lL51JTkdKwMz(nu3@=U88PF25#8GR2pFP}LY>#y zofZI(uu`1-REE4+L7lhe?qyg^pN7Za&e0&!%Fa0rdm$oyOuGnDE&^STH|xg5B6@{x zFBlRhFWg3rE9Q2Etdr~SQLYAo#VUh64FhvV*;e`>$%SXG;9pK!bfh&wUZ%C@0+dj7 z&_!y1K3`UTYP9xi77AF*!l8h`i=7bb(HMjcon5J8GG*C?v0SFe;!#N>e9BSc<$c(h zK{*sf(jmt+ahJ)f`SV>z__B*$9(7T>k^DI7noa`Si+qlqE1mfK>h!Mm@RN$R;n!Mz zx8&I>=JT7?((`+g3iqh@nLyi5{GNmc=^ym7dBgiXb)~_72}%919IQa z{tTOS!5qv{!m#lLYXI`uq3dGg9Gtq=i}oyKPmq>#_s$B2u^>-$JUQ{L&@og>u@AJm zzP6Zi-laxXeo+Fv)b~8u_)_uone0)Aa-1{LICmdfrhjm!f2fx#Xx!tG?mq#&t~*&| z=0Lf=8V)_3O*TWZxdkd~^Vk{8Wj3MLAPYiuRX$qd(*4@vj7#gnYl4P;dV0xr=`!a3 ztmj@VZcd>q?Wa5C6(s~%s_guR@1an|m-&aKyUFYe0XDZ@@*5+3 z=sjq6dz|KQ$}5}oaOS4J1H0_AahLIMzaBnXPQ-CEt z#K5e3oS0B1W7e1^5Ei0eJB<4Tfg`#VKK9%HC6KH89KJ1kJ-K|gP{)TQa^JIs#|t?b zUNTz~PtOD$qZJmzV@v80`9>nmFLwU$FX>Foi)Sf1jZQi=$Ey8BI z_g*HutEF>$ycz1DjU)tUP!j~b=5S2Z+ZT$ItL@86lEE=7d2-_%m@nH;QRL;9}LeTN2j&Jy?N|#5`bkRr`7yo$jH{$Mv+s_3roG)td435Ro4O zxNj49llkg;Rk~xAZvUW6S7ub5DfPQ<&eQk)xXzu04le}>zO(nJ5?#7;5y3E;raZOL z5?9C5kLk}oEvFSc9nkWw|NU2?MX<2zZtWGR*vt1}iyN-{!@(#0haTzby}s`Y_<8-g zq)wHoJ_PJFzFG-=m0FcW^)5`shJnYbin{4gbeao(?;HE#Z$21j*y^ql)!zwOc2}w^pSJjch#;8TJi+%os(KniEyfA=t5#c`?jABGcN1zN5HI=y z!g3>^Wfi_+=H7N*UZMA1S+;rEC${six_tcQk9*^BG|}8ys^FJQ)8`>C8YhK+T*0n8 zFJ}RfyLrnH8mxx4b76zKNMnPRngn}Q><_Tohe4TC_>E~<#DG@#`N!n8z^|91{?+G( z;L8yAi#q5HVi>+Z(aM)7ONvY$a4Y&JpV0`DF4$7zPAPjSo)OnOn83_h4PuH&Uo|2(mm}JC~i* z;xXftF1RbAr%FAK4=(wJo$We&uEIf)tFlMy^Yk$^HTS#T_eX2__kbYvTGtq&otVx0 zN2MBX_Oa&A(zx=Gl7;;J5km5p&W^J;>dDYq!g)`lK^!6%Q<^*8Q zb_Ombvr6=-sq+@}AtB{HbTop$W%vd7HYXLw1zWH=?W#8#)DDXn2{(Dm&LBp}{oY zzm(E?cB6K~>#FRZ%FhRSR@WI0TFO2+PB+Lgb)2I!{>Wpu(Wj9_@4fV1DGKJe)16U-P!<9@k5 zvGRqV{Z0)t5wWLBf$?SFX>>5_8=|y+q9u9Jti8KjZX?*w=22h2!xzyfCJmKY6#@H! z?Vp!?D;Q?F6=Kb*+H;>AN9+83?HJ!RXR%l^#}gpksM0d;+;^?EJVW19igR~1_4MT7 zVd?3~*>G>NZi3g3Bx2=%t{w2AL-gu6+EKZ$?-tw1HgUPoh_gb zI!hGAvynFsw~@O1#Yg^ANwB+`<=@jMbz`i9&Lg(T89-;IV{e_N3;T0@E5k$!dcCET zr7EuBQMPxM!xxMiVhN(K(j>_9YySg5_y%>BB^>5T$xQ5KRJgjyEjj@H_`&JSdZ}#H{|WY^Lu! zXGqnCd?w&Prh`bzH}Tb?fdo`YrKE7U4LSba#=6C(qPuJn=C6d^Rujcx9QseXZu*bC zS|IT+yOISfz5z5oAY}KEp-3>=BaYwR%vj`JHnoHbl$`!bjw4*uZhbF7nxc@(idKf zaMSX5rAg+mlbIM=Wo{vaaEVwC;#p9C7sNlu29fWuEk+rTFecvPeR?+f{o zps|cEFH7L{C><5SS4TaLCvXY$*EaSqIhIAUb)n64%o@9% z(~7b@I;zL}L=YAJMqKmKxy&gAxae68aCUfCYw_vr+gsI!TNwog;g`N=EwPO-=z#k4-nxBh4 zLm{y^PDHA_y>jC!+sh@fZydFiy7JebPl#SmbAh|p95VVO2c2x4rjYS_6R-?uX4QQ5 zNv=D7tyJt~DN(~>!wvGmMg3<39cgN8e$sPESAAp~ovlDJC+pCmeam4-o}QiXBR>t4 zFfx^YZKTDQf^5~d4eyEY(d`r$RkeZ+R-mE1m*LlTg&3tW>XbCm94O)i)sg#)la-Vm z+8ghB>JQmycv+jnlRvM9p^BEw>x(M_0!#PRhxVBVDJy7u*6B?IRs~W(V|-YMjp&>? zD-wF{sJd9!ck?{w`ch(*^KyBn+$tK;pcCrpBJ40XMgBU4T`CIJ%j-e2u@==Y%hzH5 z8#gC-rKY{3gx)f{<+!!>_9`CSUTlsLI1SV&KfYRO!DrSB zqyXOxM&K8aIA9pt@EYY3natdUyQ+qX2fT@95i7ZF`E@4sk}V?Yod0;Lc=_ zVL=Z9*${0rb^4I9m^tP2F`uodmQkDfBfj~Yzi62RRrP-pqR1NYGA_y6Ekm%(ndrU_ z;!L!f1Q|+XpCx@5xGZgF3nCk%DXVbm8eF{0EMt*bH6-xW+zk=?7*`;f6}Jmb!fQ%(1XEdQ&AKw%>^x z)U3M6-ThfaYtsH?wu}Oi()Dd-XOKKc`N@gLweO2YAfU(*uIiZQby)U0@WN`i5ZI-# zr#Nq8PJzim^aXM+bh z;Idgl_}67~Xg$Y@*nn82xZEJjtZb1I>N-F-UrrmB_i?UT|M8i1y-mQo9Qg!Z_daZl zml&^g%t8fb7MAgVk>x*=$HG8t)Um0oF^I zfULv#`@iSGpHwHu9I(D%s@GiG9#c6Yn%J`Lp0ttwMoD^%(50bFwGd7|8Js7M%n?u#hsPsD{!{U6{w?e4QW}#=;82$<^6~Kg5pL44f zD-9~06OmuPKV-22JIgarw2wH1@E=!cIRymPy>M#WuJK?02@)A1&{a%~Aoi_^D3b8? z_|$oPN^JlN_X!omc$f~Ia0UAPGOUnVuBhT)FfD89N`vIl2cy1Scf>a7ois|Imx39F zVjl&h6B^&Q96X45^OfKvPA9DRg^oHWGqn&%jY)LTI=Az)KWrQl4OL~T<`Rdmt{sML z3z$r>MAj$VpJ~GGE0iu{Pd}PqQ+BzVfWML-CkI$RI!dDP7#^oSOh7-l{DPDq2{m@d zyJj6p***UG)$_)>_|B*n902W^ExpM% zOHHs9hDZOzt%k600f1={3>M?hmJE|Ly{QJ>6M0Oo8I#-1E>5n}mGHa~l{$;n`=Wg6 ze<%_sLt-K8c87?bls{Q=_AB^OESXY^A#@~9j(xRDFL zEHathII%I|*#Ve$4lvH&z3y6}qe0t5%3{+GJbN&yL{KXAjlaHLlfFBN&N}~X)w>M@ z&3v3bl_L$PIeaUyQ^ELh;tDe*U8{F+_ocpYz!<@}QR4lQgOHluZ((#bS50^RnyuvAD*b!gGN+8L6#~N&4bFerP05yzN>ZKwaYDyFE!lx zF9A?kRvpIfy1?7EKptt%zZZwrT?YwO{#{V65>0fj4Q^^u9357JG=0p{aKp8Mx)BkLXFix>4f%^t!j@0N>$O4B3~1Rw*qtc zm((O`B=L4^9&(2sJOZZYNsKY|qYz$tL^|G-J+d{lRI_r0;oz-z;}G*1aH*k;_2I6} z=p@W|I4o^Cp@p?G;EQZE{6?nDf2-HARH@Lw*5LdOq`9IOj47FC_53 zAy^nDV2S(d{UP}x$p1pxaklYwJ)YwP1_z~3`;Li?oY+lKZdkk=E}Ea~)xQUjy1U=m z2;FU3NZ17ERH`rb) z%<3e9@!%ENCn*^cS3)(3lR$cs;g^neDJf_7TmUc#5sE!}@ykBd%MbA}{GacX-@5{L zgMRJt{lDT``Iq3G*`KCRZZk0vDR>BOS!~R1y1x;#M-9VG3*JT|+9AWGTlkk2`@%(z z6LtaVYpOc@lPl6YuPSq%{T}ysF+N>_I)|^j1a?8kXTx`%XvW>=RAY!maq;_+Y|$f$ z3j?%e!t&D^z3K`h0mckr$@Z)AAc&X=I*p4NtctjeYR-U^RC-R&QFqA9{K=U6afgmoC@D71pV~q2|F(*L1$<=*2>6U zr>qta22S1gw+djR%1&*11)QdEEA{rhL_04wj3bqsynI<2s`Nl)m(9D=2d+6B`xM2C z$h-}gXu+7%1A3dG?g4{_E#UBvgz(+Jg<7PTi3sm^ikziZ8;aDSeHl22`S3CYC*UP! zFn!%2?h24{G}M(t!4dagk)HK*A|4$9S_ zc7U33uDC)H$Rm{pEz-dPW~JI@iWmo~oX=po07^;RD}&i5bhobT&Mc_xofNa2?}Kt- zuj6@TUd`qC#7JU~45ce!9Rn7%6_-ph6F7p*7Zyp=-Qd}r1R?LyQk?6AH%kRAfbk?>#;&{9xncT*}TZVPH1V z$q6smz=`aBc+DM!sIK!g|CfMlT>QhM8vFL?mt@Ak?@WK1_+bF@^LwaHTLbA5b3TkU z-REVxx}Y&Y{;ckW)ghDX0?G1m{j)pIV2|EszH;Hg`P7VFjCFIn{%KXW zDM`?mp0fWtPAIWl`;)r)pJfU4kKM_yUuG6+-qVPQIt0%sc^<)YgU3v^9}z#?su+as zbr75!Z_3T%iciv+JGV;)bF9rlQ68V_KL3;*JJ;!)V<-z;v_H?1Q6&I-@4Zdt3aA2{ zp812;^TH9hK}*tVjG$&k9*!P5SxgVDTIbb%F#iPEF4^iz;&aLU!b?5_GQPRQ1@fpm z;iug3r+==q#*TqUFthZ@o|s2VFOwfX+|G3f>wV?sLKp%;VcYcAaO>CgL(f_B&4Poi zBCjJBU2Wqb2OIFaY$z5SP4soY*(I|C`zoQaEL(vpb}HX|#dcDV^ijQP9gkHm5G1)< z7ZKg#lkhI-%zw@AL7b{xUVq>)pbZ*AOMfZ^aBcq81}0~)&d{kZSj!nrKlpgtiAozO zG=pZAisl8QDXQ%13$~FZI1hc1;p9r<4bs^e1G&Yvs;Z!zo~IMg`iyd?=FN88-NU9f zj+;~UJC$@p|8k0i{2U2jzRtLO6K;gc3s`pA;QGLqv0U!gwi#yq6TOqpOKT8Hm2}VY z9)X3d)WybFn=Y;IKF86D_3(^d?aud2SxClxW_~*C*8VY?TM#KOWK=UTVSj#&7mT{f&>|3bu} zHW^WRTaGv&nQ5nVIgz;;gA6y48JF>9V`h7ai~CoR19!vD)b(049sIbEX`&eXiNEFd zeqFPdBA(_~1p=Y$K>>0GtCdws$pV8hbHB<}9$*XsjRuTx%Sm!nWjpo!Pj?Z^9Mvd9 zqxQhrI=@H(OHJR-#NFE}kh_Vq(;%$hRy?xlE5vgB7mMy5mEbSGi+-;|)PYvFVgFnQ zcIi3B;J&-1Kn0{Ht32e_={t|)@Fv)Bs(ie=nCc!0>?;55NwAObWv!khjKhCS0nuZf z&a8UkXWW$#9;9Nxb`Hz;Hm_Dn*QfuHtNIP2j%3|7WR4^2)odMs;w@R#Jx<9NZy)~g zQn|G?D#q?w?lc8*TRwU4Ks|BP*QFCo%iB%g4#AbWDRX8{FjuM%_>&v?{dN1jElm*o zD#+J2T`=Ci791IFw>!LYn0f`0^h@b5X|ME**w<8Z%&O-+oNT20acLlI+uvu_l2~47 z`7rDJu2vC)4RfqzIsR6Go%DHhNY1q93;8&%ga6jGq>zQv7+_~CEo)8~Tt1Wwsqb(< z6G#76{pYq@C+oP+l}r3j3G*Gq(`8XD571MR(B( zZdxl0d)htNF9*XJQ>GIPd2Y1c-B8Ca(m6lHbX4{Di|p`zWK5lXnHNUO$ogKO8~F!Z z-LM5138I#HB7F6#JNRIZn2CC-QBnwi?)eke-Ji7BGBCS=>A?DLe8A!QICOQ6zN@X& z=zbI_V%gtdwe$7I3%EUamr==ENqE~iPMqq`-O~&x@@B+|E(;g{7LAj97T+E#I>m*E zvE1Q#!WG;&%w-ej-k{l{B9cYG=v5r1l+Oj)_ZT^LW86Jujy)SrZu|9W5= zRvOoSiC&iNXYAI8+^E>gLkiAhMof*m#p@mtgDplrtue=*4`l^*4ClrM{S$%^5`)8r z7<)RG-|5K^LbEU*=mA3u%t?dtefi~KkJg54le3bJ8yCL|?4pkwx0)LpHc^U>B}m3< z^e#~w0$ccTwLD7U7>A9+L8uuI`|5mbPV%Ns_zil;;;!)lCxd+-`at?JL2^l^mE5rN9+K*bjY z@n5FI;NSE}n1(|iF50jW1`=xir7hy?eDrn6m7DW{)!*0v-|SP1@w)*9Aj8ZlOd``2 z&bijCu47$vc(+UBw`p=$)UmZfL&D=WSdnRwmrKe*I&=Em{ZxBgeu+Iw6rd0FSW?c8 zcW_!VVEnf$kV9+OD>Lib`KJC}X1F~XR$y+P`m$*5XnDu5mz4k<7DfJAt@8s5h1QwS z;cf2qzJBGij>0!rnnViX@|(s55{fvmT5r;})2XtqaCMj`TdgFD20mW;u8#u{GjCnl zAi&QITXS1%J-?Jkq*IonW{js$#+A7n9vjI!KrTp+UjkyR zDSBeWXpk<_Wy=3fS#bUtLPIY8>G|U-3+)8a9IHK@ZbaJfJLM0#(UpZ(pDa6;!D_D& z+0>E9hIW5IIx*=8gAwXATV-d-dGJM$W&5{}q)hva<}^_3V;onU_*8pB*X88tp|b<~ z`XBO|T`Uaf>mk0ymS%wFuYK3?KPbN+5k5Gh@LRnqCg~z}f1-2I+D32n&YZxdf^{lK zL6}$Qls_e->FWvxi3xEW@u9?&cDC8du|WgyIrI!D6NUJ_bvkj?P3%gqFa@$ic}q8f zP;iSj4rUF~dLl-*jJyOEv!oYU{c?M4!2JL%=Cx{5dltEktudB-QaNY)wmPeH)&C1g zLAJiE>bYjcN^48rCd#SnM2M4gor%()KRzIPb@K8e755ft`$v7?SP^~BDu!XLTda^| zvXIY4$2tejI9T+p?n~dEszqPFx?#}n`?RY(az4n@LY|0MUmY+TLXvVPc7RNnOua6U zK(bnm>yg)^Ihwu+*L)KRYY)~eab>smahWK)aPgt`;n0ewvRSfi-&C<{lkM9|!Yh;e z#n+E?zj$`!{bIg;jy5{9mN^} zO>-RQ^aXG7v0O?JxXa}-c``!)x6$6-UVk3!Rsly-+B`^XAQyojZ35k>q@kNb;JtZd!;q70InrkZ zI+DHeqa)9jh4BR)WjdlH5hTLag!SB!Kb!hS+4t2%j;R+U+ab~^uN}fqf4D_f-PK&* z7Y0SPj5i-y2mTcmgNU;Fgs6*AA;FP_@s6W%k(wrbL%--cgZ7cxgsS3sHTjCh=Hv|UqovHhZ zk9e}oQ)J1)f;6`xWG7KP*Ie3Yo%C#{zqgl&&7AEA$*~YK8dINX-WL-IgTG>bY-P-j zMZ-i1gJt{|S$dCGEuC|=!61Q%svDL!%6aD$>q2cVjn*AqF|*6V@t&S;`56!rVaF2P z+i$4IifhX9hy88l!%cJ~5kGiB;#G@q!ytx1W-d2&-3|6Rt$J)1>%fc1(RDiLU`eb+ zkmY1VonVRZ+nqxerM38yWB@m_HD||(<42S}i4zZW$CfxPm#I_1yPPG!*n<#B$ni`B zrm-(%>Ko;s?(t%|vh^Q^Yg*50wq$;m4iXV!EONFiHt6GeT6ryzrjZR!smIh)>U20R z2v8)+8+}}OZ(hrfy$g|LTyqQnt!zbsri`BzooTp)>+C@r!0Mhsg4$*A2)9J?EuTG-0yd z$zXN7lz5J_g6Flj2IY#Y8*Sna!F6CTv4mj)=DN$vGHZTbe)h{gtBVMNW0fJa`%KKx z&bGJaZ8DDuzQ%^6-RIuNg!a67IoY?jW+G|*ghbQ3uLYm&qk+0V)ppKAbUT=n|DNQY zA=XZvrGmGVl6rQg9w+jbWOH9|Ei$Qxct#zXJu8_s<(lQVB2r^Pf+&2Tt7_L{+U2T? zd_r)ky=wv2+-rP7Wm+G4Yc7ckU3$^Z(OcMK)3(hIn1k-C9hRj;(_8UrxaIVMi@#kWz z0~akV*<{v+jl`|Hr_lkme1rYfbBUK_@M^2fnQ$_a#?} zUJKMQ8qio4o{^t&U6|1p9h&qX#nJXnNJ>9X%du_eunEN6U#?ius1v%{KCDeFS11$x z5LTeH>OJU}-|v@~-x{)Og}zO=U*x8GzlhUhyxfm%g;Q0vjbx=_w)WD#69};G&0!UK zYdXP4oha(W*3x+i(I6N5;;X3ZLV zfk<)^ck0IK>k9Yp-#dV}0}m09g8TvEf6`xppjl@SWeEki6|bTQgb?B zU}@_eDjE>Uc1R>nBZ&0-SsL1Oc0tb6-$@Kq%R~-Whm%CAvGtO3*~4Rm0Tj`vxikU= zfx}h>5QTqfN$#IB!xBr`U^UZ|XE4lw=6pmN?VOYNi4J(ML*a*L{#rE|=!7q|j(MC! zA(o0H=q%VWU3`4;gJd8}XFNniHflYrYTX#Ap^aT1%`zyGX_udY=hj`rI?(R70qUpk zZIzpEY?P}mZ;&lphGpGb!)B@Qst)vsK4L<|5-N^UaZQNTFGWaV0!2~_BmAZF`NJ(x zI0r0gr`||kZ1su39Ca$3bE2(?0Lzat31!u)meivp5tAH|qdi^>fQhi?ZHO{hlpvz< zcoqZv8M(v`g{95y3@8(i-w+u;nd1_g9tNNcmibdPjW`YOo3Nh8hlC-sfH;C^RZH6^ z1O`M2tOUlohHYY`mQ3_u2a~;q^GA6NvmGWoo=}1G*6W(&+DjWO3XI5N_M0d?qC|q_F*MJGwUO zSOlkq2)eduw%@b$oV^}qY-QqlRUOrS9zi1?Nm`NO)Y@gN&v4z~366ydUgs-UHk;5l z^T>q^A%D=sR_xr<7&P0v397b~-x8;_mx7kq`OcT5~!zPxNc(m#K^PwQsCO$PD( zm+3?f_H3-?z&E%aMH!FWWv<(ys@OGu`9%e5D=t}Dvgqe|vvOt=!)IuZa)qQWk34Ki zd8T?_J8FSk5)qO~n5ndL)0ykE>Nx{Sqdt7F?+HxRT_i zpe%@5OgbD?QJ+byWK!|a_k@X64k=a+`|BdPOdGzJbG%O5m%sl(w@!TM{o?$R{6Ei~ zDLb}S>~&ThVRwRvj<^_B#!R$bjz2>C4#ZBKkR&Pd(=U-5qM+xXXhI(1H$pC6-q)_k zS#)_mwRgK{aX~ut{!Vg9EHjb_b$Q1qty6HJ{ElwemZv~lfcZNC;R$lFQ>t_>o@n_6?0 z;B}+co3}cvHuk}mjqf3Pp4D1r(WduK0)VU!kso>D#{2%`L0n4&?8)Ban0Pt&Xzp>L z?{tjXQpa&lABZ3yo$E5yoa-7ikAa?^9_+!bnVC6rX8rYr3m4W~Hr2c>I2=MGdH7o) z^P+Va5y@3)$K2fBgt7VJ#fxJV%42;!s#Gc#K@KGCM5*WV<#O3P$V1^MG&_^-lsJxa zdXYq`#^w=;@Y8-q(V|K_v{OYw!>lM3;Mb%hTC+%OI?+UhSv1o)(X|X78JrP0%+f?6 zG5F4dJxK)Ou@K7X>JgK1ni=pi`LgorMp>i-+^k?b9`-F!H0|;?6mu5kL{#!ue$XSEw^!x%8ye+O z9hA>kQ5gG;d*2($o;?-$p-u>~{CI;7uBy>Ef^?i5h*B|0N=AJmI*JK`Vt$MrNkz)O z!6Z7`migxo?VClv**=axR0+GuzD-7?neOqzhddFF#Gy@s)&YHW<`n#7a(4V`pcab?*C>m+idy&$6a{Buguv!`al5uV;n^@z+! zcJHfBss7e7{xjjatv5(x?0L8r?PIm1PA0Y)({Hnmg|-l~5TVB89th{-GceEAd>Pt$}K07B5e!ov%+mQGG z9}BCMY#Eqz=vU$B@#hB2bDjjAtZaDf`NU88+T{&qy+nd$>S9QUH~LtFWO*}m(vKv; zzj1f7oU^DP0|R3H2ikq=?WS#2n*_Y+%F-dP{4CSBuZ`G7TCL;EuZMj_2>(w1h<#FA zi7le^zT2nJ&P}IMrv)xr9Zs$%n=CF#UA&A(?2h&`R1Mqy+K>eUU?akPtYmgnNdzzv z#$)A`NhZZ&i_*2Ey#+aHET@PHqCa}1&pee$zKSIfeZ8AkG|Kz5Jwhb@)l-AA`X@c| z+^YjtFUCtQ30%*8{X~)@b`Hr^E6Qe@!77;Z%_}))^}lmxvMS@vO%>_uj`dne`mjtk zkhn3{w%`rDe=+fCE0o^BNb+#(!~MiwJM2B_Jx8yJ>QJ((FRY}pHaD#c<&xBsI3KAl zU^T*f$F1Z?(Kn%7l!1FV`-DB$oi|p@lm9{$#h-m`Kt=w@+N{NT9$we>fmJ59(yAe` z44K^hLPp##NX%}NJDK;Gl})%QWo}uC;7;Fe*d|78I~Tf_{@U&@6z)k$^$!v{82_S9r~igLzKbS zM(rFw`^sQ4aF3G2ZUfrU$FAUpTk$7vwq?4AQAs3d&ctgrLUJb(EWPyRpbh@ALTG3x zL)02V0-`SO8yHT6Z=!mbG}w1=WY0M?uuY9Q^V60vp%6tD5j0pyqP&K6n_u~UxBTq! zJ{2i*miTJ)_appsS3u||f~qc-Md5u@IG|CoKavi}o_Jx%d_(u_t0cbLW9&b|BZsTI zyJkuLvP*!+`f3CP-hbKB2KoHQTIHJS%d%r*#ge%ZjfA%{6DHILqNtaukh1vv;-m(& zR;GCR(iOyMAl8cuVRoE}dg%>x-XIB+13hOVw(WL{h;SnB` z_3yA7cuhM1HJcc~ro=2|8pd9oVFMr{to1s|>3PI+b!hZ}I-|WDiwMc-98V>b<&9e^J7m8$ivSh38I)>W1xxX^}+YH$mJI$!dHmGM!*TjCGlb&f7Ocl zkj8OcWFJv5IdVOpN|O@AbW03dOQPMmhP!%3zAt+XH#Z%+hHaWHPQ1;ha}6UDV!eS_ z&Z>qN^_p0*yllsgddMLDs!JN=Ol>*8yVY@c+IjJv`W<}#v>hH^M44P%ch{#`= z>;XGAZC2qJtly5(vP`%ACLtk{WQVn%)43-dgZo&?k{H+Ish0+=jk)-OlAO1=WOgUq zJDDsYLX+f|+;&Ywu*Ux_4FR#BnY>B+F1EO77eqmWIS(=EShfO|C z`aQ$^nUg}3_uv~p>#@?Am|Q;w z2^ItzC)X7ToGBks()`|mEf$k*W8}sSE5`_GppD}k9qm0Om*x%W?XOxgZfvyZLlRw# z?>~b1<@)o%4>ZfXnK}8dU#Xd$4`SSSwku(tojUT?=89Z%MdC3|yM{b5;eQs51WO?FW5a^Y8jlFXZ*Cs42>-g(+c6}b!h4c2%1LsMa~#K+fDm|{N#Ggb zUJ`_I1acC0qD{B7wAgE1U0wEw-3mfEb}LJlE{%Ba%9ShYO=iY^JnnZVm87Fa#8`)lGnbxM zlJ@y|>C=uU15F0VjmL11Z-P-ICPL$5h!DfTm1r3{!asPpPcC0pmYc6@wB%`<%uBsy zQ6l(`5*5#~@AX@TEm;y`pf!(~5H#M4?;ppetu<#84J=*Vxwj@Y?T}(0!m?5#I7-F1 z9-T@wC>>og)X$xjgv%w%Uz2U_(ZL{t6 z@kK;0;U$R=WcK>7flp0LNMcF9sAx$dDH6dY9++~>iH_oUwY88?EN6+?h*l;s)K|W8 zQ2zYu2W=TWb&|SFlqdu5Vj(%7H90iU!{5`6ix%X}xdoe48hu<2o$^HJsihhCj>qvh zbWC(Gu9JPS?(f2R1)Hp)jml0GCN@GW|4y!PD`%Y4L5qr=<~59uGS^4&VSVC-L3#g; z6}eJ{$)#r%%?|5g6-OhjBKS~m+&)}Syh(Yeha^3$PNDw3s3Pm==(x#}JHc(%=(5yI zNSR*yZJxMkH2%?$oy6jvI$bG_>#rYyX49aGjl**Og$PtFI*%I_yIs z)BXez&N}o*u{*Y^qLAbrdE)s2dC%3@J(NrcL+Bz}_n`I}UU)rO(NGv8`J>(UwcT%1 z(fhV*%d$W}|H#jJ4$CTQoTQ}l4pAQG5W7L>lLH(6DJ z6$bs7x+O(=oQZ-ZdfV(5T9PR8JvxEeH&B)N+CI-}%O`ev8QYD}RuolzoU39rwhjF% zEKh3nQTm?=aPi@GO_YZT1FSL#xY)6$Vo47lematG{wy!=y{1wA*>APUwKtaKJ+C*} zJ%{6G5xL{kRU&6O)`*>ZbYkt?g^2DA=GDz0nLZYi>y!uI>}|V;RG@9p^Om=X%-On+ z&)8&BC5d8w2cyWrgkIf;d2^Bk=9Zk5DDT|J_aJnV%vf~NV!xileFtkMXp*~Ss&xVT zJCZR*V_ZjL^-a#4Lf>R>+BZr1w6T?Q?Ycp^>Bz$82LtR{__rI;$?_aX0pzS7;#(7%}^0UW>q;u!6wQ=-=s2_N!pLvTwa*s=keN65#j^n(033@bQInT2K zIpA!f4>3+Bsojd%gd>#av=f0RIiFd-em$P#BO2fIJ`jZf@_Hn>WLmh@t5-|*wZ)4U zThh+#d-&2|w?asJ2yaIy4~(JqJC1W2!9b+9uVxXwcWy(@V~4kegR%Pg_6tx{9Q4Y^Yd|E0;eWO5n)u zphY?9K0f-~fIRk0zubOLi`;cnqxq?>UDt0NccRL%leplLl3aah*&_KY$x&0T9-d*z z)1oA46cO}9nec5}+j&kV!1o-kg`~FE7hdorpk{vxI@U zWB@^@9lMdGmo~`t%NyjOr;4&eJNQIp;zXkFUJAK{jn0*5f8glE3KC(s?4DmaDkQpd9~3BE5o$IUf9-K`Nf2L@zz{@{m0C z?0^ZvB;jHpL_J|b151fTTCdEzchthDFi^MO;zLo73>+x`!6}xX$aMx|oTKy3F4^F6 z)3%E2+>=D+G1y@FGM<`<3)fxVAQ!2~KW}#G`#EN`Rub7ueS3Z5u-vMH70Pq2UMEDo zQHE0Da+Z4XN3z_!ZnZPf70cUO@Qtome9MC~6;CY4AAcFtMJiWti&kUzR>7;((} z-}ODTMK7-()MGR#x8BfX-&NERF18O_Hz&sdL8K}5p=Mh|p`X8aYV|ivj!lPr{=~Lf zSy;Bur0t0I`;y7G-w442!_zNIU8{-?36Yi~0P$6(Czf-Ka-BT=q6*YkHQIGR{)UUR z+z7es+gn?P6LBebNc>`=Z?KZA>}hI9A_u1{IJ0f)K5g~u8;0bP3rglCPLgDjhmymA z=-s;A))>cyy+_a?`j^Va{rBq-(9%Efc`Ivminqv2XvyFZ*?pT84n*m0*;%o~j=|Q2m1bLB9X5gV>dQ)U z=i*kgz2KgYjmD8MTMwNeq|L*2<9xkGV)cU%j8`;m55r^5C4H^+omK5;Mkdx$7OwqQ z&61^JlXBCE@;@3v?uR+^^KyZf{o!ZwW>eCfKeWOTu==<_e@L{rN%xcc91})wY^uo0 zfvMF6MBo0Q+L-%#EczzcGV=Wm`J^sAseS16o0}}ShB}4+^_hBpX`^0E+Xvo{b70GEq%Z zWb=_fmUdQY2R4~(Iy9-8{kyg4*40VIPK;leoIFwYxTIX^WY{sh$2g92dN4GU-0ZUY zGg0K|)0s%}7*FzAtrn@CZSN-tTn@Kyk&G@bxzuJQ3V$S$Tz}$~D_2TZVOQlno;`cE z%$PA_bmC4T$*nIR3K+7DsyKd_!Mfu(rw>GV&?$ZHl_7cN#X+X!nO=7St zh}!sA5-s$1-|v>c`13CL`D1<3H&`8!m?%lwC1xQoS3B@`-q0ix~552e91n@okYe{r@B{@HQ zC(E^myAu!CaPG!Drp08rFBS=}zB(jJFDzLk@0s(87Kz0VgINUU^Uf~HZ{FP^a~2lm z>9sm(qx->eDIBNkf&nYn<#f*X+4GBbzBuj(93>sx&YhJrA&f~5eB~LGG2p#rMU&j9 z6G9}ytfa~KXg7w2Yj#{-(fwSi!rOiCZ!s|^IWASZ4oF~mh7SBc^TAg6cYioT?zyGe zqO7qqnT`lG5u7i|_`II0=hqENiw^QX^1fzSc5Z`BZn3|6nGDlwcA1Km_pWS`HWfao z%N8vtM|TZ_B`k~*0h+q8R?D(aCvjF>(x7#+$)X}3e{Rqw7=q|!t9rByMDI;l613`M=x{YM>jTbD6iqFL z&+4OIqtpQ=OAw!KyS~xHKPF;{(A?jB$fviO+G?IsZsPsdH_7EH21=SQ#4LQYyj&;KY3HVc z-7|)hd&X<84aub{B$D$1ixIn)(sr44s8O$D+D)#fkKK`6bBJY>=~vGT$iU93y!XaN z%fo^#9-pC2;qR<^yL)AeT&olLoRdoR$Wu8J)o;W``Z#yLr&<2bKb|GO^RYG+u?yDz zFabS0lw6~h5WX?8WRaAEzfIKsBoleD3gpAGL9cHT{;t$M8=m7#%x>J4OaL^b?f1)i zT@b;3{bglYp`tSF(0ZL%qz{@q5n-=!>gBDIn0MXOY!hBg#xhCB{grzjeS(kQ*&++| z8h`!eL3wW7kbRJAdOCH1djZ$ic$d)8_Dz!2D~S+IeRykgl9ZK6TY^Jw&})J3cv|-* ztau9jZ_dY2-5;mWM&r3o8^dabUVXgf%0``VZZM&r2^Pz}P)hnj*aI;M!-VwBD;g|6 z$SXQAxNX;PlDzU5R2?>97G=Z9{eYEd921T;?MXGg2ZdZHNfO?ioo6QbIHzxQvNB_W zdtCpqzsEsHoPI3tF^=QB`xvb$ldw~N%x)#*#2gqHh;cZ{qPn%Uv{fg8NfNUsT%FXd$(%`E<>^9HM_v)bI|MmMbbRgPq5xX3Z%P(w@^Uo=ocl#SU5F%2w zka`5t8D{YDa~(jOso(#5ztJv#pg#=C@%ZfOPQ*B(qwaZ6ll-O*AcpZ0)$$UN8E3@g zXq;nEKTeU1NZMoPUpu>7bRhcGANH7FdBY85`Ipr;`Pz^37WspC%DH8^|L=XgRhH^N z=#iiI*>?xgvcA1BzB5EGGZFKF9aXvEss{PQ2b%5tBZ!iOi0|cmT&)8_vjNE@KQ+Na z>(gfaY)#K09;bJ!aJu%@0qN~cqGdO0{r&0>rdEDLPtM=LGPdFyP3KW14QBzPdqH6a3xxy5(QZ&C3Vh-(>agJHPDJ z$%3lw8xfhGxKjtuH#N#;9jN{7ce~Bw7eSc8nZ+wchoX$1)*fY+3khbIW9eVTnche zdR)G($AyH2)JGyu5qm$VlTCwK76ecR(n(U-$<_1ZxQv>}7-x*gHrZeH{cAtzwWNeM z+}I?)r{CH1{hSFe2r)#<-K_#S%l!{_R-{RVAFBcpiPffm4fhSk_0=y8#YFgiTHB?Y zRMfj}d4ml`Irh)0NI_Xj4Vh)}`kmc9)v4FtNJdy4Oeh2-!LuP??fA4=@kk%m$(&=6 zuu`ZxM*E#N%b6p!-$}$|vV+rVKhgd4I3Q49TXvZ$NH?hE+56NYEPrg_0pNs+!r;`ZHxV{>IeVDJ;(q}%@CU>Za{j{EYl2@-&@v1zT ziRehuu5R#u?J18%`o*X~383}esAqrAL)M{k+)n`?VyZPEi zxmW7}k;ady;AY;&dQDxpxM&l~7wDsO9f~54$2jIwIWIQ(tl!Uhc~Y;XYgM4U`?e;F zqNSh1dBKW+>+8MOCKF3NdVk?Nh>YgGwo&gX(`o0jWwP@r6BF(kSRs8`?*spj*&VX# zo@U#|4VUYQ7Jspp8X`Y^hk?$cz505@IXqFi!bdEX+B$Z%>Cjwr+9yq`YmWu=OCGAjLj12w(F{LN~&r|u_WI@eBEv2|EIn1IpOJoT9JY|)7W z#zhyMSI}#sOw^>_4@D;ksy3!#6$W`<&REhYw_aV=`aEp+FU|{M_&0x++~Yp*4&CES z+^;3#{yc5xKCAbb_fO{@<2cS~K`M}23lpqX6ieJ00<}YqOk7SvB)Ppw#cw>xVd27s zQD3D{p)e+M_w1o3*m#~Exq2jabt|#3UAx9XHQHdIleB~t&GAV$w&E- z$C;gS9OpE|1Y{Lf);!f`zN4S}bemkN1Crk#NIFQ5Jlm&(q}Z141^>}RxT#r`1`!!_ zIz#6?iMFa)w8Tes5cxYgU~5}il>ORa{rXRP%uaxTX#N;uQ zj^MeA3(~Hg(oH(Z`1TGA-URHP&Nb6+B~vwZU7celtNeZE6}Lm!El!jo{x!h+1v4sw?c1i${1 z9{H7u4Q!ipQOGCWqy;_ZoX@xxj!slRP!^%A=LsLfa&xk5m$H`AXmTbH{_9t|fheC7N{kIp;(Y9@GKi*M8ii$0aW-b>Q-E|LIKG zvT4W!fLS__JzGV_vcB*2*M?2peRI>W9pmxpZz6Uk*VGSx)oX*^KhXXC%zZj|QLzXQ zOzI62C3EH%WwQ>-{`*%B+Juyy&q8v}N>-=yk}uTkwb6rLvz&<%zxPHhXKPD!Qp;a0 zn!Gmk^G)O^$BX^?!J0mc%Dh7B^8Zhd&#P|^+dyiO)=vZ^2Fi~=J1Ey&mMo7C$;9wv zC2<%+RdQU~<!4ReCMDJ-g|7Bx(#e6S>Q7-&*`-?MApo^bVV^AG6MuTWXApsUk+yjQdY{4r zcvBj|or{tPKaM9W1+b!+dvl{)uYdc_Lw!;quZZs3c(>U&bNt#=KxOhJ+!Mkz#C3=G z$h~pd9Xipi_w}c>3uRTItIfr;uBW~M$D82tuzkm5;--!CXjry(kg|GgwSH8EVN9OCf zyic$F-_iafLMO+xQOCrzo9#0TvP;|ZFMqd3p4H>cwkNY%ilm8_gi-f_DCZd}99Hx? z*r3<7iLgzNzb!{V> z8jk$>k?yfcPJ$#EXRFqUXV(oRNhGf=%LD&>rfl6=wUrEKYB?8af4g7%)_?Vl9{I8M z6|kqFUa@(E>*>^mVsU(*_Yw8%kz4l%mYJN$#1fqNGeKKdG97-iy}i zHS_9*^`PM<3#8{`3MSr39xQ63+k1LL9 zOF8dREwX#eHQ9U2gA?Cl9LG8RNTbQEVae*(Ay6BzTLC=Dp`)WC&f2IH3Wa#tvSs$W z3gj`7= z-QArdV`wQ!bXO~`P{ zECdIg1I`IJRmvY=Kk#N`=>d}$C)<87)2(%c3M}*I&zBo+xIyO4n$X+?kMyc1DW%Sk9e2D05Zx+S*l;-95$QmdFo6bY{0%3ux!Ny&&hDUzCL^lAS#R zA4Rc2?Li$h_75J48l!`07Aul;01?M~`jbua_t| zV?*wk>`$VDN47li4DFQe+nX#o-lQGJLNPZY!oj?UD`Lx48Ne^lj{5Gsi9aJz8bsz1 zrNeT}st&+rX{V6Hf($s@T9b&TN1qyy|MiV-6=(a+il&rCQP}cy{f_Ost2Q9ovb8E* z2NOR<2GT#*j`x>T@O$nx5*?N0vgKuYMLWz4SQ%(AaN#mS@Z$a5Dj0WYhn(myzMuVL z!1j`Me*gMgJ@PFT+v)r_qyr+vOYB34mU&o3i4C12_di{VWCMBZZ+7W`64AMHXW}uL z2+=j^)Xp|bPaE{Tv)T^re{)l0k#s-Mj`3gW{;z#yz{-

    j05QGs=-g?$GIHiD1Tq8`muL2tHp))7i(v?t3>zy8x+`PMIbwLRDKsqbfL>1!K?>mHme(K7V=uljWCLgf#BDn#Uw(wE#xyDy@7g!?yN0z6&(eVef@4U6brA7h1+c%; zLC3G2=`-Oacy}J@no1(vYF3~2?5jyT%l2oIP%Ljh?A2O5|9?$=dZN!Ft@ys0<$QhJCi{am$}X+jh0K6Iv1P&YSC74(|JUBa8ey=WTMJge1aSb5?!Bp${Zarp?KHG1$3&q0t5lFP%~%pY!_HSe@waVG>Kf7a z-~M^84u17`Y5Vbv?icnK)QPaPe5%)QsWd9@Llk_IbFx4`KcwILP5sU%^_Z9?OX1L( z0h?%>M*XF|n&`R(Rt{j zi_@!D+4d8`0$FU^?!-oc<2gg?F(M$o!U%k<-Z)E-VWoF+`;^qh*zOIiLOM?;Yc}Zl zS*>m4M#~?NOYhrJnmjhLeVwUQPtES!zvp-L*F5vo@){iBbtdG@7a28=gmw$%UI*rFK{h(%WEo<&zqGun~TTwcx~B{ zByJ?BxZQJ9v}6*M>&fc3es=rr+KBqix#zln^{pXGjLx|{pvStSk-C_-2|9#g#OxPU z82{pT5+RqU*@j6bY|rJAJ{u9S9QV0;!s$yme7#Sa6@DT%7+_ z-6j+Ntd!cR^B5P1U@ z>U&<)Yx8H1_1g+rB$6;yW8=`eJuWjb{*YwL%FAdmsLcqd^dUJ&DAMrlDkw5%Q zQokhi&Szx}?V@O%o~_^4qU{^^CF}y}cVHVtJ)s?(smG>W`$fn`<#;CS*>yD%?=2&o!W`ujpa}(cVj)p42KC4Xi3)>s3^7bB`m^)Y+HxGvM&O@F< ze5LQb_g=Z+f(zuu7hjZ@UV2IP?b{~@4<0ma>txWePIjGbvV{HMm=oMgBsS;ZWZ4fb z!?#se-j@bmGYwZm_)gtR;;P;-^rxnJbPXGDUeou-t*xzb*as4K@?nCw-+c2;OX8{T zm#eS7TK??M{w!FI*Y~&XN1j@(MEr5ps#VeM-MgbTYu4m2HrIoc!|YrOlGg(8oS!@* zxvKO9ElfcxMP9{ZZlCy^6QQ0*EZ2h2MCMg&{#;jA*B_oNm3kr&Zz(H+Em8A(_wKce z@>HwzL4?Mz9($b7xSTBe5td7^TH%T-u8=?ZlRuH=%a_Z){@4Fn{`61()XE&Jdz_o!UYB z=_CE-`ASC|dkUglh%%#7OXrWMDWWX!jU-9u9XB+}E!UT&tewRH?a+Ny2S|VW-5%|5 z4ckEGST4__lgmJcfieSIgnkC3Vd-~vAL%&LvBd_40WUp6mc!F|O*+)A$cI>U#Ae1HM2(0xEW-K;=K!gw5q0>s|wOB|7c?iS|B#FlOzy4&FeCUI% z^3{LnmjCj1U1_4mDD~=1287M!q(i$v%RqG7koL2;?@GR(0TTn0@cqdEBC^PRF2%k3 zYxaGk_dgsbI>B@@S;C1>5GG0D_)x;8*)!Pxg3O;&uueaN z7z`6ti-iduw8O*kh>kPQ@kb8JgxR;V<|q3`l-k=msNJOXWN^493+JXuhYr;0=LI+} z)HMciEa7H52*NC3wdLcHq~AOF@`vLxl-7Of1q0bL=IHs;qbkRe)icFXyzHMiblGGD-d;f^4Q9Q+gUq;&5!U*G;fC zV(>$~8q_+%vUtwnM1zQMzAc~17{?1g-fc%XUP;oiL^wh~MG)XUK`cQiBEcQjDMQu7 z-*+G_*NEey^_K5s3Gp0NVW}gC(OY*W^_=5UPA4*^U(aiHT~5^IgmMk0=ZoX9XhG2i zhSWo%-8f&gd+Zm-97_QP{gg9I2u<}G9xM9aB4M=*d=3j1+8oLduGP^@wf<7SCUe~; z6JcVy21~IO_+MpQ$FG+sMcKTC4c2)r;bu(wlzOi_sZ9@P< zP)D?)j?q3MKHL4fZ}N3vqWc=(O&O@4v-P~p(e1HaOKPiPI@TYE@rcRfJs>Yb(k`a$ z;}qLDt|j)TV}fgIEV6AQ;uikfTu&rtOlHmW2PUtXJ3+sAS)V&_H33V_M{HPcK#82 zna~NZ@9&CD%pdRdaPJ+nylLCY`6nu$Hj{RM78~Ozw>*UXOLZ;M@)#;r_^Z$ey>!o(KNaM!VFO z^pEz9(LdsS2)*=y_`cm~zcU+{G`il5_xy-Q%`dzvUOkXf^4$zS}%Uzkw-DxCWy==RwsI$d7thJA zvrU$;9~^Upa{RkF2Pct8{#&Xqzo}1a!$}gcYWl-nys8>>RVOE^<#M^A@2#pXZGTq{ zx2pA|rW&-pPlfT?wr$&Ls&B{Z*RQwrT&jCpWcmH~-yf&@YK=hQNChHs=i0SvqlF6> z+V6Yz?2)Y93IaO^QfC`tJjt1HkR)n6*sa*_;yBJ}4XsV7m?TPCyKX@K%a>z|ioW~S zCYg6}Nj|fV~MET(y6wmWZeN{n@AkQ=q;eYZZxbkK^eCO^AnYYeos)>Ww>m4<0cY?tTKG$r z#3zwq3=UsFqzONy285+{$W<% zpWGD7U*EQVu23cFu#)&bzm=4Y0n3m@-X`b0Tsm~#Cn6jlt`7#IuWuZ(L}Gkb@OdOM zfWJ3n1acd|MTy{ou)(iHc8?wahwBKT2A^>14kAZk5|%6Be3zzqyv)j=lK3VwNx>u; zf94tq_B+(!7vD_8ftJQxJ!e7oxG-RSW7Fv4k_co(YiG~@bn7|ws3FvInfQFMO`>>T zc{_>TvK1)R52nH*BzF4G_qqu z8&gXobtyl`$Le}hQcvMn+}Eg|45|>3D8n03@;f3D?Hl!uNp*xd%GsQ@RWO})j&mMQ z;@TRE(QP9M#oyYJ9B=-{HAuNBSJOC?wH95iWkyt^Ehv|A>BMF-;mrOKu}VF){u1Xi zy&evXanF&oy%@887n0W-N{3bmy|TWl=Pb$7fQ<{QbAao=shpVzo+KPMgiHi}o50kv zjy|5@o=IKan%3p8U*WihZSfon3yvk{i}TMp<5*1g_0ax}S>Cj5-KOXNZ9V@D>A9kh z!~KLPYO}bZZLTENMA^y1V_6r*V|3r-HSL?cIf=f>XhNBp0O9;_U5$0zv)?2<^;R_u9#R|B>D&aT1x%wa3H~p8mNgO8XpAZ!bxN zOz_99eMyo#-a{WZw0)LKr25X@;38uu8-aZWFS)r$VqGM?lFan}=f@_VpbNj=G} zG15tlmbfx1W@A4>Tity{Owo~Y;@snSjcP|F5N;!eWZ+uPf1 z#sLV&XNdtH51!bkNM; z2Vc83bfxKnS`#&X)NjO4lr!#Zd2qH41GJg~#FmWR_{2(CSpxnU%b^>JmpFGT+ORb5G!q6NA` zj)vFE^oLOjWAA&)?nJz=^jhCYhzM<+CoH?KCx}owsc25?>`y@8={vo^%EcGE?I+V;6`W9xIVn^Q6Z8_ zFO4U>ad+YM^L?=geI#4{Q*w2;PD^)bbG-c_k!bdz9k0n8^{)e}?aklR4$q*z#x?wDyr0S9B9muJ z72D;0h&@1OY~H>jBA!|`6akC!i3Hj!atz~KP4tLTrsdjJB_V=F;x)b$%wcVHH6nZ; zDrwmnA6^xufDc-+Gs6uA6n3?sG4pi_D2MzhTygFg8F=BcT^w5SR;cZzuFF~?oDsCl zkO=TH#G?zr$cSDLOe!|OT3`<{x)`Jd{a{UN-Mw^68b7iqlaoJ@lQ+_{M87M}iuF!j zZvK(FU*Or~a*^4v`?Rfr>y4M!r}Kxp@Nc8Zyjinat6Kd;)O{X2${Yt`wnGc6-gTxR-4_O<2gM^nS>m^3i|3;D*~^E!sLdoh+P4)eH1IZ&%Ig7gD} zxnsYh{&juAjOf<>1o53dk;60phAJQSaB?Dacg?NcX7<(p21kO-2?^T%0#>NJbCOQl)12?afA{4a0Tc}-JV9Hx^ z)eCMf$gls1q^lwhP0aJWM8BF-uh#jFKi`Ofo4dOn1YD>H*xa<=n?{l`iYTfMN$Z@4 zHN$|8k0iJMiEEP%&oQ_KxBLA)!^w&{Y!5BsXb%NFSlsydh3Hmry7 zK`AS;VwFkQ8qcu1*T=rm<;8v`ZY2QiEC^;sAk{NpliAZgD2i5y_}ym?2gYN!?r`IV^83OwtUVKMLZgn5oERG(J;|x$~yD4QgcK4(k4bZ$$sw zI%mU8P|E&vOXvBT^ex7JI?FP{Cq))^bk*bCrg^M6f z-mvF;fkuMt6;MW)@kha5m}U_C3JOOf=NZ@WSwq7WI}m+5l~`X34w=}EA6yni-T7-u_elo_nJm%@XwduE<6&qyyPC^5BZdg2JeL_L=WPG8{({L&jV!;m4N*XI-C&> zw|BwVyW0)1J4R((QuOkg@;3-fssrtmD)pj-goLs^->wq-hhw`qSXp5oe&>Ee1g4Kk z;-lwO-^2Tk@X?#Dk9(U5vsn~5gJvhhaQdb^sKRrz#QJeU8VyNL#uof`_v^0gaok}b z2+etZFKM$$2mf%0J&u+(8k4+}1~^Y2so>-H8((&pjJJ5!f__Mrq5CGeS|XU54gVg& zf*=5)g%|;dj-mKT{|@^nh{*&zmM-C#v!kuoB_$J!QDhj7fv>goW zs`{>$e#z1o>MymepH_lRHi233+3HE*-8jI+BkKf`HIC5t5eu5z5P3-T&8MzA2JS&) z!HAX6?1Qy$X&!i8!d!%kw7B0UCSELjDOn%mCaS)qssJg!iBFWJ6)9Zh590@ zKC4Wtk|2X@GAi}2fXP&8)bGTle79A3bYDOq?EwMg1kIiNo+$w<08#4cr0(1Sv&JTO zVjx%MTr#}&&jNyNWfOU zGVs^BH>!Kq=xUDZvczP6KeyE4I8cvVz)*J~n>XCq@-=Ap;_2vw&127cMORwSrAiezpqbRTn%BBgv%2@kr-BI-< zfwFezh34}=<`k~8!JN9Ub-+#(X_Qo|7)_U~3Q6~^oS$T?lZ;MHO>qEvQ~M=V-A^aCI)O z|2<8*;`&u>wB1%_~2;H~N{;aRBzu_T*EYk-I6zk#wj^KC1)tdab zSG}k$c1)@ts25RSxQN|e*H)O+E!GA#691|vP4FW2c-faT8AMi^;gs-`=liOdM^1P_NuW*rjI?sAjXZ}2UY^4gI##}%=Y1a zn*Bfv`g);P0&)VFmeR+_#k~_josw<;1g~J{mNA@XuccL$FO)UTiIBX)bAl+G;IX}k zh&dmhz4B@d!cQS>lF8s9S)$p*5D{Y3C7nwA-@ASRfWG#CuiS~?L!@9>4bFw`< zYa#=I(Q*b5DrS`FmqoqJKp)bwKYlQC+vgV8w7A(rL3VpTuCLH_fzI;MJoQgT>#*)O zKlp8<;|vSJowJxN`=}Fidr8N}3=`}O@8qw{3Xq@t1s3(GR11nen#p28nbZ0OfB($2 zYuB8@AP&~nNPm&`cxrz8$uQAZhT8z1R`ty*W<)FyS3SDDvx1I^!$lwBllCwLJ0uG@ z7-h2SoqywMBOT+(6Jl0;QGZ z``81Sjoh)?8*}~dsia=gdaw6N1!64dqQGhRwa|P@XJqSc_vUmU+C`B^9gNqgR1fDW zEQaC3Q-$f2@nW5?9?VnBpQkh*KYr}~Fv%reez>96{tjeQC8#4_!TASX7tEji^!;U5 zy7MsmOio+#YGScg^LtXSrRP1S_-Fz|ZVBf7h+aTYf3-({lrC)+hkn>(>7F*qMq(`% zQC$13Yn1=}zs?S9Keb0TtkERYBL)Rs17jCFIr+@`hlv&!o7X8Qqaj%*EL&#(YBU4g$W zH}|C-@?6(GJw07_JMUH;Pa)$`3)y9fuNegOrZKXSp{#KYmAR5n`R{ifyz+T+lH$id z*ktN3#@MAClIoRw>8JnWPV%oVj7R+76VO_0@oFqDHCJ$_?Lk(gN0k?bvh0rLbkZyh zF<5nDqeBgA!D=5YK{jpe>_ly^-=K1Gws-gNkbm~Aq_UX_9-qd0*5uu3mC*jKFqQTl z!*bGr%f;yd%jRft=lWR1lb=5>lJ4-4DAE#pj6XeA$K0Kz`0~1K>=OJsYjk$7zLekX zI+XLWdz|x8UdO_Y$xAR2B{(9pe1$CS=s}TTt~i2Ck>I21&6syq<6mS6Aw!ns`K7N^80kpdJ>t zN7uF6Suv*BW#aZs%b&ZuyN|B{I;F(X@ z9S{&G(wMJnG_IHQ{efzU{v^|5uGP4)UeYwbEjrW~&wU|ESD|yM{~+kgeZY6ja8K^Z zq)@P6Nne`MBiOU=FATi2V`MUD0wr>fBpQdL6Kku!rK#J`iim&HxHx zr|adXeCrd{AAa~Qv_~B(+&c!DGp4JpCp7hmCPfd_CsR%%LoLz|sk1KY*03hTY~R`1 z*oeA|G0k=R-JaeyB-EsGK8X!Xu@t{UV4JS<;qgnd6^b5k_4csa%LVIQQQ*m%Fx8@j zvC+$CwXSkTJrxa<&PU#vwg258(R*reR-Mr>_=}3SEgpA`6A#-X*i*S#EZuG>N^>XC z7Q^+B6|(z%!A9SwYY{@-l~EX_puLvANsZ}8X;@cVTg#bBC+PTz(~Y!s;|N@= zj2Yz|7dT`CR?RjymU%O>TvtMK_>o~X`8HkOalH%xTtM0*Vw9;}sH-E~_wb7=sC*$+ z`?jDNU|i_;j`zKvGo}O2z0F^(Z*p|K!#jOq)Lm*&U88?`ooQ_1aY9Kz_6;rPr~Ojd zBu9?mlKX$Q&a^f5d88J#154`zZSIDAokR-NAfDV>)~HjT=4gH6sebxC2RnE%Rs@;p z=GEE-gttY|hoCA%f7&(80%iHe#>T^|{BtM1{xW=Ce=Q}zT)q=h+b6MELuxfUffnOn zNC0@%-slj8T>!syuJ_wivfA0%k-ema`zwJGW_do^Eb5}jpwD&H^3g7EDH&Kds#M#{ zO+E+ryR0tHGmYoh-{zFD;*Ezcs-#B|go`Ykjsu|a->3J&QtZgVu;=EFdKS*HoEfo@ zc1XTh-1uayD8}%@%g5!e@g*uzE#1X8^CIjF>heP>z>=%$Z#`@Cmbs^Elt{#F}rsMvJ_hUEFj1sQrSA%1^2~Y(;9gx zZzc{)KvAsTZmN1p3wz~9-#^}1>p3q47UL~KeR@sJ{+BHgj(V~?_$J~5-#J&G!4tintMhk@-Jh*b%=DNSZYcE@`_$i&zhBk({ zGc5>2R2rAOO7(GSBsK+eX)3H8RU(N%};1;$U11ptmbP!J8qtL3_^6iWv z`w#_ZNtT&LpK337E}%hN?Qf@lBynb0%vba>#ME!}uDrVyoV764(hjs|phuMTr%)$8EA>D!JeF~Qn5EnzX0&xYo!uwjkG3o=ihoa7J`+Q0NM`egpyE*xqMgg zu>)B5u*QC>E=*w;Tyh+DiTiS`-G$<35)tEJ>iuU5?YcjyXgTK`z>z#m_J()o^~Haz zpgpA(F>n2%@h|@Q=d>Zh%U*2GK7e00pKzH}`TFrueYD~@4g*{yY)QACH1Jb6M17;J z0|MZ3KEbQ}Wyl6vnW$?FJN9Nm6iETPwZJ4B++uYbgGCk%F5qt%&G$eZa4sV#SpfNy#C?RShQd5K;Aap&$3_!fu&MMk&bXihUo`jjV->A_49N+Nn z-5pTl5ge|n7p7PP@<#xe{<2D!9~P~kt*$1DTx$+RBo`P?n7g>R00}(`3Meue_{17u z$f{eG!Wf}gORK|$s`m(@Sp$lowUdB@VDkL}Aa&>fC)j?6XW&NaI`?RCaNqm|F|I5_ zIAce=h-aIHD3DbCIXKJmS=Ef*ARG)k0e=2=T|X+oUrd35yopNh?4kIVM4WtlZV6lX zloB(ERS(@4ERB&0cea9t7{c0c2!WMpkw#EZ5J0VuvqT!icNLuf_w89NvO85B@X2Nh zWYZ3*i9Mp3M_+olySD>(60#5gqtM={-V@kZJ@H{pC5u?3Vg4!FL>3ffGbUnh7sBS(TShIQyAR?$*V6c z?9PZuta0JPh=~CKO4g!gd)OUNVe@%wya-`a5vVPXD`^TL>Ri)YInNcI>tFcSjQ#Th z^;YsR3bM{e*eMNx*)uOXdr57;4j><-9X&gdDK_(R(LbejQQI#c6>5)}Q!0vGEf1=y zuKu>6Ej6!};KN~%KqlC=u7L<={@;a0Fa^A~?OzC6l)TsH4KJkiOLoDja|4qPJpqRJ z$;!aHq#751o!|T91ms?=wH}p;UmEDc%5nCIXI&+7XFI2j1P%u3l1cm{bPFRWzJ>%KWUDuA@u1i! zIO<&>8e5&fd(Z0IE`nUJwe^yUoQm)diyFl%XxPgq+ENxp+C2k@7qZziA1?gA3xSLs z_xx6A)#{wx2^ib4W>Ebm+3z^u8rHf8vB!u_C3Zb2Bl64HNU^?vRE`{rJRO(C%`VW7 z<=_rfnS~06Lc+2ygN8_zcA9`feOC~8vn+&4PEMtV`sB+09DNlTZTck!#t0E6cnn4C zRDZvwhZG=1%1x@x%sd&Rp;U};5fMeYwf03q5v1niQg52&RvNeYOVR7< zGPs?XujT)X%>I6XGNrb6pA*FS91mzTHy&6~dFEqabaxOi7yJoOLkzO%3usuCT>Ml) zbh+l7?g;Q!#X4n-?e?;yM|I(_7*OixyW;fZq=EY6-F$2iHii$3Zu7kwvgfv1IMK>qlQFCSeRprIb4`w zxUr1p!_E(lZ{_Z6PN#BbJS}~o?ChgS`CFh|_mF+t^eeNzJq?Ll7kq5?Ti!0 zj|v|tU+;F&zPsG|N+9Bo;hKH*cdkX;F@hW?&Gb|Dhuc)9Hg;UQtFFJAT4^zX<1Dj#F&>UGO5oA7zL?%bw{S5Ru z*j3aO#8h;_h|W~mrHPgpH&3_9Uj3vviQ@@ZCg!uBxXl0$?a63>UFNQ6EZ4Pd zgM0-m^(iF77}bllvt6+mfy-%U3ImhFY{>60ZEJS2_}bWB3Kqps@$$E)5=7M#T!r=% zzveQw*KEd46Lw!OePQ4)0zcn}pzDG&Ii88;FSXv7=f{VR`KIZrU!wF*V7E3vMy9|^ z9-Lmn#Ahc$cp|`FtWhoW`i3oeR&5J9OgCV!y;{G4@6Lve2un3Tk+*E}Z3JR^V!P?|m}OChI}w zv3idWD>9#JTkdBK5|Fl=XLs;6>!Up=5rzaKau9i8TWjmGBVd;;MUm#rMp};}rTDy( zR2q5e6L%We%^rYQvpq;mvzDmp7_s+IjW7y}j45RKwn8pjH&a=$D8@ zg`QPQ@xde~AYOJhHrY+ddpL3Exjj5G_Iquf$0LnVJ5?O&dgRWo}@ZsaLx zZA{pr7HCh~76t9?U!WFH+8Lmy^y2GN< zCi9s}PgZg7X+MEn&t#EtGc6Hs`xi{$%$(I_+w4Pd5pwVjh8|ID^twu&v{72RlO!bf z2Lt?JxZ?E5Y!Cs@9uwWgvixEY-FfC%F-8_hVEuYt*nJ#+wq=Rss5ckumW$=xoblww zzWtM!Utc;>N=O{B><*?4rMFTm@t~O9jGd8!uK}j0FsUxL9pj;X$PP!$I-YlO1dB{D z$JDwqF}2FuKGM_I_hK-rbIo^kcb_Mv(UiOfU}D}!3ldd4OOv-^tfskzX{eg7%iOhr zOScXpBhJ3i{m>X6A4kXhJdRnEraO&oB&eq=dhAQ~$Hp8AqSp`JzPxd=_4CJ8cdppM z-NC@7v#G7FG>0bZzW(Q*WOoN&4+Ty7BKwp3KkhKei@(KZu+5PVJvur(Y+oL0P@6}A zY!f{pLSYxAAMo&F+6l?3;HGgbx8F@$qB6G6u`H#I_^`9%^_wBF(sL!Sh>)nyjG{P#_vQTHWXL1-OF{rrk z1A+0(xA@HYQ(eg>chw|HATYs&NN8-{MCQ8r_N#P8zZTX(Mbg>~)Gw9kga%yGLw zkNfLPMStw*8zT*<6=j=B5J+&8BZP>ufv-?{{`@DlpU|`Va=QusOK5J;?heD*iVBCs zlxwrGteg}2XZQ;~xWaRJiPiox&GGs0(7kZ<{HD5d6M~NQ+)Dg5-2HlvTSp4^Q=ELR z9_sfg&lU@fFA6x~YW7gVz21vS`u5yEGocxtEv&*wF-6Ai2Y5&9zr#S-4!vu9N34Xt z0c#SwE@SnSJJku{Vh?X$f@&Iniuqqip=#SPGfDz}HY-V5>=g`EuY?k6uqHGB6tC(e zYkWSzDAhNWV%8~{RYduOGehZe-==ynzJltRF_386>nm6ID6J}He6fE5Za>v}?R`&~ z0yP)_j&)}Xi`Too$JOTvw;c9*bLhl;Gch&Kk=ekC`b#8}l;HHS$CTS^FKiCuCIf!k zQF?XJgM^0NF1C^6fmq`3&6~f2CR8{BT1+NECQk&oF!XLWmw+giA5q+1zdT(c-OuBW zUGS^4X$1_J5qep|zGD8ZOg`61*tcVX0Fg5vdN(odqi}WxxB=#_t?NeE+c%gKdQUH| zGQzaU9GWlOgN^ZwBW657N;vQ`Cc$0|;B&HUd|^H6BMK3#)ZZaAwtRr`+MKBO{ zlcnjA9@ki1V?n<+7owi+f+#H zH|S`#5i~_01=D8#(0G{Y>guY42@T=^sz#eO*+1sgBZE*fIwR<57`B=P7Y?l7{aA8o z?#LuTTppL_(*L*Ni;|D-2m?u5{%ta#4mpa zd>gC`L)d_^i>T{l&Z|`0>1IdDUnJ#ahRCL{@6_3yWw`RMnUl#JMKw3-e)uC;5)=+L ztlA<-4^Imo6=q*T6Zeh-h`gpc@AL8YkGs2{k3R)mbMo==85$e})S0xxWJzeaR0sTP^60T6An6m;aN zM7?VzMJY?`U%l=6aYu@>06@e`|DW@o#6Q|g%$*I7ob&CyevpY^%0^b;h&;-V!Le2c zxaIz>i%728R|_a^=)@z5hks(AqS zi?Lj>V)j5nS@}7Vd5ijrI~!nVh@rcZkG`81X&>J<-GFSOUuX8@f&jP%`ioZo2T${o znq>o8_Oudr(d~-xa{mh`c<51HHloin0A!Umn^tFUMLjOoyDz2HcRg^4u%W^TbzBad zt8pnoUYF5NdUv)8%f6(Gv!jWvf}RO1zy;cJJrGZ3sURT|pZ_g<`upSJnE<`oETK{L z7Q{MV85~UMqd+`vNxiNO5_eXTQ;#L8tOI1Z(J6<5MY<^(`-Io{>N23>09%7;5|imZh%(@J6(D4r1s8VnAW9 z`%i*x_rJ$t@gBp?uSE|k^0OiooM~NZ4M4pHkoBCRP4!GI95lM2OQ4#vG+YEng#x}y zJp09Hv8$V#tY-sAiN0h#y2Yu4mt1KO*W>ZObA(Euj{sc3jj9>I_3f`-!@^JIjDmU< zYga7gH`(=->Bq$hxmkn@4{Ercg%3LJf@+Z!nmzyr0Wo|RDqUD{k|Mr_*EX-V7S`nn zm*;s&SW@tOwCSJa{`FHufzp>Ius+BoJU}D50-);8D;R;-a5K=K#i;$#?22?Nm>W2{ z#$%)dxUjPp=VWiW4IOHNEY{;v^@oH$hVo7u=P4;LYPij^hEx3fXLpM<}>_QoLg|m$RYW9!&6n%<(_bU*<#vX3NF`?Qw2Mj=^od=HH{8RFex^;7f-!o0dDyNdetZt}MJSnfqEjsUjXQ4Z1QpnUm&-j2%cH z^Oo!HP6NC~Fu})*WhC`!DJdyUivv|XLE7`j5C4j5TPrIRF2bJ~Fuzve;P(;c;e9pZcg@>H9Ca~Kg!W!LRB%q!~!;~K5=A3JAc@Q z-zU(l&%9t(xlZ-$`VRA!$7(NpgdwcaHEQpG| zsXq%aR_ieLSs6IV5FqVLugn#g>(8qVS2FoIL>(3DROdL;*8GxYXWTh(w&5ktP4YhnZ|RM8 zhMgfJ(5N!#0aFalC;WbNzJxs%jUSb|b6_>l5J^sr7H7KTnD13dWod{jvOO+|loC_h z?_WP*vta5xxiM@^MPNG?&TSbKY>~t&dAqU$N)LBxZp}cNOdPyv#^r zs5@&+38zW(dd1tbku!D%UBSD0>B2xoHPkbmVn9*wwof9stQ{PxZ}MG=zPLh(UB~&Q z0@CBJscl%r{Si1jA&^Ab(T0%c9rMTvOtjr1Z#XC-I!H%xIkuU9%W-$CoX48cbkvQImKt|Ua1U$^u zPtx-!EvjPqj3p^6A@Sg}HLA4;Z_1anrj8}hDKQ|X)Ig8DU_H>*;n14=`js#@iH*rd zzSPiZ+AFXbpPW-XL@LW)%z)+hXbS8&NqMF~YQre`VEvE$OUJRjZZJK4{G=J64`W1L z19xh_2bMd`G!c#O*SW2&_62QTe7Z&ud}LBeOgdmW=lA^p1c;`9c*maY2d;+$lFr2l zH)tN>&$RL5`;hp4F-`Esra)iCr#Il>9 zl@CMDweDUa_9)RQYY_{c7Xw5c(KOfBi3xoHIcS4uLk8KX>`r_zJv=m`HIkgX(&rEM zOAr+&DURn&DU=pbVPENdp5G@ypBzK9P30seC;h_1lU4*7(3%cpOeMm3t)ViSK74W1 zJHpuc0fcv(08NAUu+JYS2CUD(YC{q!=TkFP5ec@OW^D|v23(D!3Y~d07bMk z`3H*&fHcA+U#gWyk5cWX^}Iq1M7(Jz-u-$M%nFs#Ooicu)kvA{bEyJ~B0Q(jskC|2 zU_T{9(lzKpj@2mq`iHi7nWU<5xrO3QJW{cme_X_p)t&olBpymOv9!-Hkt2~T!0QRm zE3M-|R08h}ww+{4aUmjb2i_|=b*64W-pS$0Z9=;f zV_)Cg*r;r#5p*|bBwtN!twE{bEuJP~; zB;hy68GC~UP%5|>>btiRehA(S{UDzs48=!rD@&Flv&@{Pk0}}>{7 z8?~qE8KE~rqBwL)>7OQ9fW7*3Lh@K>XPM<7Y*8jX^yt|;3Qeo2I0bMHg5#r@fj2Bu z8U(JP+7xH&cU$0u7^GN_DASdsUkd&7P(}ws-^e3c+^XwyK4i2gQ$07ib82+-b_gP6 zc^ITj7RHM|ffNfip+JCW`ue@@M5&P;15#H!0ie!Hyl4W6XwRMbHdrwRQ^Mf>7hNV- zl23rfSQWvvgdU6D*Y-F#fV_B>{0d>UTdIYTh4?n`1eY`6j}wy1!SDbvYvRhALE@-1 zo4<#OT}3iK-J_;S-OKcT8VtD4bej~i)^yxOW(0uO1l$JTh&&A!e#8>z$Yd$xW$eCR)2{OAz|el6z-u zZK|@21&FT2aFIWP`a#mGiUFSxfGAY<1tdHyh}{)boK7tlfr>%)^>*Y=R@kQE$${e&Wo3~#u6L4!QY_SfOzCu36b zK@!nKLR7h*D8!wYAdJuIvzG?^36NOF>3g@6Kv%6_kQp%i>Ga9jp+p+vAbCdKIrVfA zR4}3;NMif!j@h;7?AIQ+7i>fWz-5tsb%{M@v;u8t=|0ApzP+ofC#AhTMxq~BlFzwM zf*WypS*~Dw(`uvJ!AzMkWET&|KKP4k7hwG%rmh{tQrue%>_yr~e?aPnG@MiNY(Xiu zQys!i0$P#ft0y@0kaZP5r2Ki*Uo$3^aKCiqjn47n$a10>oEz%#?l1f3b|D0DZTTkl zntuyLfbXJ`*}QDmLxe+nMV9tT;Fq!YrVg4zzMU41xW`T)B6^*KuULblT%ZyS$ zd0k@`0V-x<53}lWrv*8Eo>J#UrC0O>SG~xTCsTGP1@5O??P}?MXpU=52Qhi{A#hvG zF>y76{Azc)Zs&C{K&D}}I8hi$LPdlUESAhsbgYa^RC6N7R~VA^jQkD$6jrFomA7~j zcAU(?J}^a_fBQ^O_p~(aaCs*_XjWdV>$M0z2?gQ0_JE6>)Ds}}X1L70YnEa|1*r+U zWmjP$3sR${7E;4I5P99L&CRMPzyLMbr||$0NOD(TGoCWxgY0durh8%U^9SCjqE`^; zd(!PE(hzxUXduF+iy)q^r)cNew9X&LFBf-ALf!R4>|Z=#3+Gcg9qK~uB=k=F+<1<| zdGe!v=GAanH`Qq>l3S=H>>JQjYW3Z^sR+N**imbBjUm7s(WnEBeqboxs*t!0S!iU| zr(KLrId}|8WEDXfOi@Lj1n}OvEtM3kdC;*&=g&=4gt2B4yxq@<^ZCJTzi!Ok_|Tyg z$OD@4q?5)H;lgdZKnUk;Yd*o|cFFqh*GlZ%l zZyRU^%xYU)Y~=X>W@yk(RbbQb2jWy1De^@0!<*g}b%qOMp+d&DmhV~y9Ls9kVj zjRQvCF6O=LW}5u4XCJL=Slv zjmPGswGrpnkr6V6+d;IQdCkqu_Lam{9HGQ+bm{E**`YP3rM;XVh!xyXI& zTF#23=^CR<$wCQQOw0HiaS<1XVy?ENKNZKr8@sR>_VA34r3~>1ZUt`Pnw{3cOi-q3h?}a2%@$~z5BBC023Tb1AQr7>ib(6 zF?N|zE|8#&tBGIF!ew3odY;@*=NO6N2h2xb&Hq>k0CB@CT6g+WojDhtn!P>!7_c2R z5*2deDy){@%kPq=Ji64vYylnkhR6ERtXxceEuo@Vodh7uv{(0{ppAsL4t zia75)Xsn_^JRCS`{p>ChTdB{Th0xd=3N_F=jy~{u8&MC~DW(J;MkG;J<-G%r>-C8b z@^2YYsUQ4dxh`W>qFI$djSQiLSy|e5?rbmUybPbI1=Yx7ScOieW_njPdH^8~GIY>! zPm4gE5y=}ae8S|9CTe2$#ja26sWR_DRT#I+muGOT#8}|5(<=UP+Q6w-p&f*SKiZIO zbyi$x42N_84mVWm!{2#)aIpkLe&!A${q1YxZU6}a<#FdBMGN4@R=_2*C1Ct$%gQn` zGQ1N!S@R}dB6(6bF(?!assDEn%&$P|)Jfz_h?6;N4um{Lo!;=!7mkKb5CX@lCp;$|h?1Ohk0)#*TRG+v#ehD{kg8MFT#YyWC_I$e9kS3-C%ZiwY ziQ+%(7FZV&iTz_846Jyq8!@F<`UKOH$wYx@@a)d(?iAWa*Oh_1%F5x4g-l|ZT`dv; zj|78Kqq(-9X^k!c1J5|t&Zgsa5s3J12m=E#){RhXDHg1o*nwskn6)_BL1u~b#UpD4 zRaJ&=W3ldT!_y-LhT{c=MymyRGPT%7HF?sQnD;07445#e1EtM2=P*)xHh+ljiJdj< zEv6wMOK4}o<{3TXrmcx@9$Ociq9;Gjnt!Sfu&wSqIoh1!P;*{RJlZ_oS)d6K26bZ( z$_AQ<%ont*9C37-*{uv^{{6C`Zj+o|TqO?{#qEG|BS{&;y0bOPqYR)e%wamxC( z>aWkANAWO3>$i{LVHJh(eed(zbV)JdsX>y!)gC~^-Bz{ILWG}zlq|1ryJ8av=-;EO zCl?^b_)j-6cfH?*El;KJT7nRn{)1#2m-bO|l87_ERjtii#$R7Kw*huEv8)2t;1jvN9mC^{iw;sns z)$w^}AB1h-l0D=t}ib$Y-gGpdr5cw!8AVQv&UqGdWFrCZQc6!-Qx=BBV3a@ z6Tjy81o(zGT`I4uRLE1%%fq11SkVfnQN!rwcqJQ#8v;#HeaWiy@W*)Wbn_sIkf!eL z{I}f2`g(is@9phLPbz6+-?0xd*&oQ;tax)_dqaMVi%OGA@At_v2*(fbeLKCF30f)+ z;LUWSRG48ZYHIBwl@!z$X)3$#v3G7zLE#1T;=ad@t;Ml(fr4t2xLp@7Z&Cpw zW}TeUZNjcvHK9G)C29>q{%atlcUwHeMjJI~DP0x0^27B~l53C<`43`#r>?J4(2nTN zIM1!hO;0dcM>SV7dok#gNc&2#(RdaW`*<6tDfR4=iZ7Mhi$QqG{B-lN5#Ee%yJ`l* zD$7N&tg!6Ao7JK+^Dem)wY*nri3Kf;bN4|5ug#Nbrw$r!L`@<%*|N+qHD?=JAn}T~ zmhNUYLmaVB>SA;FzzJlz9hAwG1v_;KYdP7`+jC!}-Bu45~c zW);TAdcnJsEdLISC~o+LAj+pdX~GqLbn2o%uk8r04nu>cM4eO5rdA;8s+7f4P*@5Y zJa4qW;rkeCW-@UP4j8*#S;Dt{-m1aDL5>h%a2a@g~s@ioQAP)OVZn5U?|Gyw;D-uQ;r;$Zuz_;Zkh{L*kYHYZB`?YoA8 zWr`jY%7{6cPTXH)TK$a}O@W9Ln{G{oZloTCwqB!0{-MCH)5yrCv5#hW7hIF*?HjJ2 zkF!zxt#|>lozygOMdXg=XiLZL?cY-3aR8%QyLTXPov{$jb-x$ocC zd5c{g(4~!H$_VjICaxr&TNOh7wm69&WvS^}ZTT!2n3S^^`1LsEW1Un zQ*~|(MHP{Zr5FTHZ_`aS z@ox$ZK6HdzGQ+(2@%NHTdy!;oZfXgUJCdJ(SWRL+gL{o(#~!zOw)BL$=Z5;kx#;KS9(AWdYA|dswpveBGCA zV7E9dr|m0@ET6(Xa_$QQS2*#OQ5*&vqY?G~cqiU>?|UyO`d>le6vkWPU`ta`e$QU$ z*Kp#VVA~1dc_h^&zRXL6E7C4hJZ1Ji$bXodWcBr(BlP9A>22nnABoz#xA{|yK^CSI zQA5i5BD)i~f{NY{JKzs%0VpW_1WM!zcuYFUdz@`lTXLR!HR%jY&p&?kuf+^2k|ApU z5*Ae@VzYf6uKM+zk6&F%a&nmI!-o$c+1rip9GCX@`4sQcII$@bA(^V~goY>>`7Zjl zYKq0Bpt(PzEkZS-v7;q`RbUGO&pB8O2CgwZc$o1>fn)OSul-h-b!!9i+E??VQ4c3m&8DB#8VV}-@x_my~GY-4yGOp7KPhZ9?JRXh;qhOAM$2&ACFor<;Z-@lvv zn5y?6sJmY&m?enBV&0Xm`&5<&z4I5%aAO8E;f#GU#feSzPGC%H@r_#UUk}a57VxTp z0tpG7hs4aa7F%E(pB}7*wxq*0fa^*HV%Vxsd>Y&-Z(w}Q+OI&pXy$k{KllQ)P)2y` z!2f-61m>bA4>BO}fXyBWaZn=FqV8q7DD?L)lNEZ`*7f$@lzbWkXgCMjLW6Gg%Z%NFQ5QTWSSr85u=Zl2eP%>w@V zMlvQ3GNt74y7Vo&c+5evVdGy@Wbr6gK7(r#{pM_PX`HxvAR&2M(xY85gRuI+ax^t; zfLh2kFUt{1#13P0+jh;cWcJeUC^ixPkmy$Mq68#znd##jdZ=MsZ%%qFueUWZb_nE* z{XS3Kn$NmcnES#UBF9^UkEFbA$QN>^po5mD@aA{ifKs}*#IWAX;CtLPJx{Mn%^F@y z8Bt3NMi?&w^ziC!Np}RXzmu7VJTr{W>K3d2gT*hdb$^%=b|e$j=G}5{klYmBBKJ)x zkB7Bg;rds2kWO$qF<-%X?c{3Cd00_cO=5X$mg5r3g_PO(`MEE6LnpRe^*3@d4m|O| zC)nOfxV|L!@z3{`iBL3LBZhi?5RM@&M34d(+-M|GZ~EosdkeTg^^E`7SHbm7zGQN$tXBrVX1^p* z1&Wh}5ZR@cOjZ0zIj=3Ym(tg|f6mLzT~-RjrpGFCYRd;3*DoChBro>u1J`ZI>I@cH z;lWg~kJ)doczF5%+Ar&hiahhl(9jU^+}URRmVLQqEfEaw=PmhKvp*n^`_PLhZh#O! zJmlIRce_y!@RFIH%2cjeI<9D{iu1fSF#;`1zeR|Ag+GlSB3xkwU4==fu)tiGH3VQV z3KCRbpJkJ>#3XL!(Ce0TL#Mdv#B%k6{`aHvQ3k&msNt_qCY72jdpzz=juBm(zBToK zR9$sIliT~hA}E3iNK5BHNc! z?0&D_`}>za*!I5Xoaa-|bE+4=Pll31%x@w=J)HI!x6g3gn5?a2psvI-sv^&je#-n& zl9o@Eth08mJ(?sD&9(hXHyVKqiVN~EdS0F`&j0VGD9Gt$Q)$+G4eDIFip^EeHBm?I ziA3;1ZkrN8gVvw$x!w*8M$}n;x@s<(h%PHDd#0$htd7mk&;O=+T^yyeu2PX@N73Bs<$Sg_8fX)b*RwAr?)3{cjoXgpAYf`vM62>?b%h^D{ybKB3n&I1fV*(y}%u zoiMyNdWWBpMkl(5&KI^OV$8dU?2_z5D7D2hqfPFZ%rl0eG|Q#qK#u^7k{K%6VX-2=dbc)HXh|o@Ta0$SApqWAS$N-hhRx?dj=4!KQ zuaH-Y)catCx+)aR%r=ZhF419gmWxS}aBmx=I^0h0jyDIBh>ce|IszC!3|EsC5fLdY zOtWF7zBjkrnfY_(bE}w3V~}6$U2z6JvmHtSuHVbmg$Ggdqp7J0PMzmCIhI{vVy=_- zwwm1|FaS}Qc*XVyfNZ8N^3b(0N-t4X$v`O*dhvc-KlF;3qU@pN7dSX~OH7#^x!V$n zjmxYx#N_1U2&Z`8^#*iIMH+^dWhl=(s_|FXuld~znpP0> zhs{z8m_|@Pw(@ImThe`j44bc{QoDYSdxAEf)2BTzW%rI8ICGRH9^B#N zeUPTo<(##3i=tH`=X5Kw4<^i!`?hYI^WR7E1|=-QFW-09wjNk~by=`m@24f-1;9_} zMFiybtNZvU2Bd;qS8+_5BcDgSZQu^~MCekjDMm%^Xdu*-x$z-MS5pe&FvF<(S*Wv* zEQn>)E$p-u(Mx%}OlUMUM6qvtoKdt^ukXF*0IQ;L8c1(;HmSZM1kSHF;&Y4dq`70D zW1fksf0ljaE!!uFVKLlS7N0Dd){&x|+1P4w3>{pWdI)ro`*7_W=>}1^ZTmo}3f~9J z!5yNBw7u;OF{*NH4!ZW_yxRABf$5i+Ca>ir1PA|zp3D@(UX;~q04mz==H%v%g9dR! z!9XdkRO#w$5ph(RPV3$j`kv)hZ(CKIaZ-ljJ7WB3E~nMD$wz9ru#|syqe}t&y0a-Q zUqpelSruZSk<=Jwz}2|q_N5}COj>29{gxXB`%pgKnTO*-)?}%bT0(uOu>=4_wIH_l zeh#=`<2~KocfiaYEd`(3p;|u%xYV^Pv(Yu#@{G(ejuTa%aUv0*8#M_e?al)4psmY{ zIas(FW2C?j&+o&=?M7R!3^{z0rZORU=g@Ei*-t5q7OTGMSD}^rfLodK9C~7)VW&NdeBt zp1uld0AmUsY;itOwIQK;@U3=KW>hj#-u0tD>-#}&A=4~zR4~{3ZuZ{9IX5Z%d*fHj=_bowsOR~jxg)Tn72sluevGJ) z*SHt)H9ry+`SvKG{=O{DC%X}-R=l#51b>2$tN0LEC0?tlEUznoYg4LP+>bqtl*TpC zxhA%WpLjOZqRwz4OkSL6Vzkt)?x3$@6uF+4a&r-Nosk2oe&MkYaj3DV{TN02^=HVa zchnc;>G$t4|6cKF0@*C@sBhA;9s-Ph_gG0>E+4%n3fVm$v9>`8h?$c%HHxJq1tzY8+0-7)O7W*5nv+>6_|6`=C3&Zfd5`B(lPg& zxoT*)7^*r4P^6s$eWWkk5!2e2KowUAHcX7d;`?q#Nv(%CSDgz6=vBG;`T3EGsc}lw zK(B&h*UW&Uz|DQ4(<#84Lq--c@Md#TARBy90N}%&CsbLYvRaDRG~a>#cP zatHcE=&dvZ+D(YS<h@HD#{m*If%NV%ttOqq1YLrNAF&wop!H*EJ{`NU^>NLP(RmY_LHS0U;*H zT#FM1Fh(sTu}jf{-=wOar3S91`SzGvS!yM2)Z0PGy_={#iX17$d#~{KO9+`zWT?NZ zE;v1)82P07S&XVFB+e~SWoo65!OPkjuIEZ7E=$uCYOD%5jAc_XHxfr>pkoD!(+BTD z9;|mV!}mJ9)Z(;e^$h`V24Y35s^*NQ5X#7?t;mwi(BX59&D&-ovuf0%r;b9%ujpvk zZYP8*+{Q+i`H%QheR)S{_ECR-zl6l?{39eSTc!pMLpr9^tE-7L`QeFyQsWVn z?W)(oL@6|1P%LtAWy+{fc*^An@GzAxGT^XRDI^l!@*OFDq6Wq&J?KShx_q6Ib+GtY zMOhu{^h@Higl3)7vFn>T&V?4L-!DJb`@Wv@vhAW>2%4}W3_U`PkG0k*)6|h_h^AWa zrg0Jn;{krY9b6d)9C3J@UEu@8dW03mF7xQdQ;D>cl(nNk{?Ak;fb8glhtA8mx*~s> zJ8~)Py9{PUsw;gj+Pb475>nO?`dEQtp~~v|pXwW8qG=tIMBUv?~2AO#(<|k-+I>{UD!HA@GWr}~P(fiA%5qB=_Xzkp& zBgz^{i7Mgw>>8d`mp}AiIgj${HxU5(UvN)cF{%>IUva`n236s-1T)>+Ysy%HDdB#k zI`$=vCQ$m80ni=>xn<#a>D=mp$QQ-3i7f!OiMC1=aI7ZA{EQ zO2(5dXuP;$Hh5zEy#H-z^35cm>MnClToFfsYJR0|q&k#3`Z3adtJgBbI8@|)Efd8C z{ZPdo&_}kOy?7BO{=SQ|@>F0Ualq9LqBj z?-n~E#$KhMfEK%XM~pAzn<}{&PRniQz2&yt&0eB}RiIA0PkqP*=)kqt7>adda`KAT zeEm28@-0QTg?Ts}2*QeF-oMq>l)Wk-83}!a`^+XbX5?3Ta{NC>2$>N>qDEB3y&a+? z$m3)arlV8(K*_FSuIAfrOKx!LOGQ$1`W_oo3>&nSZ|v(kV~*Hk z2cXyrxr63TfWgT(T8jEZbgRmGq-l`oyrZiHNc5C`wW^begfmQqc|qxi@*jNS;+|dc zKImZzdL-Dit6LrW%y1w%n4!UAZR}(M2hL~pb8>QmaO}-veM0rc10TqXz(X=r_~)vw zeV}RwU}w_b(|#(9exxhrFd^=vDJ8YHCmD^rAN|6@(;%)s)M*eq%g(8&`iGPjUoRKA z^DtB26Xgz>trMpDkpIFw$76D3$YSGRHo^yNJ-$$GpwqD->cC=2XyA4I>}11Rorie^ zk3yXM`u3LL1yC1NC3~H5ni@_w?}TVvAa5_>OQj4U?_HTciV+|B+b{zXI? zOZR|1<7uZu7#Uqg-AHlpl^-Q$Oo?c~(j9<0C<5NzB*4i6~%q`E#@7~5YrK{TcreNEuu}H+vcKLsQA7eT0v+gDf=sxH# z5wzN`aiv&WHM6H$#|LVG$BP#qQB`qna7BT1X#G@Vxb)64sD^yjF*Y;33ANyjwhp<$ zacVlQG!1stIN40om@KR%T+1)Bh%l~Jjle|n!2if-CNlX2-@TF_kNftWAN!w2Cs2B< zT1i#n*egk(lrua{#@l1{^;i6JvV=S8?f?#Xr)F# zXZ4TbMtIgsV`(!@>Cw&JY`*~pE31oDlWJ52(p>ZxyMP|Tv-ap$?L zoLSVZ^g0Fxqnm^3T3U0QHc6oX$bKOvCg!U~cdM~^waOMPJ%b8>&LYo;=lcI~0w$(u zv(h@<-y~2@pp}iUmtN;2rHta2;Z(Mu!723>6zlW>E=pLp^LQGpLuTHl{l_hJe4s`} zZu}fjn|M$QX7JFomxDR`jywc%Ptfk+-Tzgw3odC?Q+FtuolPvr#&6WQ*I%EEYf53@ zTn9I?Q6aQ1jYwcN#vN*lcoNaJ(@(hjY3KB&8IfqCI`R->36E97Li`UYq&t9=MlBaT z%Ftpp;Oy{GfNkU&-N?P$taF^~ZLS|-{JRE_k;PJ~?t7AT#z!>gJprYj15w8p%kylk zw<<2_2HzBJ#!6lmtuTd#G!Q}P%~;LJEpaGp=6E1W#Uc}~+JIkb7Usevy{JI4{HY=L z?L-yjzyI5a&pkzLXMUEoL>Q *?K7kEXtEPN>`&niDGZ8uJd;A5^D@Yy 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

BC-3{oZ@?Zef_2eNU0u6w28~J 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 z zV(LZYsCg#l=3U!MZXz0e`4c(|>5a9f^+0QlGN2spz^A8gKIPU}%VBC?QMs^NaW{%< z&m~ZkC_CdfT0qL;aO>7B4+1^=*LFwN519eCIR&sApTf2?F7pL^6gqdVv@}T8dxnYk z(`K3alNtJ)L;O5?(rG`g6<~(cU%#UCmNEQ3Cn7==5yf-W2?oMFIdO69k^hQswlJ?qXhd;N_lHIcLXVL`e?(UhFmPaW-OKK36U zW#du&G&8^FM`d9n041|U!&^HSmg1ha+2NDCGacF$dr{TLv`;OKA_UL@xoHh_`wJdJ z+u(-hFsp4WwmulHG#3j%B_tOD2H!3FMy?e-h}8=efgTy^E%_4=QKyjMBh;uLFr&ee zG`3bJi62|ZmAr^>UY3bZA|rtV9(f<2UUC;QERJo2@-=G9xaf6Z@!o2Yb897>n~-EM`>H*Aa3Obb-1CCu21XlAJ^DE)5i8yx}cL7eLcL&gsJE z|FS~u^o9m*MWZV*uq{y>A?e@&SB6?0tc*Jo=hyLXt{92Iz za~qPdXfuo8s`Ej-hQ(=6_tuc zVJLo>C~FRnv{s+?@@h8fE0!&KE3H1S9k+{lT@qYFvKjNddonnw{}-Z0aZ=S9TcbY<&=+ zDCw^$K0XyL7$f~mxi!Fac@f}g^p0&FsUxa(_cI=cii1YjW~iK)#3bcx~q~?L}YCC%~frt zr`!)pN<^WpENI+~L*d`_Zp!$-*H{%>LvMeJROPUgEAYj9r^Vb*UQ~Ad5N9CaJLYfa z3GQa8H!v$|STE8y9Jo~kri-i~EmR4hNMI8ZUkuj=y{8SlL>F^ z|HvyL;@5C6xkY~7y^5KQ|0gHVHwbS6i%k-^<&R$e+8AW;%gz1c8c=;L)>NcIwmpP&OD^JnJLA99z!)n0>vPPq$k0W6;F>Si z*X6o$S@(EjSW5BMQ1KjZ%9L!ESaxdVY|*>?8;(>GmfGY0OkHOGqd#!S%&GDYjaLW; z>xB6khWQyfB@g`tpQarX6P(-Ya?MxKXreU6zOcQCX)w6tsQ_m|$k)9jqV5u@3V6yu z-#2MdIr=!Z)L_)Qkj}-MpSVgqZbGL;1raPU6bJ*%k?C(ialQv zSZ~9w=pRvR+O6mD+4a0hT~2Tc+c8m=D_a@ddffWIZu~lTP_Nx!=P5y~DjQz3jx|Jh zDGeC~olHD(W1gm;eaWcQ_B)=F*67sjT9gnz8bZ? z9z|TRUgcC*xP(k>T%AHV@yQ5dv2%C>M)~{z<#800r(r_PiXYN};j`^rci`cY`&;rt z(F4(EK6NPIkS7lY)vw*DXM9OVBuS_N`?gw7fE)^x zFI#s~`=^X;@^!d~qA*+Ac*431v@f9b6FzYIyf_g}O;zk%W8|IK`XD@5YX4Jh z?#Xgjq^!Voh6iXLeXU@tZgA@(9bHMb(h0l>-9r>XUx6pFnQ^`bMw@mO4(_`h`qFiM z`7PUk%#lwz4J2L8Rb8^^!jS{IZM$}ACJ%N#`&Hxx5c*_tCiduXC9P1jlJ+VzH_w^UkvQtdK@vw zTR{#~7-xX{)xwba_KE*=hx8)UTFwp*FR-0^a0b+;qo1Lp`rr=Jug8PfPW_WiQTn&z zIzr(ugzIT0$e6gMp|Ev0^@n4g{1k_d?lQx5c+UF#;^>{NSxam^ zJ_=!Tb;Zk`qL3k)E>0m2PO=$laqq$ML4wqAPfwmxXLrgt60nk8*F#-fI~HYz%Lpml z)8prazxxjlkA10uaoepnW^DK+4x`=jFyF7LM|wY?-gS?2RyS^pP(P!$42ta&xFf}7 zt_FS$3o=fBl<>$B{DR{JU2q$p^EM=Npj9QDqimw!mY>%?KH}qLc%{14G-)`!zK!?n z*?o71l1O&=x}Y`h#oi_#vS3{yy+Nk9y^G6}{ULh?G@ZFsMTzLz2Cu%f0-UgqM4teW zqWk^M@5&Z=VJz=DhJme)D8Eo;bm>BkHv81w)k{q$NJlQOlLYIR1^rFAvqZmnoA)-` zP|sw}@P5p`4#9Y2tI3k0lFlOXG1d1^%t8i=tg%wvbfjw(6eg7wvdJKP;BL{<=xhD4 za76|e2qq%f`Ntnr!aCKibOcV7UPSWt!K_K{t$_shbh**xhL2z+(|UcQDzF_t?*3fS zuDrZ7sAE9{NTrf=>@sl+Vej6pCc5=)78W5T$-VI4{vBww0ebb+N+qULC$wxrV}^ZC zymoU%BkHj#t0hdSS_ta$1$cWZRyVLpy4S04mu+3X!PNP0ULoOr6Rh|oeAdo~yWAM4 zBmTrR>El1{Z0_J(+|k9Dz8I1kvb@E8^9SlNqJ1HgBpfdQAEohx%^4$}!JhqrnE}58 zKF_vD7~{)b;o@PofezaGhY^c-qM2W7VY(e88PhVQR}HyiyZwE0o}S(Ay}GFUK$Iof zEfsS3uW#+p>G#7(ql0=>AJ>ivpMEMkw@T?&z9{a1{bmAPAfmAnMA`95jd#--%BG>4 z^I|sxhu)XF9_V~aVni)5ZjnG(XnrANTvjFL(<6(OOa)36c;t`}B4Dv{E9304Ov9Pl zMBFxoNW76xrN1XaMG_#%`X$!-UYHp~Mx~&(`cYl+%CMGt=g)p=G1Db`S~8;=L!-V~ z*C~i3J>`zfOd^K)JJ!z)*9k?vG}!zT&oTHVUAR>E4xCmpU%Z>77e@NhyLH?61!X*m z+h`xiR0!wtgeSSUe<*~Za^#Q2%T{r+(OxK9Q9~8$UlZr^%=Y5e?lpOo7Ty-ZFo4n} z>z85rAHGYMe!l)hx9}}?n>vNxdWUVH=##yRRH+EPTdskbH`8ZfY*eHtk&36JU}Igg zvl^xN@=pFVts|Hw+bNKqb*(|lbFrSBQvc1s)8J1f@_8b)WfS`QgnH%avL2P!2=_2r zcUj1GaY_A>R1-MqNc923xsL4!mpCeHxO>l<+pL_Qk9F^G*l1?(S7+gP}VI>ot z{t{&MVS*O#_d;}xJu)E6(tzIBF|@qv+c}HD%a0@l?xZir<%uIgFptH?#1nxREtevU z#J8#V?ai7)wBJYeoz6--JWl_Y{s_H2jUwDB>O0VReYFt3QP?GkB}o$7PbF;AZ;zwI z5ypF3g+mooP%+&5d*|mm>V9=3v($XwLtbb?Q72F?K-at)ga^n-W2$M-&CHx$ zlz+c00|o-5Y65p)g~o6NMUjKe;X+6Vn*nyp4s(lSXt`?GMa0Y#f8V>h;X;sbML3J+rxUo*uE(bCE?BbNB(dKaowZ;IpU%a#&BC_C#?p^v ziTwomgB}Ud4M92rPf8hG|D2~#=HGlFJsL-&SV|LT-(d&ipG}A`Tsas34l%)t2`(f50j5%>hZIfH!T$8>g036hIu)!!_&*fqsoE z^3s%y-T84l%K(Sc0}}%lL66f4*M4etR3un<5_atm-LUW~#YeCgDdG5x`W{Z)NFJ`= zFxoEgN4O`J6PMN{p@I7sNy<>l>>?N~7dg5FGlY3lfwvIQ$erSCTDiI5RDiK&MQ{W; zhD79LR(~2C!8$5@ib%6JwAk(v(1=kr9pDn8V;GX55%8pX*^X@{v~wnyo0w#NkRpmc zdh`f@awkv9x_j=N!xWL|DT>~Y`66jDb@XHrW%>z+d@?TgJ1>@@b^_6h`#O1h$5&yi zy?SWrq{7GIxTnF4%$!@FZRB<15xSh==OxQ(T^nn^uJ;67BMAY+kStC6ahp(2VbSdK z;OQwdsX06$`#TZl)svsI?N)`$jPsdb=nqecMqjU6w_xSRHojB5^jYMj{>9QcXB8Slr@Kl`!f9sqex zU=#wO1mph;UyrgAVOqMI;Ob4Y`vXpxPRsa4`|^_LX+*x@8D0K5}Y2^>7^E?Cb~?hD$*5$Z-BIjGHtmYIW`X+p2{<|PqXEmwpbmGbiecdh^)i88@5AT$p)$dRBfN`Ycep-=Ki41lw zzw&7%n^*n?`8QNoY)nfOL`XzxsQMEg?vd@PMmecZ(o$1@iJYCJ&|1;2S=`{$hzZTH z2`TEYBlz8*GI` z76`p?;U~c3q+zH|x)2;m61TV?_tf+-0{OU+Ah9pkD?NR5p1wn}?F_@+avjrdm#nNJzY-ARUwXw2XFKc`9js zX3RKSxoXZ-GvddtcZ|H+lhW8E-UpY6(Ap&*Hu2IWE7R;Qim)2PZG?q$zg#@D`7rB1 z*bM8uqB;o?z&W{m{*#R_hJE^Tanw@>w`JB0F&q83v0r1gvTL_6^Pae23TT+ksr13O z&nP7J?8;#9JX8y#^=5ec|9VnL3mWtcDU&UeLhhK2`F9KRiNi<_<%{b*VW;Vl02b01 zr<~t;nM#7$Ex)=CJN7eFyzvmG*Q_WrJ~1&dfix9ZJ5+`xb;!yGg|lxK@T6IXRq3wk zFPLUUtBR2kIB_jGim19sD6D_)G>b^)Nr!=}+Jy|LqoQK+U&h|P)e&?N{dtB}c5V^BlMl@&B1&h>G~c;y%Nq}a%v-F9dn zK|SBew+BVu?@86!x*#v#Md5=6FoI0!l6U#sXWSWgMn$&x5&^yXxgB>zEt8B$=?>#_ z#zlUte&5u1Ss@8D@hwj`y;&hw|0^!D&GH$x1}~3D=}tFBYCcEykt>44$WG^SPahF! z))0|kkZpHLVo455B`ttol}l}0)}zvbUxC9T{qtSrt^qp1EJ~^_W)4kjzY(?l@@#3vxOc&T+`dHa z$mEUt;j|wP@K4Ne!Ye*MWcF~JF{2j`dkm}(iyv~@RU%;|upoOn`~!{1j>uwkbh^XHaB0+P6<=Fk3$z(*}N`ELsHSWeGar{q(RrP&^W z@jj$f*b7;~K=b}lTQsvNaq1I4v;5Xhewy}!kmR017jB8aO z+2bu3nGDxm)zX?=%LCUnVEvT!{yt11k^D`8hcmER$a=Xt4j1qH2mR@%^#+EQT`;T9 z=`Sw^uXdC>5P$fqnW*g7pEgglbo_fq#i;g&o_?l=+URxG%77jxGDF0|2Y|sW^hfVAcGwfXt>$G8JIUVJDNFg!5MzyT22uLnjV%+E-?*D{vFc1Lh{+~m! zVT1IX3N>}nOf-=#$F4vgKIVql>sejZe6|lW^Cm;0J=8A{+JAh4F^37EAacF4+8GVKra1Uxhp=Mmt!a6krK}lK7Gv!iSp~9 zSNKm0Kd&@!Hq-d*0cWU={IxT#3RiQjpK#f1UXdHQK%_TB>9#5p=pV?Juu0R!qiR0I zN4kDJ5t9Xl73U-vy@8ci{GBdy*n@)3_Zdu2He?O(|Zd8Vs$%GYa zPuum=c^S*R7DRhk`@nB0 z8E7JAM+_G11Eu7DCKtTnjVU;~A%*|X4+MoO?PF48XcG4waaWV9ITVkA6upyK5xg#@ zB|V_oo;s{kqwkTLPv!bG-I%G^IdNfxUf5Y&gkis)v$geU1v#U`x}l#()LQlP0B-$X zl9t6iR4Y12-}CHU-gWvr)zHVkqgEv)0Gy3VI;@oW`T_%WXukKd*kwzpfR8{+j1?msP6r;(i{Uye_^5Fl(U%C)*!DN(zitkVj~C#I{yiX~ zqCg#|ejGumm_|4*JC+7HYUV11Hbw?Q>;J5X894Z*jQ~cs6)+LKJATvbCOHqaYY;zSDAl+#XHcn!iet&OQ)yTvB zA-Q>ZDgAk9$=KLfX@|^ZJ7;G|T$a1VdDgkotYtelV=+&QHtzkGO_Rse?vxyxZI)AL zT06k^9=X)?eQ&W|gKlWIOY3;=M$kK}jM8X|l4jJhBT$8$_G8vlNyHv{SIKMhrgib{ zI1C=#_Wnyag38k&%k-qbW?$21nUay^<>k=T%w<|HC&6OxoHm)d5tA zlGEhJaK^YJOEoqV22=z)!PI~g+emB?-Q6F`GkX7!WwLek4Y$F{(M znWBp@0paH$M12UiV5}(t-iMtJ7l?@Dm} z`*b@P5DpO#n>OL(7r8xc#fe3?gA(4<)Qsfb%|oz#jL=XWjI9zdI)H??w3L*NV&r5H zuINBaZyC)L1>q){o0QOYPJ#PLAHu%pdHFkf4p%JmS+pHw10Ej!9_-}=4iAUmwcDer!;)rI z9z1JFg0>o0f1PjNtqKrbpR$9WOCxQE8>=~ht8HwSJQ~$iKu0bJ(*~!;%oE@ax%RWD z{8?LDn~$?|VdQrr?4m6h{`Kp)!91-6#$x*RM}u+^h=H8Z6lEHGHdBlqXFI4DQMhP; z6-4uJ%)7>l(9;-9+YbbcH^+$_zjjRrY$EoxYBDXrtZ^way;wf;<>jNBKiu2&hWK7t z1+ZUk=x%z%vPqnjf()Z1{5Rb0EPZJS2wZ|9k zL)W`x3`0264xMND`K$%&OTz&vnem)@t%K?@E9%S>Y!hR_K@4+W1vE}FKPiT;i&=$- z(?B8E*UH~+6qlalg_D1jrVJQl4#MWupM^z4BxQZ)I~Q=WzQVMZfPe#e;T(~&zjwLN zg_R{9MQ#Hk$1pgA7E4WSTAhKI*|D%N`c4vW~hoIn+H^xu!brPy%DUjwq+nTeA-pT9IJo|3Z#I#r6x-7l^X z5IPZ`URt<0tBJb`gk6KD6cEPI71n7S4!l3{YQvHeAGNc&3x=UQOegVX~RUb++(=$o+gTwy7bQFfTM zNdsCT)X#xvLeH(?BA|w<$OaX9`JxYBzXL{blv6ocRmkd=yBbh+TqJ-K`XMe65?0y9 z8AAuXuA$Olw(VPx_e;OZ$~(2)Voh%gCc=AK_#V4H_I&I+m@Kz+%LCqCzE8SNGseqP zo(XvXNYs>Y2mP@+7el>=o4BYFcz_WM`U`I&>g|Xb! zv$+e?`>;^Xq;l$)#+rU?+UpMywG58lfLslWGaf?ysrIPQ`p3FOAL;v!(~N%Ew>)1x zTSQUG3_BIXGsnvh>v2PF;;ulzq}bSKglKn(A#9haKhDalI^b|zW=~cvLvK{f7YIB* zypQHeNmp%=+xW!@FL>M!nRRof`rej~9r2>)Wb?0Oi$8QV0KO;?KJV~fZSVN>8}WH0 zyT`E_ZqPr(gb--IEvCDm5nsw392xBK<{X!=#Bj|2`0fw%Qvbk-+g1VzuFS~FoQeKp zaXhFj4~K*Q`u?Hi8UkMKfP%jXT}z(+_sbG8!taD!k;;OIs@im zHi`4tw0XiM4CjS-I@PNtb{88Yvi*0b13myU?@`oPSe3V)cY?S}lfII&_u%VAsDL)& z9X3@r%fXA*2MW`5RYB+uaaMOR~vb%6N`&#~r7?H2bvJ|g(&Uc@N?Q~bPvpe0{dM9- zi)>zU1oEsq^lxpM=kzVt0?B;gvcaz2-_GD=s&7)4QOs5#h)qTWXs&RM_!wn?I-Xc} z7YLc)H#Y+=tY(Uhvw?m_=2!bwWSAT18IQT6QthXIAKT_te!*oaMl~m|or(UveHZL} z1fOWwlRmi9@&@~LGaFXy`twQR|F>RpJaqz&~6k4D8i z50y@5?1vU40J#15D*(8iFy-uCA5}oyEu+xTv3|$K85meyn9It1(UD)WElNM(P4b(# zfw;m~xmy+*lJiFTd`Sx}&CSA_ycQXI z8M(LM^5h;3MDDO@iNp&rT{1Z;Mi;AJM^`{4S)(^t28NyxdYup4NiJ9p(|o=mWgpbd zNwo{A#uXI~*OhQVq6KtWr^iiwj)D6GrwC9*j#(PU`Uun?BW)epb1B$<2RKgrl`j7Q zWKud`()8MW;KUHBdsF@m0rtXa#Lv zLcHa>R#v0NL^EG#sfJ2pcftukS<|X#Ysw5Hc;t+0f{owb z^+aFm3nF>kT`n(aFEFpw={!N|{)g(nX;r|HyW}val7a5N)BN)ABum2wU~6Zq%^r-5 z@`Xg>1n3I9y~{G@h5;P1$Ou8PhcmHi%YKbt%wzxm#ls7_yZIEZE91v@=)6tOD&_HQ z^W1nG8M!)pPFPq1p48@cdg{2iVeeJ`U(=pROL{A@ce-?s#U0I3TzQza3@}KkKWjGq z&&*ZU4pIhEJl7I`DjGSL7WyclEey&q|9BgpiL^ z1HfBCuG|}a_eurk0*x^H{##CdX);6EV$#d} zg7ntxDidmgRpH_u#hPdLaWyo~_{^RW*jHCNBwZXG9lM^Ek4bCBY0g%xTkYNZ&(b?E z9rjfdRFRQ>6#gL@QmJx&%JeQa0qovjMaKTcyVBSgALs}urKi5rA?q)vVTObC_QzSu zfy-;ku^HIERnZ))_YMP4b!@OdziuS|#neG-;@7rnHS>*osk8qWyhm1-1JH5cd8_Ve zQ!9b=T!B)+Kg8gQ>lTO9)t}YNEo%;ZyIUkCMGW&QZl&(duQ@)hz59~T4hA6R^Pc#z z1PIK1%l{k-?c0~UYXYydBPf&mp$r$Z4aLB z8`Ivm?CjdCO(J~3+IrbQVYhZLMzc=(rW~TN}{Dn-NUb?;dfL`AAg%U8rg|E(`%?hYP zX^Ez$rX44)IBX`6QedToms5`}dFd`*sQaePChPMGTLZ)+Hku<;LJkScrzx9=`Oy+Lb$_yCC20;i3IFSNcd66Fz8kV)d)Q>YJpU}y1X>bt$X#wFTf2Mgz!#V|CXI6M*H+= zn1Di~lsuK*?Xblp2lZ?#`_~yt=nO8OQUxyw$ui>T*%*jA{@OZi?>NY)<{($7BDCW) zWu>LR0dl_aNjS)F47w*@R4l8Lvh#mxipc&QD6@6r8`Jc5d}ir;T4+F`I>B6-2U9>s znUvUaWSRqwT_KdTL~D)@xUZNHmEeFgfR%S~F$DeC=X>nfF%yaVFtq|a^j|}#z9!hv zK2Gvjjt1Wj9gKwlHkI`6e&a5{0LpcND?422WbS9+z8v(|G?FR&6O#h~596wE0NnCS z@U*Z+4LCi(A^Cs;?$o>FXkOTu`qed9V%H{L}TJc-jxupUN zH(#uauKa_@oQeFz3)hSLRj?2?UM3ZqR+J+;5*R)Z7T%77N3Q9lZf#0V3L6s8rC7)2 z*#uXf-M8u4Wj;qA&pGVtbko*0M;2_kV_zXoo0x(=fFf=2MYP+(obmAQAjITu zYiMCWZ)nTPA?G(hSV3+VU_gKf=vjmdupR_-{H3Nh<{B?IIJqrc|BeDNs#@)u5<*Y8 z(BYZM@D)K{qulPYS2PTLPWHluw^pN7RnqryK0!;7N3;N_%i?|*!dLhy*VGs4Lrepb z+e1h1*gTwg%@AehvI|v&VYi5X8$C*`SC$Q=nYzw<)3JZvxq~4dFt~{WpnuYdRX&zr)uP!5A*eJ-jt?HedFo8KYfhSpeu?;`hP`KpJyZ3*_tUBdR})ON4uzCjo6^c4q8o(Wqphty^+y}lQ)Rz-%#-?2`!we1I@ig+ zcW`!ZT;nsb1vE>~OF#pam_`xBQ42`f)`rwgB#fvbw_()eRNje?lPIac^~KO!HxMns z{0YyQ&o3wx;2M?qxNIJqjk(;UT@uS}xbi}9ZmhcUze_U8QwSGMRltuj_!$H$Xve{m z=()rSVwFNrHzlKofy7Y_S^IBAoddqr)pNt0ot>eJ(5ZXhtwWCO%M}*DLL}^{_z#Y# ztMXkR3=vEYIOzBqK6>I9-3W=K8=&@Qrzp}OVKFtNu6PjWYrn5Us08uuan>BChwsmZ zNx&omPzByefmlLhenGM7=F}ngzv4A;*2r7Fsj{?-`9D7rt^8BJAZR%VZVlNNJaa%! zu1>O`g@P+=Kcek|pp_EKi!L8)-mEHnunC(8HElejQyy>ZBy}a?S|}5jKG} zMI7_YITZdqulRHX=ktFe)k)`|hC>bLG@QFnW zR35<;6pV2)L85djlFyfUbLXEWC(l%P!*+)R@pQ-Z7t@-Hx<4DYFUNdNU?F|H1ax*s z_!}e;s1gUCk%+LoD+!Jh%8zPJ6V!pL+a9-kXPWl)#=pV+l^$O|OGh?aPLF<)cAnOY z{sadfkP~Xc)zsPDJtd93G%SfEl%_R=!OT@t0C_g*3}rycdk>+Hu)sIGZq?`kBFdT! z2o}6Na93cY|Jj>IKC8ZyeDkc}>Df2&hff@0|7z?4t~y2lLUuZR{Ys$(T5dtz=dH^x zZUIOh@16qUmeu_X#1Rl_umZGI0nMy*@%&^`o?sgjmyX))i*fviY5x^eNBJ30bhi3m z`D%3(tE?;M9*sv$3VjrZvKTKSB%~r+Sx$$AyKY$?&brqXvbKJcEQp-jTtC*JYzZ~o zwwED%`%51^J6{a(``7tdg6TzKRQ$hzds1gf`I?^6bBf7DBKo*+=AU2_CA_W>8_a10QRnEg@ItSMxX%^zfygfN|#fnVr%elfXyTjz8J7*zY&qO6+ zV4LCZdqU$(5xu>ioB=r&s;k2578czqQr@A;1T@GvpS(-p4{8n4Op1G(Tn$M$*B+E} znI1xUz6AmWRtdP{;Y20YdJr@*W-%|!at#c(#e3_u|Ngffm~vim=eCX4Jvzr@E9lJh zq6$CyhGZ@S->HQ zzEtMK7lgpN?nBT69^+v5sLGXb-0~yvjp451V@`a0p=S2@-(jA$WXoTuctqfVG^059TjR>4BNx zHoTkkFR)LW8UDRlbIs-$`zLkcV8fO*9)$7awLSVsYRU;vXgM@1hxR=Jsd+KFrNU=a z9#Fuzy(NGIzK$-KGi8xA1nvxjA&RH4eb{>J|FuiciN#_H*xtl3(GW%+1QK8!gZ93K zRX)qnvH|(Lm*XsqS4y``N=caw&N`#^q!e0D+b4RWzQjJqa0PtKp~)BGOj^jx%Ns5$ zE4u}`4;~mgC88TWx=#!wMqW;Be0v@A?|JMScpwHgO^wh}nMs4ebmEA17U3YT$oe-i z(SUriu&J7sj?EqpjR&pdaw-$$b?3?1x98SEP=mKyzU0KyYOz7_*0G}-8Oh}hP#@oV z8@i9aFP`bq3{2_#=cl5TwfjacETHAr&@74!{j=FMJoO+7_PQ|)z!R<*+!VyZP*C}1 z2bA>gH(r7e>j4K4MJI!3+4-vuus3FCO9LI-Rc)RvdEgT~o=+iU zY>S^@Cho+~`T=T`YDg;Cerkci0-VrMWSCS%&_pv`Z|sx4Y!D&MnQFZGH$!2LM@h?$ zr=NS^kG0-l&mDwzywY&RYXrEiudJ-JvO%sE{l&LF(7Ro2!|-it_#gH)SXtSjUHbu= zyq+5{^9wX;kqFd1ml$s032}YSU|xfT&#+)~@ZzA0$Ui{H%0t8D7~zOp`822372^w? z__DdekYO|h?KmGFpK^_=s}?SB)71OHsHD2(JlDXIT>LEF#1_H0uUG%a)^)&Bp|^je$jvHR#<40}WhLW~O|nyj zknE9F);Sd+lB{Ip*sJUn85s?GQ;1~GY|e3<^MB60Z}-0c_kBN~dvEvladZ5}^Lw7} z_xpUGOf-UN;>Bl=((m9Xga~i)PIb1uv)8c!m2PvF!&?>zq8R-hiZ~C$cj|x1T z(j^eG?gDQG`UzMGpggPj$$rNkXp;BzYBcS<#rrl@8bmF?K4qp)o(98-`TOgaBfB7t zKdzv2^DLFTqeBhM#`+knvJnu8woeUVDB)0klY&+`QQmi`2Cbj8PI_G5~fX%K&KS|2JxkH_u;> zq3n}?i=JD>(U0OLmCjBeQUbA&Dq|-A^TWWEZ3FCI98Y^tl9XxWk3-XZ1o&B~2UuFB z;8mdMnWHrn+&40pS;%#h@8skWp0ay||KqI^<>sY#U%RbAo3EgyF!DW!;#v`Anhn|~ zesy$qZZEi`{ z?n*(w9OKx{l}wg+fhm0ZwhThycgXuT7D<_H9!;7!E8^N^<=mr{dRtcSC4Zl2v63&E zmI+C72`ZelH#JJx7p4TCSh9e_2A+rMd)c3JN=QY@&eiY7daW*3ZWfEbT5c9@k zNkECKEZ zUfcWVAH6ssmnBf0?yK1CRY2i9L*lxyz}#-ov||q(&7D&>x(>q}BbLX0{|voC#Wz#i z6ovTxxkRfXp4%k`$&w;8aE)Mjq*n&|I5@^c)Rc_wz9>8W&yTj`pB>&N>1C2QKkrkG z7!YVwLgVe1u1bKcV-^rcbA>5Vq72HuVeY~ zvy3}udZ1J0oQyDO1+mF#i7?Ld+SRLNd~dL_^H4 z1j=*r`!)jJSQfO{8@yH6=SxS4jD7BwnH8u;e|@ADQ* z%I@%$`8DZuMx7A&K7u3rOW`@LGTjAupj#mcu2@Vo#mv|+u<)|jakEj#PJ+a>cp@5B z14>zt#FiG~$e$VvH&iU$`Hsuam7C1sd>e|s)bzLLC-BRWFXw{(FlXmC90Ad!JV`6> z(Wws*fcL3TXUHV>C|<+E`~6J>8&=DJ@NfZh6#v$Upj6$QFHnClDvfDn>htDvP*Rw? z<2L7KAeH_OaO5!o=$@+0hZ;cA*tb8Ooq42wUc<8|;QGzq+Ze@e#w8kZWWwtxq*lY9 zQT*%>+Mm4lf}phTb|!9K7j}~VIjfXjrJsYPNq}11!r>R7I_!5$gEZooYe-)vWuBD{ zS7e%`DgH`u9?IE=ta-L%oX4_K)93F8m;G|2kz=pQa)taQ=H+kf`*W)9C&*PRVK(?J zTaY@oyN7xWlM2tB6MvPC&R++CH<0x3_+#grNchwo$^T-EddU257aDv6P}fWek_+{E66c8Q*P3ls}2q zWdn`a&ggkm>S*42W0f=K@&M_%6t9REWS@b&#}By^-oAZ1B?+R>H09B| z3fG9neEOjkF1lMADF(!hsW+#N9Ks^XoK62;S{16B4G?zRugiP6Nu}= zIDU(g&SkvCY}(>d{lyw^WxK7xLco%y<*1Y*qX8b#u#Z7=!jonnzd;&^Dwp%8AN(#r zrBK;|kJtJz{-}We`cY{%Yt4aCq@-#4g=QNViCX@5nVuf}3|9Hhp*vKS(J>Y;Qp@g~ zx^q_1eZL^iMae)2ml<64Ny%)4n7PC zrPv6X`wB`L8ZX|tTsiJ6tleJ5op;v8#>JWnI%8nV25+M&rLyOUq}R{ZSOL@c5ZfmD zqteE*XdMQ5<+)~&NeR3v^c?-Q@$?iI$x9x+k_>K)9T{-VF3Zug9FG>B7U}^jC%s@t zKy70BrcU&h_)}j56c4%6#o_bOkXim4xLJ>H(*pdmzbE)!y}qVEIGz8gA>Bibgsl{< zFHJ~6L&fTYag8#Xt^p59E}m@Rm}k{6IfxMu*9ke-KP@J%t5h<>IZ zbUVo%%}rRZ4jz$N^DO4SsUq1D*A>i>hk_4(1WLJmp?3cKBfA&WcBA1!2a#;#I)F8p z1TLJ6Fs@WC}x4}~|Uz1rE)#*PxQM{?RYQk>yeREf!Z_>q)U1<$k zFFwmj#69?kl&G&6!6R<{4iTnQ)lrPE81OF+>M*%MxE|eJmRLEoy%`hxHuH#8_}-?U z`1e6Qva+acZ=r*i-$3zwQ{B7Wo(6qg#JsM3?+xT?4t&`+UneBAE@n*+fh!ED>F6yZ z8SoN?vCB{nN|<$QMZfV>3jwN8S)&rqE;Qb{7XUefu>XxVkvMbvdCs9hma~rDysHtd zHl@E(#MuhJdk&da^$VPp{{1o4>5+GiT~%({6l27{INbeo$xDRdsh0~(`-Ra|*+PcM zz?&ppoHS(CKN>wGuX@5fMrO~p(xk2!VO#8l<;g?V1sFV6gBlHLn1sKdfJ$TxFc`LJ zCHVgk6^8)>=aIgOsSc(fzFRs#{7PaoGj;Kn?+_q|Ge7eCf^V)WylcP`aY@Jt>YHTo zADS}uj8xS==0o+lSmAQjE`Z5)%TSPS%D(Hh^pMMK08>bi%?NaHXixUV$mcM9@Hmj? zmBtn1<-)f+Z#hBI$Xb8<3R*q004X%7q%Dagb)#1OJa$XhP z4j*l}rdeOJsdiiX%@yPvb_!S@AULj88vvIxUh z)H6f?RWPR9HF{xle}xV6O} zOnZ0`7TqyE=$!1W^v0)jP9(J|Y5HoU=ZkL!P}tf?P9`oZX@nWKKH1edngJTbg10?x z+-jir*F?%Tau67~b{oVWV@%BMD!yu}izGX`x!Puh;XBMtJN3zdQXT>uRwdNd5Zu82 z`fQadOg85|HAiz+IDh<5nURnDya&!!thm)z-Ln%0-MZieCWX%9^9D_n(eSP}@9orl z(;7)2?bi2h1LHX4_*EFVuOka1wXx1F--A}6t)tD3kz7Y3mZZac$z$i^lK_fou0|yd zj;Cond=$qXAD5fDUg)z4!0qANFk;?s$sj2G&uU(Hc#p47iTR}{Nn(&uySPrJ|Y zqj>vM9&%eVSN#mW%GHTQ9`OXf3Lq2LF3~td{b{`cuk|%YQrWYpiwyv^`FmNw);nDw zF=F~Cy~x6w+WTND@M!W3;BemLtxw-&`|(Ye0j`)Mszms4M2_|tRMY5G(RW-{3f zMpkpy?Pw7y^C-0=*vyjprE?KCS0-Mb{9PDtx-v^CGTC;QhkXe4ZDd$kb4A`aFZWTD z990fac~j=U_~pVE3s=Z7Wri4ge}(X;$SH9(fx#km<*w`uv?o$tmcsMZi$$g3e-siH z3O@muwgP#gEASKmv(;M7pFpZr7WYy(3oet9_uin<1FcPXHrbP%knijJBKsB+bbdqo;o`4Dxh9*n z|Mhg2zFctvN+Nl7FQ)XZBNTGt`=eOzYV%1i5Pu8+N>mOugyjHF-zP;j&{gPa^^d28 zoCEb=<}^#@6(QN5V8aEOHGs*eWI2vXjr5DQAeF$1NAVKw<@NHMx6_60o`m3c>)FT6 zV{#60X-$P07+&}tKr&2tDUvS*Yd3u2KIpAp6%i)^AuB(+@Kyz-o~J4`!g70(7nZsA_pMN2RKA$GH4^mmv~j(zLG)vcgxkv^ zC`9CPpv?At1Yk|-!0PIh;0~4Nr52r-yS0~X6v@EMv8L|byRT7#7SLxD zZssoj7;*GmZ)VK(X)LOBO8IxRQcuszG%+?Lj|srUWKPXe%6IsjFG~$O&DjHuZV6v( z;73uPxTdNTStMf^b89J$O7zMNtq{$?O#rRY3 zaoCPUl3*xJu2_@$3kr;OhVI(b{bK*tELOHPAazEyMOoa?LaUU7*^z2B8d5ZZN#!I0 z=nxdXpKNT$Fr)m=K*pY7ZWYLQ;$GDaEWj}231W~lE(5HXp?2!&u~=lW5I;JPP3c;J zTCoPb^uHm^x-r?U02}&H$}1MKgjY;aQq^^%5F`)5kv;PDi2;) zxEShcxx=lxtly#lA35>MLFqqrNl2#M=0$v03hF4Je$BRUK-rwe0Z1CCbkRigLKfjP z9^}R0QZ?41x7If(&G-ApgVyb%HneVJcX94MwM@|4?{kLI#VkC?$-e?B-MOz`t3MQs z8>V-t(Hf7m>0UiJpIqss%@udonUt$Nq>cbg^$VtQOnCgcvvW5r*_Cnep|th$Plk(Pe%1szr8 zCHcZnkl#rzh0gvZm|NtKV&?{6UhM>Dz~Yl0`$|WW0-`<`e30#PaaE*l|6fq?ECXS) zh)C0pdndB|H>wn+Vu<{BE##Vglg706d*2=BD{M16^W!7+XmqDDDQWKg7+&EruwCkV zmrR4R8xre{4UoK6ZiZ@-Yriim6C=SY`cYzEfrT(2>P$E7O~Qi!!-7X1&^-L4cO~`> zy7%rUcF^*NXa2|*il{FEFNxm^5T2ZaZ()|56fSyM--oSOfgZ z4tKV@!v}>74T_|$h_UlcI6Ex@#V(Ea`UcSKKxUebTwZ5sM6hR@eE%*4twJ$f9ko9o z`hRLxfF`L85}3!D89wvl9AJk2UZGS=w;e_IIXn;d)m8}oKGU0OC!Sn{1XhE(pArA? zRwmPs2M7;a3S(Kb!ZniRi|O>v_FeGrg64{KFDqlvL*h;qS*izCm+2U71+@w2V-Elz zPhj9jlk{mj+-6o(ZFD;u2V;HzUH{0rX*U>l7q`SyodZhLJnstU@5Y4{oJIh>gmWeN zA>lDmI#;PT+85iN%>vp2Rz#*!2o?oS=FK7WdB{NThYR$UvEZR19eF<^AtlG@-}|QC z8thGNy8xbxSTbN6=zf`&h)3PI#D_^$%QnwW_+`^ntxGPJWA=qvelvLT;8AYS+yh#O zT?2qxa%l;aa^?g2kJDJUm2!`&U#~ue`#TYjyuKbax}9%8=4p6=_WRcaVBnN`w30VS z)xM2HI~L9YU?0oiJ);2ao{wLHXr$ES+ttoHhZYYaO`&;ifaG;8Q|V6v(1^pN{$>h4 zThKdJh6v0XutYN%PHPtZn~}M)Q|nc?Rq$T_qC4y}hRFa9Wl9)YOdO$@0M$O1jS#7+ zif#0Rhn()(v724DYky=fkIQR>u15n!Q3j0+XEeUUL46M!kN1Q4?fnPeqv5iA7IK|= zixWH7$$sy7hAxx0x-+?|#WmBdrWz!$O+W(VW{u42VoTv+1u22vyT1BsBOnbUC<3Df z`AlMT3l<@gU;2}W3LBb zvwb>bo>aJ80&@#eBe?dsJJ4;=@mV#!beAFeU_N@#CpkJQjaG%MkPhfYe z@F5p@21TU5su&wy!Hib^TOXh)tZ7vFRD}yAa!LU1NSXggJG7N)(t%JCl;@^}>vpY9 zBvEtlkoN}U%h-=)=Z_sOho2PC{$RG#PzeIO9m-ZaBahY1#kUle2CONRhwqnV)fS_(SaM$U_r+*X|jLiDeF{}rr95_i$;m~7K;WT3W zIJu%WTa*=h71}#DPYeCH!Fw@Y0t~z1xusRUqEyulUAV-t&CSgqf;-PO!*9CBSPJ>k z5_pb|Pbt6e0*@V!()FW#(*|xg6x%A9$UFx?XBrJkC8dEbD7_61Zp9+KCp@tet|OI# zjTkJ}`+BRU-WCZD2VIq&0ZD^c5rdd^%eXkMZG!7BpAK#rQA2ER|08>7R{Eju7R{SB zQ3ui!Pab-S^NubrqeC%g)EOibvA0ffGqB@Q+lW zLpm=lJ(9(CGUN5>w*>`mETFGtvHCteZO!lVNuU3tJ3D=^tkBq}-pix(z4RY_-}i2t z_x-a?FSE0sSqptH%xu3?E@L(gsX#f?+}n&KYw>Abfq;gGd*vSWZeT2*53k(0Lj6#W z8^L?!G1DFf1duj9XcChNLFs-*pUTCMmG}Ot8F`-TuoGrcisg<8Y(kb(^qIeMD*?gg~k8 z7B2z$%@zb6utXovzM3uagO5{Z2uP5{O6uUW;d5uS*c5a$q7tB>ylZctXRXIMsE$aA z`(EK>!g7pZ?Q$&AE_ma4Y2TTTp%u~#HI#gT>hWrN@~d+$`gJ0Xy1w+zHIJHuoH^6#Jlm89REZj0M9$W3^WGj{jH z@;G!~kN5wxtqjJs;L85Y%Y~^o=2;f0sjt%zXy!V9>DNQ+8zlKI@DSkxxW?BHKonG! zZc<~Og`2l5!O)VE5-7X)Ac>{;?*KKQSYey$1Cs#NU;h970 zLYcZ&cG6{$lPJOEO^#)mE~@$9)0#+w#FcZs`v|e$S(G|A$CC@cuH9^z&3bl6NOrm+ zVLiG$CjIT$z0YiKWJm`6Rpt}>Fe^6W*4cVFDXA(@^wdF@Q>)P{7F%e-wFBZl9K89+U*&gr!1zVwuHIt=KPrLLDq$m7=Ese<8w_~u3}q{%(~enL zS=B%zuRu1!BH;t{`V1&EqcVk-XM;Ml{7?+l_4419lWy8M^bOO5qD=SiM#4GSq@rvv@9gU2i_X_Y9b(q~E zwbE94W%T=c+u!MN6nlf7%6k15Wl-!g@Y2{kVErhzRxJ*WdIKAr$z!sV2+G2jkDJIa9wi(=+j>i~Kdu@Yxqm*pg4#4swlTd%PeeU0XrW%ebdjlt0&4uI@bvi|8b{v21}MDVc_y}F z-L9z1?;n3r^&VyS{q-r|5sYa-%mrBo z?U51L3R~@=SCm<%oo^Lwf*I~GygkeNf(xsrYv^<8*g3PL2j}WpyvMEPvorJDAKtmE z@~P{Dd2@^3yI^yrPscAA3QWi$Ls}a)D%_0e6YNYD`yVD=uS+hxRrPouwGO)Eg?(ru zSg-Zn`H4avS(?A1GrmE^sSx%qCF-z+A~k|vLr>+BZmx{1_j({jx8K@N<(R}=rF)`W zPGoHI?BBX`E*Z$RSoPVg4YW4<^3YZk&!`H`1?P5jO;-12Vsd9VytSWFet%=Kv_zbD=HQ`yuW@!%!3pO+V9*iTiCqom#EE60h1ooDz4sdCweMPqDSXx4qJ z|M(auKVc|CJJ;!NY=>9Q+kTT=*-uvxb7w>y7kI=W{DcpB?!=HF!ErC;4CGXZ%oAeL z&B-;$rIz2q`u?h|WX1;Ur5PxY;aChJ|Nowlwg}ZA->=mg51fm`#*)nSo92WI%_?MHerbTt4o* zV0Zbb_w(z{P4J%HNyOA$=mx@i@n9Od=vNnkfU3JU`AHC4vb5K9=&o+On|`s|;%#~- zd(8$;nmP9zidS1bAJfs+E78N@9m|OAw#J1GiG-$N2 z+7cJs3mW)Z#6JQDymV=&Y1^l-+rwZox2>+c{Ko>a`Yh|x7@^uN|ERcL2>M?Emf zg9q2|phRHG&!Fn~@nCP;iMEWC12R86`F5j66_E)gc1CTjEahZ|NIm|j$yUEl%C{rK zGuHIm-_-iMcv2N^GN)a?8B`Lek|Jgi&6#uU{7=Y{#Bt}?oHQuOZRyyW>yYK{C8@gO z+|>Lx7-I;URr@mk<03xR5u;n$*f=@VtpH=io|iFDw)EGcK$0bm-dnD`A?t)9?(aD0 z&hdblXs+$ue5g~Xwj4I2U=WyEwwX6Qa%sOd^HCD*9JhIN^bS(TmRJbxZ%?&E7hHj4 zN?r0)UT`44VN(xZp0Q93--Y&h5I}?w6nxPpD@2F?u|C#6NF7h_$GN%>`lyYJQ^Amp z7y3r(0*w)&GX7z&;UsyNerp#otI27reg|E42ut%_6p~xQ??btGgh{meCK$(Rn6*Ark=4}_#ZL#xP>>> z>%-wJk_+~Z%8B!hU7r!?Tl5kls+RrqMY`NoJM^Ly7&HOfE~6EFYp<8PdmBn zh)=FS^8B{s4K~`n{qqY{-eWc1(Zf+LSU^}r(@-ht5beWnw@5AsfFic-{>tb)m$sa7 zYxpA>lj>{F@bi1v4DRAAl>-+0mkgdANLAJ$1=t&+mL1j5H9J`HHDnGVt>~2)hW&uc zMR!f#yzx4sS)Z969)3}pjx&UUf%QrHVR@oXGSUBk9Q@hO3O&Mv(*m!Uh0>|sWfb8~ z6^vZJSqId6|3)cUjg)TIkUZywP}va%;dKEmuupl2WHpyHfNb`SEjv5H%3U;KWgd9v>{33{vr+87k9Mwb1dO zOh3+VsLQ%BV%bHc{e1SnKQxBn>tr@Hl1Rgdd^hXn!r<%Skm$j4NMR@^64|X*G{qto z)qhY>R|iL3g*!d3%*-tf=2=TY8_4<$2*^HY35C(hF27G#1IRhUqYz7&Es7DBKh&yR($ZUMu)d=S0e$p^cQ=!a<(8Rm~ z7nM>xGHm*DFcl>?nbAd-#>NGHgK@!Dr?+tm{auhE*qbTsFim97JXzfUz3%X}kzd&k zqNi*6GLmF2`Uo(oiEdQCb}-A)oW6asXLIc{Li=M#olzHfZf-M>2VOKs?{AGk3L-&8 zMdb_9qLK4QbEZbDD0S{pw5?bWQ~J3cMJ*Bgl+tL^(4ibLuEO2~YxTXj{}JATKPp5j z6HI%A2%_Bn55#EC&`2}++AGl))bDSPoZf1(`8qz^ktpd|h)GLqhkoq5z5S)-Wd)@? z-+d039@7OgBm@;r$MnPE_U82c7ECYXMg`q89=y2S8y>(xV2aHhJMl3$>wArNsRU~B zQ}_|?9yLZ(^|9_qkDAZg;rP@8y}DoZ@9UTxD-6~+quc$Co}u#~8rii$eD1j&_1~w1 zK7d#_M^CI~Rh-CH#*+OZ#4XOKR^Z^%qm&b&!?WPi0@BQNNU(~3X@7Ea`4Yk0ujAcg zd2#b3HP_cvwPB~8(ewm%(ti0wjO*2yO!NzlBJZodgR zhvfNc&$GqW0l`KD$l3Ru6ASbM4}tdN>HhwhUjRvJR!YVh}XQklu;-q0HS> zNbL@ohlYr{!+Jk$pY6gF`tHh0{zxASQ9aW6EhP0*k^OD#2B!$-jo=k!MP0+VLlfb}@M z@wI$x`9lu|J8#iB_w{n%U9pu<=g;IP=qA-Lj#O#)5Ov!3kgaR=|0A^YO{B2i4_kGw zv^`STwxzSHE)0#i8qbD zIeGNDL?R&UCiE*CeXWBbh<@@3DxVhUQ$8AqDXhcs(;x?UDaYik6#D5`!gfW~o4c;- zXfvT6b{L-xq80Zre7Z-JcWQj(5XiyFbeJCAsAHE82i!zFr$^$8oNpV% z{_Zr6lAM=qnb_13;q!&*^|!{H1%;17EyLbt@6P`VlZjScjh=R8M}t9EFNIXm{Dw|Uoo%sG2A*zEw8+r zyUdv~(DCeQI8UqTVZVdkGIS5yxREPk<&z07)J zn07}eAJ)IbZaOajygGE+;$#l{5;M4C?FW5Aqd7QI!(~@C0z7UWqT3_%*GxaN{&Adz z#o{&%%IG;U`v`jhF-~uYc&n4h^