@north-light/cli 0.3.561 → 0.3.562
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +27 -4
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/bash-jobs.d.ts +5 -1
- package/dist/api/dto/broker-ops.d.ts +25 -1
- package/dist/api/dto/charges.d.ts +9 -2
- package/dist/api/dto/delivery.d.ts +26 -2
- package/dist/api/dto/execution-start.d.ts +57 -0
- package/dist/api/dto/execution-start.js +0 -0
- package/dist/api/dto/modelauth.d.ts +5 -0
- package/dist/api/dto/node-records.d.ts +16 -0
- package/dist/api/dto/objects.d.ts +1 -1
- package/dist/api/dto/reports.d.ts +0 -3
- package/dist/api/dto/run-events.d.ts +7 -1
- package/dist/api/dto/settings.d.ts +55 -0
- package/dist/api/dto/settings.js +0 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +11 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
- package/dist/cli.js +7 -7
- package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
- package/dist/clients/attach/chrome/bash-jobs.js +1 -1
- package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
- package/dist/clients/attach/chrome/roster.js +6 -4
- package/dist/clients/attach/overlays/auth.js +2 -2
- package/dist/clients/attach/render/chat-view.js +1 -1
- package/dist/clients/attach/viewer.js +550 -567
- package/dist/commands/canvas-config.js +2 -2
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human.js +2 -2
- package/dist/commands/node/bash.js +3 -3
- package/dist/commands/node/create.js +2 -2
- package/dist/commands/person-settings.d.ts +1 -0
- package/dist/commands/person-settings.js +1 -0
- package/dist/commands/pkg/market-manage.d.ts +2 -2
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +13 -13
- package/dist/commands/pkg/shared.js +1 -1
- package/dist/commands/profile/default.js +1 -1
- package/dist/commands/push.js +2 -3
- package/dist/commands/sys/config.js +1 -1
- package/dist/commands/sys/connect.js +1 -1
- package/dist/commands/sys/context/sysprompt.js +2 -2
- package/dist/commands/sys/doctor.js +1 -1
- package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
- package/dist/commands/sys/panels/keybindings-panel.js +1 -1
- package/dist/commands/sys/panels/models-panel.d.ts +8 -2
- package/dist/commands/sys/panels/models-panel.js +1 -1
- package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
- package/dist/commands/sys/panels/prefs-panel.js +1 -1
- package/dist/commands/sys/panels/profiles-panel.js +1 -1
- package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
- package/dist/commands/sys/panels/provider-panel.js +1 -1
- package/dist/commands/sys/provider-login.js +5 -5
- package/dist/commands/sys/setup-core.js +3 -3
- package/dist/commands/sys/setup-settings-runner.js +1 -1
- package/dist/commands/sys/spend.js +3 -3
- package/dist/commands/sys/update.js +1 -1
- package/dist/core/auth-file.d.ts +11 -19
- package/dist/core/auth-file.js +1 -2
- package/dist/core/auto-update.d.ts +1 -1
- package/dist/core/auto-update.js +4 -4
- package/dist/core/bash-jobs.d.ts +29 -71
- package/dist/core/bash-jobs.js +9 -10
- package/dist/core/blobs/directory.d.ts +3 -0
- package/dist/core/blobs/directory.js +1 -0
- package/dist/core/blobs/fault.d.ts +3 -0
- package/dist/core/blobs/fault.js +1 -0
- package/dist/core/blobs/index.d.ts +21 -0
- package/dist/core/blobs/index.js +1 -0
- package/dist/core/blobs/r2.d.ts +9 -0
- package/dist/core/blobs/r2.js +4 -0
- package/dist/core/bootstrap.d.ts +1 -1
- package/dist/core/bootstrap.js +2 -2
- package/dist/core/canvas/canvas.d.ts +9 -7
- package/dist/core/canvas/canvas.js +8 -8
- package/dist/core/canvas/crons.d.ts +3 -2
- package/dist/core/canvas/crons.js +3 -3
- package/dist/core/canvas/daemon-owner.d.ts +5 -0
- package/dist/core/canvas/daemon-owner.js +1 -1
- package/dist/core/canvas/db.d.ts +10 -9
- package/dist/core/canvas/db.js +4 -4
- package/dist/core/canvas/executions.d.ts +100 -4
- package/dist/core/canvas/executions.js +1 -1
- package/dist/core/canvas/export-sqlite.js +1 -1
- package/dist/core/canvas/focuses.d.ts +4 -0
- package/dist/core/canvas/focuses.js +1 -1
- package/dist/core/canvas/history.js +9 -9
- package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
- package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
- package/dist/core/canvas/node-tokens.d.ts +1 -4
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/paths.d.ts +0 -1
- package/dist/core/canvas/paths.js +1 -1
- package/dist/core/canvas/person-state.d.ts +26 -0
- package/dist/core/canvas/person-state.js +1 -0
- package/dist/core/canvas/person.d.ts +3 -10
- package/dist/core/canvas/person.js +7 -7
- package/dist/core/canvas/reconciler-leases.d.ts +12 -0
- package/dist/core/canvas/reconciler-leases.js +8 -0
- package/dist/core/canvas/recovery.d.ts +4 -0
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
- package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
- package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
- package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/canvas/sessions.d.ts +8 -11
- package/dist/core/canvas/sessions.js +1 -4
- package/dist/core/canvas/tree.d.ts +4 -2
- package/dist/core/canvas/tree.js +6 -6
- package/dist/core/canvas/types.d.ts +7 -0
- package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
- package/dist/core/command-plugins/bundle.d.ts +1 -1
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/command-plugins/compose.d.ts +1 -1
- package/dist/core/command-plugins/discovery.d.ts +0 -1
- package/dist/core/command-plugins/discovery.js +1 -1
- package/dist/core/command-plugins/help-addenda.js +1 -1
- package/dist/core/command-plugins/presence.d.ts +2 -0
- package/dist/core/command-plugins/presence.js +1 -1
- package/dist/core/config.d.ts +16 -15
- package/dist/core/config.js +1 -1
- package/dist/core/conversation-store/client-worker.js +1 -1
- package/dist/core/conversation-store/client.js +2 -2
- package/dist/core/conversation-store/index.d.ts +9 -11
- package/dist/core/conversation-store/index.js +1 -1
- package/dist/core/conversation-store/patch.js +1 -1
- package/dist/core/conversation-store/row-store.d.ts +14 -0
- package/dist/core/conversation-store/row-store.js +13 -0
- package/dist/core/directory/join.js +1 -1
- package/dist/core/env-name.d.ts +1 -0
- package/dist/core/env-name.js +1 -1
- package/dist/core/environment-recipe.d.ts +15 -0
- package/dist/core/environment-recipe.js +1 -0
- package/dist/core/environments.js +1 -1
- package/dist/core/events/emit.js +2 -2
- package/dist/core/events/run-event-chunks.d.ts +25 -0
- package/dist/core/events/run-event-chunks.js +5 -0
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/grants/mirror.d.ts +15 -0
- package/dist/core/grants/mirror.js +15 -3
- package/dist/core/grants/on-sync.d.ts +9 -5
- package/dist/core/grants/on-sync.js +1 -1
- package/dist/core/grants/remove.d.ts +1 -6
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/grants/watch.d.ts +15 -9
- package/dist/core/grants/watch.js +1 -1
- package/dist/core/graph/custom.js +8 -8
- package/dist/core/graph/jobs.d.ts +83 -5
- package/dist/core/graph/jobs.js +11 -2
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/graph/names.js +7 -7
- package/dist/core/graph/objects.d.ts +5 -1
- package/dist/core/graph/objects.js +11 -11
- package/dist/core/graph/reader.d.ts +3 -0
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/repo-sync/identity.d.ts +3 -1
- package/dist/core/graph/repo-sync/identity.js +1 -1
- package/dist/core/graph/repo-sync/repos.d.ts +1 -1
- package/dist/core/graph/repo-sync/repos.js +2 -2
- package/dist/core/graph/repo-sync/sync.js +2 -2
- package/dist/core/installed-plugins.js +1 -1
- package/dist/core/keybindings/persistence.d.ts +1 -1
- package/dist/core/keybindings/persistence.js +1 -1
- package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
- package/dist/core/layout-migrate/loose-keys.js +1 -1
- package/dist/core/ledger/account.d.ts +12 -0
- package/dist/core/ledger/account.js +2 -0
- package/dist/core/ledger/charges.d.ts +2 -2
- package/dist/core/ledger/charges.js +1 -1
- package/dist/core/ledger/check.d.ts +5 -1
- package/dist/core/ledger/check.js +1 -7
- package/dist/core/ledger/chunks.d.ts +8 -0
- package/dist/core/ledger/chunks.js +2 -0
- package/dist/core/ledger/index.d.ts +2 -1
- package/dist/core/ledger/index.js +1 -1
- package/dist/core/ledger/infra-allowance.d.ts +9 -0
- package/dist/core/ledger/infra-allowance.js +1 -0
- package/dist/core/ledger/limits.js +3 -3
- package/dist/core/ledger/payer.d.ts +1 -1
- package/dist/core/ledger/payer.js +1 -1
- package/dist/core/ledger/read.d.ts +14 -4
- package/dist/core/ledger/read.js +2 -3
- package/dist/core/ledger/totals.d.ts +5 -16
- package/dist/core/ledger/totals.js +4 -4
- package/dist/core/ledger/types.d.ts +12 -3
- package/dist/core/llm/run.js +2 -2
- package/dist/core/managed-providers.d.ts +72 -0
- package/dist/core/managed-providers.js +1 -0
- package/dist/core/model-auth/api-credential-store.d.ts +10 -0
- package/dist/core/model-auth/api-credential-store.js +1 -0
- package/dist/core/model-auth/daemon-login.d.ts +1 -1
- package/dist/core/model-auth/daemon-login.js +1 -1
- package/dist/core/model-auth/flows.d.ts +5 -4
- package/dist/core/model-auth/flows.js +1 -1
- package/dist/core/model-auth/handoff-jti.d.ts +3 -4
- package/dist/core/model-auth/handoff-jti.js +1 -1
- package/dist/core/model-auth/readiness.js +1 -1
- package/dist/core/model-config.d.ts +1 -1
- package/dist/core/model-config.js +1 -1
- package/dist/core/model-routes-pool.d.ts +14 -0
- package/dist/core/model-routes-pool.js +1 -0
- package/dist/core/model-routes.d.ts +11 -16
- package/dist/core/model-routes.js +1 -1
- package/dist/core/peer/ingest.d.ts +2 -0
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/stubs.d.ts +8 -0
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/permissions/evaluator.d.ts +7 -2
- package/dist/core/permissions/evaluator.js +2 -2
- package/dist/core/plugin-env.js +1 -1
- package/dist/core/pool-credential-store.d.ts +2 -5
- package/dist/core/pool-credential-store.js +1 -1
- package/dist/core/profiles/app-env-store.d.ts +5 -5
- package/dist/core/profiles/app-env-store.js +1 -2
- package/dist/core/profiles/default-binding.d.ts +5 -5
- package/dist/core/profiles/default-binding.js +1 -1
- package/dist/core/profiles/env-store.d.ts +10 -21
- package/dist/core/profiles/env-store.js +1 -2
- package/dist/core/profiles/manifest.d.ts +14 -18
- package/dist/core/profiles/manifest.js +1 -1
- package/dist/core/profiles/relocate.js +1 -1
- package/dist/core/profiles/select.js +21 -21
- package/dist/core/provider-credential.d.ts +1 -1
- package/dist/core/provider-credential.js +1 -1
- package/dist/core/provider-management.d.ts +3 -4
- package/dist/core/provider-management.js +1 -1
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/call.d.ts +3 -34
- package/dist/core/providers/description.d.ts +34 -0
- package/dist/core/providers/description.js +0 -0
- package/dist/core/providers/token-cache.d.ts +14 -14
- package/dist/core/providers/token-cache.js +1 -1
- package/dist/core/review/companion.js +1 -1
- package/dist/core/runs/activity.d.ts +1 -1
- package/dist/core/runs/activity.js +1 -1
- package/dist/core/runs/events.d.ts +7 -5
- package/dist/core/runs/events.js +2 -6
- package/dist/core/runs/operations.d.ts +1 -1
- package/dist/core/runs/operations.js +6 -6
- package/dist/core/runtime/bearings.js +1 -1
- package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
- package/dist/core/runtime/broker/attachments-fetch.js +1 -0
- package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
- package/dist/core/runtime/broker/bash-job-launch.js +1 -1
- package/dist/core/runtime/broker/boot-state.d.ts +9 -0
- package/dist/core/runtime/broker/boot-state.js +1 -0
- package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/client-registry.d.ts +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
- package/dist/core/runtime/broker/daemon-ops.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/frame-engine-control.js +1 -1
- package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
- package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
- package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
- package/dist/core/runtime/broker/job-reporter.js +2 -0
- package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
- package/dist/core/runtime/broker/model-adapter.js +3 -2
- package/dist/core/runtime/broker/signal-stream.js +1 -1
- package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
- package/dist/core/runtime/broker/spilled-body.js +1 -0
- package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
- package/dist/core/runtime/broker/summaries-post.js +1 -0
- package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
- package/dist/core/runtime/broker/tool-groups.js +1 -1
- package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
- package/dist/core/runtime/broker/turn-replies.js +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -2
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/deliver-live.js +1 -1
- package/dist/core/runtime/first-boot-rows.d.ts +8 -0
- package/dist/core/runtime/first-boot-rows.js +2 -0
- package/dist/core/runtime/fleet.d.ts +13 -12
- package/dist/core/runtime/front-door-login.js +1 -1
- package/dist/core/runtime/front-door.js +6 -6
- package/dist/core/runtime/headless-pi.js +1 -1
- package/dist/core/runtime/host.d.ts +14 -3
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/kickoff.d.ts +2 -14
- package/dist/core/runtime/kickoff.js +17 -19
- package/dist/core/runtime/launch-config-worker.js +1 -1
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/model-registry.js +1 -1
- package/dist/core/runtime/node-read.d.ts +4 -6
- package/dist/core/runtime/node-read.js +3 -3
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/outcome-document.d.ts +0 -1
- package/dist/core/runtime/outcome-document.js +1 -1
- package/dist/core/runtime/output-result.d.ts +15 -0
- package/dist/core/runtime/output-result.js +5 -0
- package/dist/core/runtime/placement.d.ts +17 -1
- package/dist/core/runtime/placement.js +1 -1
- package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
- package/dist/core/runtime/provider-credential-gate.js +2 -2
- package/dist/core/runtime/recovery-hold.d.ts +21 -2
- package/dist/core/runtime/recovery-hold.js +2 -1
- package/dist/core/runtime/recycle.js +1 -1
- package/dist/core/runtime/reset.js +1 -1
- package/dist/core/runtime/revive.d.ts +2 -2
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
- package/dist/core/runtime/runner/blaxel/client.js +1 -0
- package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
- package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
- package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
- package/dist/core/runtime/runner/blaxel/runner.js +3 -0
- package/dist/core/runtime/runner/index.d.ts +8 -0
- package/dist/core/runtime/runner/index.js +1 -0
- package/dist/core/runtime/runner/process.d.ts +11 -0
- package/dist/core/runtime/runner/process.js +1 -0
- package/dist/core/runtime/runner/types.d.ts +95 -0
- package/dist/core/runtime/runner/types.js +0 -0
- package/dist/core/runtime/situational-context.d.ts +3 -6
- package/dist/core/runtime/situational-context.js +2 -2
- package/dist/core/runtime/situational-live.js +1 -1
- package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
- package/dist/core/runtime/spawn-env-rows.js +1 -0
- package/dist/core/runtime/spawn-env.d.ts +4 -18
- package/dist/core/runtime/spawn-env.js +1 -1
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +2 -8
- package/dist/core/runtime/structured-output.d.ts +0 -67
- package/dist/core/runtime/structured-output.js +3 -8
- package/dist/core/runtime/tmux-driver.d.ts +8 -0
- package/dist/core/runtime/tmux-driver.js +6 -6
- package/dist/core/runtime/view-socket.d.ts +20 -0
- package/dist/core/runtime/view-socket.js +3 -1
- package/dist/core/runtime/warm-pool.d.ts +13 -1
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
- package/dist/core/scoped-state/legacy-profiles.js +1 -0
- package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
- package/dist/core/scoped-state/materialized-plugins.js +1 -0
- package/dist/core/scoped-state/migrate.d.ts +8 -1
- package/dist/core/scoped-state/migrate.js +3 -3
- package/dist/core/scoped-state/person-rows.d.ts +10 -0
- package/dist/core/scoped-state/person-rows.js +6 -0
- package/dist/core/scoped-state/person-store.d.ts +72 -0
- package/dist/core/scoped-state/person-store.js +1 -0
- package/dist/core/secrets/blaxel-launch.d.ts +24 -0
- package/dist/core/secrets/blaxel-launch.js +4 -0
- package/dist/core/secrets/blaxel-register.d.ts +2 -0
- package/dist/core/secrets/blaxel-register.js +1 -0
- package/dist/core/secrets/env-rows.d.ts +17 -0
- package/dist/core/secrets/env-rows.js +4 -0
- package/dist/core/secrets/environment-owner.d.ts +8 -0
- package/dist/core/secrets/environment-owner.js +1 -0
- package/dist/core/secrets/environment-secrets.d.ts +14 -0
- package/dist/core/secrets/environment-secrets.js +1 -0
- package/dist/core/secrets/index.d.ts +2 -0
- package/dist/core/secrets/index.js +1 -0
- package/dist/core/secrets/key.d.ts +3 -0
- package/dist/core/secrets/key.js +2 -0
- package/dist/core/secrets/seal.d.ts +18 -0
- package/dist/core/secrets/seal.js +1 -0
- package/dist/core/spaces/mounts.d.ts +1 -1
- package/dist/core/spaces/mounts.js +1 -1
- package/dist/core/spaces/process.d.ts +1 -1
- package/dist/core/spaces/stop.d.ts +7 -5
- package/dist/core/spaces/stop.js +2 -2
- package/dist/core/spaces/users.d.ts +2 -1
- package/dist/core/spaces/users.js +1 -1
- package/dist/core/storage/attachments.d.ts +11 -8
- package/dist/core/storage/attachments.js +1 -1
- package/dist/core/storage/tables.d.ts +4 -0
- package/dist/core/storage/tables.js +3 -3
- package/dist/core/subscription-state.d.ts +34 -72
- package/dist/core/subscription-state.js +1 -4
- package/dist/core/substrate/delivery/plan.js +1 -1
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/app-listener.js +10 -10
- package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
- package/dist/daemon/api/auth/person-bearer.js +1 -0
- package/dist/daemon/api/bridge.js +4 -4
- package/dist/daemon/api/document-listener.js +3 -3
- package/dist/daemon/api/environment-of.d.ts +18 -0
- package/dist/daemon/api/environment-of.js +5 -0
- package/dist/daemon/api/handlers/analytics.js +1 -1
- package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
- package/dist/daemon/api/handlers/app-profiles.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/bash.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
- package/dist/daemon/api/handlers/broker-signals.js +2 -2
- package/dist/daemon/api/handlers/canvas.js +4 -4
- package/dist/daemon/api/handlers/charges.d.ts +1 -1
- package/dist/daemon/api/handlers/charges.js +1 -1
- package/dist/daemon/api/handlers/conversations.d.ts +2 -0
- package/dist/daemon/api/handlers/conversations.js +3 -0
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/delivery.d.ts +1 -1
- package/dist/daemon/api/handlers/delivery.js +1 -1
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/exec.js +1 -1
- package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
- package/dist/daemon/api/handlers/execution-start.js +2 -0
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
- package/dist/daemon/api/handlers/git-credential.js +1 -0
- package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
- package/dist/daemon/api/handlers/hook-exec.js +1 -1
- package/dist/daemon/api/handlers/human-requests.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/messages.js +1 -1
- package/dist/daemon/api/handlers/model-config.js +1 -1
- package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/node-events.d.ts +6 -0
- package/dist/daemon/api/handlers/node-events.js +3 -3
- package/dist/daemon/api/handlers/node-outcomes.js +1 -1
- package/dist/daemon/api/handlers/node-records.js +3 -2
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/reports.js +9 -13
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/handlers/settings.d.ts +2 -0
- package/dist/daemon/api/handlers/settings.js +1 -0
- package/dist/daemon/api/handlers/structured-requests.js +1 -1
- package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
- package/dist/daemon/api/handlers/vendor-status.js +1 -1
- package/dist/daemon/api/handlers/worktree.js +1 -1
- package/dist/daemon/api/map.d.ts +2 -2
- package/dist/daemon/api/map.js +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/reader.d.ts +8 -1
- package/dist/daemon/api/reader.js +1 -1
- package/dist/daemon/api/router.d.ts +4 -2
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.d.ts +15 -3
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/api/vendor-signin.d.ts +2 -1
- package/dist/daemon/api/vendor-signin.js +19 -19
- package/dist/daemon/api/viewer-presence.d.ts +20 -6
- package/dist/daemon/api/viewer-presence.js +2 -1
- package/dist/daemon/broker-signals/bus.d.ts +48 -11
- package/dist/daemon/broker-signals/bus.js +1 -1
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/live-runs.d.ts +6 -0
- package/dist/daemon/cron/live-runs.js +1 -1
- package/dist/daemon/cron/run-socket.js +1 -1
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +10 -0
- package/dist/daemon/crtrd.js +18 -17
- package/dist/daemon/delivery/run-delivery.d.ts +1 -1
- package/dist/daemon/delivery/run-delivery.js +1 -1
- package/dist/daemon/due-sources.d.ts +1 -1
- package/dist/daemon/due-sources.js +1 -1
- package/dist/daemon/due.js +7 -6
- package/dist/daemon/fleet.d.ts +6 -6
- package/dist/daemon/fleet.js +9 -9
- package/dist/daemon/park-activity.d.ts +2 -1
- package/dist/daemon/park-activity.js +1 -1
- package/dist/daemon/park-pending.d.ts +20 -13
- package/dist/daemon/park-pending.js +3 -1
- package/dist/daemon/profile-delete.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
- package/dist/daemon/reconcilers/bash-deadline.js +1 -3
- package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
- package/dist/daemon/reconcilers/broker-supervision.js +4 -4
- package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
- package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
- package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
- package/dist/daemon/reconcilers/grant-effects.js +1 -0
- package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
- package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
- package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
- package/dist/daemon/reconcilers/ledger-r2.js +7 -0
- package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
- package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
- package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
- package/dist/daemon/reconcilers/ledger-storage.js +5 -0
- package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
- package/dist/daemon/reconcilers/live-obligation.js +1 -1
- package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
- package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
- package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
- package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
- package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
- package/dist/daemon/reconcilers/recipe-image.js +7 -0
- package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
- package/dist/daemon/reconcilers/row-claim.js +2 -0
- package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
- package/dist/daemon/reconcilers/storage-due-work.js +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
- package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
- package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
- package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
- package/dist/daemon/reconcilers/throttled-lane.js +1 -0
- package/dist/daemon/review/finish.js +1 -1
- package/dist/daemon/run-notices.d.ts +3 -0
- package/dist/daemon/run-notices.js +1 -1
- package/dist/daemon/runs/append-section.d.ts +4 -1
- package/dist/daemon/runs/append-section.js +2 -2
- package/dist/daemon/runs/observer.d.ts +3 -2
- package/dist/daemon/runs/observer.js +3 -2
- package/dist/daemon/runs/view-transport.d.ts +19 -0
- package/dist/daemon/runs/view-transport.js +1 -0
- package/dist/daemon/structured-requests/boot.d.ts +2 -1
- package/dist/daemon/structured-requests/boot.js +1 -1
- package/dist/daemon/structured-requests/effects.d.ts +4 -3
- package/dist/daemon/structured-requests/effects.js +1 -1
- package/dist/daemon/structured-requests/notifier.d.ts +2 -0
- package/dist/daemon/structured-requests/notifier.js +1 -1
- package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
- package/dist/daemon/structured-requests/sidecar.js +1 -1
- package/dist/launcher/validate.js +1 -1
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/dist/pi-extensions/broker-local.d.ts +3 -3
- package/dist/pi-extensions/broker-local.js +1 -2
- package/dist/pi-extensions/canvas-bash-valve.js +6 -6
- package/dist/pi-extensions/canvas-context-intro.js +2 -2
- package/dist/pi-extensions/canvas-elapsed.js +2 -2
- package/dist/pi-extensions/canvas-goal-capture.js +1 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
- package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
- package/dist/pi-extensions/canvas-review-boundary.js +3 -3
- package/dist/pi-extensions/canvas-stophook.js +1 -1
- package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
- package/dist/pi-extensions/canvas-structured-output.js +6 -11
- package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
- package/dist/pi-extensions/canvas-tool-guide.js +6 -6
- package/dist/shared/inbox-entry-body.js +7 -7
- package/dist/types.d.ts +12 -0
- package/docs/sdk/release-notes.md +4 -0
- package/package.json +7 -4
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/git-credential-crtr +33 -0
- package/scripts/sandbox-bootstrap.sh +54 -0
- package/scripts/sandbox-start.mjs +320 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
- package/dist/core/conversation-store/file-store.d.ts +0 -37
- package/dist/core/conversation-store/file-store.js +0 -9
- package/dist/core/conversation-store/listener.d.ts +0 -4
- package/dist/core/conversation-store/listener.js +0 -8
- package/dist/core/scoped-state/profiles.d.ts +0 -27
- package/dist/core/scoped-state/profiles.js +0 -1
- package/dist/core/scoped-state/providers.d.ts +0 -24
- package/dist/core/scoped-state/providers.js +0 -1
- package/dist/core/scoped-state/settings.d.ts +0 -28
- package/dist/core/scoped-state/settings.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
var D=Object.defineProperty;var p=(n,s)=>D(n,"name",{value:s,configurable:!0});import{envGrantee as F,envProfileId as b}from"../../shared/env.js";import{requiredPluginEnv as q}from"../../core/plugin-env.js";import{createHash as x}from"node:crypto";import{lstat as w,readFile as T,readdir as _,readlink as C}from"node:fs/promises";import{dirname as E,isAbsolute as S,join as h,relative as I,resolve as j,sep as H}from"node:path";import{defineLeaf as M}from"../../core/command.js";import{normalizeRequires as L,readConfig as B,updateConfig as G}from"../../core/config.js";import{scopeRoot as A,listScopes as K,marketplacesDir as z,pluginsDir as W}from"../../core/scope.js";import{readMarketplaceManifest as J,readPluginManifest as V}from"../../core/manifest.js";import{pathExists as $,listDirs as O,removePath as U}from"../../core/fs-utils.js";import{lsRemote as Z}from"../../core/git.js";import{cliClient as Q}from"../api-client.js";import{detectNerdFont as X,nerdFontInstallCommand as Y,NERD_FONT_SELECT_HINT as R}from"../../core/runtime/nerd-font.js";import{detectPackageManager as ee}from"./setup-core.js";import{validateEffectiveCommandPlugins as ne}from"../../core/command-plugins/discovery.js";import{adaptPluginContributions as te}from"../../core/command-plugins/compose.js";import{resolveCommandRegistry as ie}from"../../core/command-manifests/registry.js";import{applyCommandExtensions as oe,orphanCommandExtensionValidations as se}from"../../core/command-plugins/extensions.js";import{discoverHookRegistry as ae}from"../../core/command-hooks/discovery.js";import{resolveBinContributions as re}from"../../core/runtime/bin-contributions.js";import{inspectHumanActions as ce}from"../../core/human-actions.js";import{buildBrokerEnv as le,resolvePathExecutable as ue}from"../../core/runtime/spawn-env.js";import{listAllPlugins as de}from"../../core/resolver.js";import{SUBTREE_NAMES as me,coreCommandPaths as pe,coreHookCatalog as fe,extensibleCoreBranches as he}from"../../build-root.js";import{processGeneration as ge,processLocatedGeneration as $e,runtimeManifest as y,selectedGeneration as ye}from"./shared.js";import{CONFIG_FILE as ve}from"../../types.js";function f(n,s,t){return{scope:n,name:s,status:"pass",message:t}}p(f,"pass");function m(n,s,t,r){return{scope:n,name:s,status:"fail",message:t,...r?{remediation:r}:{}}}p(m,"failCheck");function k(n){try{switch(n.kind){case"remove_config_key":{if(!n.scope||!n.configKey)return!1;const s=n.configKey.split(".");return G(n.scope,t=>{let r=t;for(let i=0;i<s.length-1;i++){const e=r[s[i]];if(typeof e!="object"||e===null)return;r=e}delete r[s[s.length-1]]}),!0}case"rm_path":return n.path?(U(n.path),!0):!1;case"human_action":return!1;default:return!1}}catch{return!1}}p(k,"applyRemediation");function ke(n){return[`${n.link} ${n.kind}`,`disposition=${n.retry.disposition}`,typeof n.retry.attempt=="number"&&typeof n.retry.max=="number"?`attempt=${n.retry.attempt}/${n.retry.max}`:void 0,typeof n.retry.nextAt=="string"?`nextAt=${n.retry.nextAt}`:void 0].filter(t=>t!==void 0).join(" \xB7 ")}p(ke,"faultMessage");function P(n){return/^[A-Za-z0-9._:/@%+=,-]+$/u.test(n)?n:`'${n.replaceAll("'","'\\''")}'`}p(P,"shellArgument");function we(n,s){const t={connection:"restore connectivity","rate-limit":"wait for the rate limit to clear",overloaded:"wait for load to drop",auth:"re-authenticate",protocol:"fix the protocol mismatch","context-overflow":"shrink the task or loaded context, or choose a larger-context model before spawning the node again",other:"fix the underlying issue",wedged:"read `crtr canvas read wedged-child-on-runaway-bash` \u2014 if a runaway subprocess is to blame, kill it (not the node); if the broker had no subprocess, the daemon SIGTERM-kicks it and starts a fresh recovery cycle automatically","model-not-found":"pass a valid `--model provider/id` or configure credentials for an authenticated provider, then revive the node (`crtr node lifecycle revive <id>`)"},r=n.retry.disposition==="auto"?n.retry.by==="client"?"client redialing":typeof n.retry.attempt=="number"&&typeof n.retry.max=="number"?`auto-retrying, attempt ${n.retry.attempt}/${n.retry.max}`:"auto-retrying":n.retry.disposition==="fatal"?"parked fatal":"manual",i=`crtr sys logs --node ${P(s)} --operation ${P(n.operation_id)}`;return{kind:"human_action",description:`${t[n.kind]}; ${r}; inspect the correlated operation with ${i}`,command:i}}p(we,"faultRemediation");async function be(n){const s=[],t=new Set(n),r=new Set(me),[i,e]=await Promise.all([pe(),he()]),c=ne(r,void 0,void 0,i,e);for(const l of c){const o=l.plugin;if(!t.has(o.scope))continue;const u=l.issues.filter(a=>a.code!=="command_collision");if(u.length===0){const a=l.contributions.map(d=>d.node.name).join(", ");s.push(f(o.scope,`plugin:${o.name}:commands`,a.length>0?`contributes top-level command(s): ${a}`:"command manifest valid"));continue}u.forEach((a,d)=>{const g={kind:"human_action",description:`${a.next} Inspect with \`pkg plugin show ${o.name}\`; then update (\`pkg plugin update --name ${o.name}\`) or remove (\`pkg plugin remove ${o.name}\`) the plugin.`},v=a.path!==void 0?` at ${a.path}`:"";s.push(m(o.scope,`plugin:${o.name}:commands[${d}]`,`${a.code}${v}: ${a.message} (received ${a.received}, expected ${a.expected})`,g))})}if(t.has("project")){const l=ie(te(c.flatMap(a=>a.contributions)).contributions,r),u=[...oe(l.contributions).validations,...se(l.contributions)];for(const a of u){if(a.issue===void 0){s.push(f("project",`extension:${a.branch}`,`repository fragment valid: ${a.fragmentPath}`));continue}const d=a.issue;s.push(m("project",`extension:${a.branch}`,`${d.code} at ${d.path??a.fragmentPath}: ${d.message} (received ${d.received}, expected ${d.expected})`,{kind:"human_action",description:d.next}))}}return s}p(be,"runCommandPluginChecks");async function xe(n){const s=new Set(n),t=ae(await fe()),r=[];for(const i of t.validations){const e=i.plugin;if(!s.has(e.scope))continue;const c=t.issues.filter(l=>l.plugin===e.name&&l.scope===e.scope);if(c.length===0){const l=i.hooks.map(u=>`${u.target} [${u.phase} ${u.op}]`),o=(i.manifest?.schemaVersion===2?i.manifest.lifecycle:[]).map(u=>`${u.event} [${u.phase} ${u.op}; effects: ${u.effects.join(", ")}]`);r.push(f(e.scope,`plugin:${e.name}:hooks`,`effective hook declaration(s): ${[...l,...o].join("; ")}`));continue}c.forEach((l,o)=>{const u=i.manifestPath??h(e.root,".crouter-plugin","plugin.json"),a=l.path!==void 0?`${u}:${l.path}`:u;r.push(m(e.scope,`plugin:${e.name}:hooks[${o}]`,`${l.code} at ${a}: ${l.message} (received ${l.received}, expected ${l.expected})`,{kind:"human_action",description:`${l.next} Inspect with \`crtr pkg plugin show ${e.name}\`; then disable (\`crtr pkg plugin disable ${e.name} --scope ${e.scope}\`), update (\`crtr pkg plugin update --name ${e.name}\`), or remove (\`crtr pkg plugin remove ${e.name}\`) the plugin.`}))})}return r}p(xe,"runHookPluginChecks");function _e(n){const s=new Set(n),{bins:t,issues:r}=re(process.cwd(),b()||null),i=[];for(const e of r)s.has(e.scope)&&i.push(m(e.scope,`bin:${e.name}`,`${e.origin}: ${e.message}`,{kind:"human_action",description:`Fix the \`bin.${e.name}\` declaration in ${e.origin} \u2014 point it at an executable regular file inside that contributor's own root, or remove it. A same-precedence collision is resolved by renaming one of the claimants.`}));for(const e of t.values())s.has(e.scope)&&i.push(f(e.scope,`bin:${e.name}`,`${e.origin} \u2192 ${e.target}`));return i}p(_e,"runBinContributionChecks");function Ce(n){const s=new Set(n),t=h(A("user"),ve),r=[],i=new Map;for(const e of ce(process.cwd())){if("kind"in e){if(e.kind==="unknown_action")continue;const u=e.configOrigin===t?"user":"project",a=e.block===!0,d=a?void 0:i.get(e.name);if(!a&&d===void 0&&i.set(e.name,e.configOrigin),!s.has(u))continue;const g=a?`humanActions:block@${e.configOrigin}`:`humanActions:${e.name}@${e.configOrigin}`,v={kind:"human_action",description:a?`Fix or remove the \`humanActions\` block in ${e.configOrigin}. Actions run argv directly without a shell.`:`Fix or remove the \`humanActions.${e.name}\` declaration in ${e.configOrigin}. Actions run argv directly without a shell.`};d!==void 0?r.push({scope:u,name:g,status:"pass",message:`warning: shadowed by ${d}; ${e.configOrigin}: ${e.reason}`,remediation:v}):r.push(m(u,g,`${e.configOrigin}: ${e.reason}`,v));continue}const c=e.configOrigin===t?"user":"project",l=i.get(e.name);if(l===void 0&&i.set(e.name,e.configOrigin),!s.has(c))continue;const o=l===void 0?"":`shadowed by ${l}; `;r.push(f(c,`humanActions:${e.name}@${e.configOrigin}`,`${o}${e.configOrigin} \u2192 ${e.argv[0]} (cwd ${e.cwd})`))}return r}p(Ce,"runHumanActionChecks");function Ee(n){const s=new Set(n),t=le({argv:[],env:{CRTR_NODE_CWD:process.cwd(),CRTR_PROFILE_ID:b()??"",CRTR_GRANTEE:F()??"app:terminal"}}),r=[];for(const i of de())if(!(!s.has(i.scope)||!i.enabled)){for(const e of q(i.manifest.env))t[e]!==void 0?r.push(f(i.scope,`env:${e}`,`plugin "${i.name}" has ${e}`)):r.push(m(i.scope,`env:${e}`,`plugin "${i.name}" needs ${e}`,{kind:"human_action",description:`crtr sys env set --name ${e}`}));if(i.manifest.requires!==void 0)for(const[e,c]of Object.entries(L(i.manifest.requires))){const l=ue(e,t,process.cwd());l!==null?r.push(f(i.scope,`requires:${e}`,`plugin "${i.name}" \u2192 ${l}`)):r.push(m(i.scope,`requires:${e}`,`plugin "${i.name}" requires executable "${e}" on PATH`,{kind:"human_action",description:c}))}}return r}p(Ee,"runRequiresChecks");async function Se(n){const s=[];async function t(r){for(const i of await _(r)){const e=h(r,i),c=I(n,e).replaceAll(H,"/");if(c==="generation.json")continue;const l=await w(e);if(l.isDirectory())await t(e);else if(l.isFile()){const o=await T(e);s.push({path:c,size:l.size,sha256:x("sha256").update(o).digest("hex")})}else if(l.isSymbolicLink()){const o=await C(e);if(S(o)||!y.contained(n,j(E(e),o)))throw new Error(`generation symlink escapes root: ${c}`);s.push({path:c,size:Buffer.byteLength(o),sha256:x("sha256").update(o).digest("hex"),symlink:!0})}else throw new Error(`unsupported generation file: ${c}`)}}return p(t,"walk"),await t(n),y.sortInventory(s)}p(Se,"inventory");async function je(n){async function s(t){const r=await w(t);if(!r.isDirectory()||r.isSymbolicLink())throw new Error(`generation directory is invalid: ${t}`);if((r.mode&146)!==0)throw new Error(`generation path is writable: ${t}`);for(const i of await _(t)){const e=h(t,i),c=await w(e);if(c.isDirectory())await s(e);else if(c.isFile()){if((c.mode&146)!==0)throw new Error(`generation path is writable: ${e}`)}else if(c.isSymbolicLink()){const l=await C(e);if(S(l)||!y.contained(n,j(E(e),l)))throw new Error(`generation symlink escapes root: ${e}`)}else throw new Error(`unsupported generation file: ${e}`)}}p(s,"walk"),await s(n)}p(je,"verifySealed");function Ae(){const n=X();if(n.installed)return f("user","terminal:nerd-font",`installed (${n.evidence}) \u2014 if icons still render as boxes, ${R}`);const s=Y(ee());return m("user","terminal:nerd-font","no Nerd Font installed \u2014 viewer tool/command icons will render as tofu boxes",{kind:"human_action",description:`Install a Nerd Font (${s.hint}), ${R}`,...s.command?{command:s.command}:{}})}p(Ae,"runNerdFontCheck");async function N(n,s,t){if(s.generation===null){const c=s.error??"no runtime generation";return[t?f("canvas",`runtime:${n}`,c):m("canvas",`runtime:${n}`,c)]}const r=s.generation,i=[],e=y.validateManifestShape(r.manifest,{id:r.id,entryName:"cli"});if(!e.ok)return[m("canvas",`runtime:${n}:manifest`,e.error)];i.push(f("canvas",`runtime:${n}:manifest`,`${r.id} ${r.manifest.packageVersion}`));try{await je(r.root),i.push(f("canvas",`runtime:${n}:sealing`,"generation tree is sealed and contained"))}catch(c){i.push(m("canvas",`runtime:${n}:sealing`,c instanceof Error?c.message:String(c)))}try{const c=await Se(r.root),l=y.sortInventory(r.manifest.files);JSON.stringify(r.manifest.files)!==JSON.stringify(l)?i.push(m("canvas",`runtime:${n}:inventory`,"manifest inventory is not in canonical runtime-generation-manifest order")):JSON.stringify(c)!==JSON.stringify(l)?i.push(m("canvas",`runtime:${n}:inventory`,"full generation inventory hash does not match generation.json")):i.push(f("canvas",`runtime:${n}:inventory`,`${c.length} files hash-verified`))}catch(c){i.push(m("canvas",`runtime:${n}:inventory`,c instanceof Error?c.message:String(c)))}return i}p(N,"generationChecks");async function Oe(){const n=[];for(const s of await Q().listNodes()){const t=s.fault??null;if(t===null)continue;const r=`fault:${s.node_id}:${t.link}`,i=we(t,s.node_id);n.push(m("canvas",r,ke(t),i))}return n}p(Oe,"runFaultChecks");function Re(n,s){const t=[];if(!A(n))return t;const i=B(n),e=z(n);for(const o of Object.keys(i.marketplaces)){if(!e){t.push(m(n,`marketplace:${o}:dir`,"marketplaces directory unavailable"));continue}const u=h(e,o);if($(u))t.push(f(n,`marketplace:${o}:dir`,"directory exists"));else{const a={kind:"remove_config_key",description:`Drop stale config entry config.${n}.marketplaces.${o}`,scope:n,configKey:`marketplaces.${o}`};s.fix&&k(a)?t.push({scope:n,name:`marketplace:${o}:dir`,status:"fail",message:"directory missing \u2014 removed stale config entry",fixed:!0,remediation:a}):t.push(m(n,`marketplace:${o}:dir`,`directory missing: ${u}`,a))}}const c=W(n);for(const o of Object.keys(i.plugins)){if(!c){t.push(m(n,`plugin:${o}:dir`,"plugins directory unavailable"));continue}const u=h(c,o);if($(u))t.push(f(n,`plugin:${o}:dir`,"directory exists"));else{const a={kind:"remove_config_key",description:`Drop stale config entry config.${n}.plugins.${o}`,scope:n,configKey:`plugins.${o}`};s.fix&&k(a)?t.push({scope:n,name:`plugin:${o}:dir`,status:"fail",message:"directory missing \u2014 removed stale config entry",fixed:!0,remediation:a}):t.push(m(n,`plugin:${o}:dir`,`directory missing: ${u}`,a))}}if(e&&$(e))for(const o of O(e)){const u=h(e,o),a=J(u);if(a){t.push(f(n,`marketplace:${o}:manifest`,"manifest valid"));for(const d of a.plugins){if(d.source.startsWith("http://")||d.source.startsWith("https://")||d.source.startsWith("git@"))continue;const g=h(u,d.source);$(g)?t.push(f(n,`marketplace:${o}:plugin-source:${d.name}`,"source resolves")):t.push(m(n,`marketplace:${o}:plugin-source:${d.name}`,`source path does not resolve: ${g}`))}}else{const d={kind:"rm_path",description:"Remove dangling marketplace directory (no valid .crouter-marketplace/marketplace.json)",path:u};s.fix&&k(d)?t.push({scope:n,name:`marketplace:${o}:manifest`,status:"fail",message:"no valid marketplace.json \u2014 removed dangling directory",fixed:!0,remediation:d}):t.push(m(n,`marketplace:${o}:manifest`,`no valid marketplace.json in ${u}`,d))}}const l=new Map;if(c&&$(c))for(const o of O(c)){const u=h(c,o),a=V(u);if(!a){const d={kind:"rm_path",description:"Remove dangling plugin directory (no valid .crouter-plugin/plugin.json)",path:u};s.fix&&k(d)?t.push({scope:n,name:`plugin:${o}:manifest`,status:"fail",message:"no valid plugin.json \u2014 removed dangling directory",fixed:!0,remediation:d}):t.push(m(n,`plugin:${o}:manifest`,`no valid plugin.json in ${u}`,d));continue}t.push(f(n,`plugin:${o}:manifest`,"manifest valid")),l.has(o)?t.push(m(n,`plugin:${o}:duplicate`,`duplicate plugin name within scope (also at ${l.get(o)})`)):l.set(o,u),s.remote&&a.source&&(Z(a.source).status!==0?t.push(m(n,`plugin:${o}:remote`,`git remote unreachable: ${a.source}`)):t.push(f(n,`plugin:${o}:remote`,"git remote reachable")))}return t}p(Re,"runChecksForScope");const an=M({name:"doctor",description:"diagnose installation health",whenToUse:"something in your crtr install looks off and you want it diagnosed \u2014 a plugin or marketplace manifest is missing, a config entry points at a directory that no longer exists, or a plugin command/hook declaration is malformed. Reports each problem with a structured remediation, and can apply the safe repairs for you.",help:{name:"sys doctor",summary:"diagnose runtime integrity, missing manifests, and broken config entries",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Scope to check. Default: all scopes."},{kind:"flag",name:"fix",type:"bool",required:!1,constraint:"Apply each non-pass check's remediation and report what was fixed."},{kind:"flag",name:"remote",type:"bool",required:!1,constraint:"Check git remotes with ls-remote (slow \u2014 makes network calls)."}],output:[{name:"checks",type:"object[]",required:!0,constraint:"Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. scope may be user | project | canvas. bin:<name> checks report bare-binary contributions (a `bin` block in a plugin manifest or a scope config.json): a pass names the contributor and the resolved target, a fail names an unsafe/reserved command name, malformed target declaration, missing/non-executable/escaping target, or same-precedence name collision that installed nothing; their remediation is always a human_action. humanActions:<name>@<config-origin> checks validate each user/project scope completion-action declaration: action-name and argv shape, cwd directory, argv[0] regular-file existence, and execute permission; they never execute an action. A malformed effective declaration fails; a malformed shadowed declaration is a pass-level warning that names the winning and shadowed origins. requires:<name> checks report each enabled plugin manifest PATH requirement: a pass names the plugin and resolved executable path; a fail names the plugin and carries its declared install hint as remediation. Runtime checks validate selected and process generation manifests, sealing/containment, and full SHA-256 inventories. terminal:nerd-font (user scope) reports whether a Nerd Font is installed for the viewer's icon glyphs; its remediation is always a human_action carrying the platform install command, never auto-fixed. Plugin checks appear as plugin:<name>:commands and plugin:<name>:hooks; both are validated statically and never execute plugin code. Hook failures identify manifest/path/target/event/collision problems and carry disable/update/remove/show remediation; healthy command hooks list effective target, phase, and op, while lifecycle hooks list event, phase, op, and effects. remediation (when present) is {kind, description, ...payload} where kind is remove_config_key | rm_path | human_action; fault human_action remediations include the canonical logs command. Sorted by scope then name."},{name:"ok",type:"boolean",required:!0,constraint:"True when no unresolved fail checks remain."}],outputKind:"object",effects:["Read-only unless --fix is passed. Runtime verification reads and hashes every selected/process generation file; plugin command and hook manifests, bare-binary (`bin`) declarations, humanActions declarations, and advisory `requires` PATH checks never execute plugin or contributed binaries.","With --fix: applies each non-pass check's `remediation` \u2014 removes stale config entries and deletes dangling plugin/marketplace directories; canvas fault findings and plugin command/hook findings stay read-only (--fix never chmods or rewrites third-party plugin content).","Each non-pass result carries a structured `remediation` describing the fix action (absolute paths, exact config keys, or a human_action with its command) so callers can apply it directly without --fix."]},run:p(async n=>{const s=n.scope,t=n.fix,r=n.remote,i=K(s),e=[];for(const a of i){const d=Re(a,{fix:t,remote:r});e.push(...d)}i.includes("user")&&e.push(Ae()),e.push(...await be(i)),e.push(...await xe(i)),e.push(..._e(i)),e.push(...Ce(i)),e.push(...Ee(i)),e.push(...await Oe());const c=ge();e.push(...await N("selected",ye(),c===null)),e.push(...await N("process",$e(),!0));const l={user:0,project:1,builtin:2,canvas:3};e.sort((a,d)=>a.scope!==d.scope?l[a.scope]-l[d.scope]:a.name.localeCompare(d.name));const o=!e.some(a=>a.status==="fail"&&a.fixed!==!0),u=e.filter(a=>a.status==="fail"&&a.fixed!==!0).length;return{checks:e,ok:o,failed:u}},"run")});export{be as runCommandPluginChecks,an as sysDoctorLeaf};
|
|
1
|
+
var F=Object.defineProperty;var p=(e,a)=>F(e,"name",{value:a,configurable:!0});import{envGrantee as q,envProfileId as b}from"../../shared/env.js";import{requiredPluginEnv as I}from"../../core/plugin-env.js";import{createHash as x}from"node:crypto";import{lstat as w,readFile as T,readdir as _,readlink as E}from"node:fs/promises";import{dirname as C,isAbsolute as A,join as h,relative as H,resolve as j,sep as M}from"node:path";import{defineLeaf as L}from"../../core/command.js";import{normalizeRequires as B,readConfig as G,updateConfig as K}from"../../core/config.js";import{scopeRoot as S,listScopes as z,marketplacesDir as W,pluginsDir as J}from"../../core/scope.js";import{readMarketplaceManifest as V,readPluginManifest as U}from"../../core/manifest.js";import{pathExists as $,listDirs as O,removePath as Z}from"../../core/fs-utils.js";import{lsRemote as Q}from"../../core/git.js";import{cliClient as P}from"../api-client.js";import{detectNerdFont as X,nerdFontInstallCommand as Y,NERD_FONT_SELECT_HINT as R}from"../../core/runtime/nerd-font.js";import{detectPackageManager as ee}from"./setup-core.js";import{validateEffectiveCommandPlugins as ne}from"../../core/command-plugins/discovery.js";import{adaptPluginContributions as te}from"../../core/command-plugins/compose.js";import{resolveCommandRegistry as ie}from"../../core/command-manifests/registry.js";import{applyCommandExtensions as oe,orphanCommandExtensionValidations as ae}from"../../core/command-plugins/extensions.js";import{discoverHookRegistry as se}from"../../core/command-hooks/discovery.js";import{resolveBinContributions as re}from"../../core/runtime/bin-contributions.js";import{inspectHumanActions as ce}from"../../core/human-actions.js";import{composeAppProcessEnv as le,overlayBrokerEnv as ue,resolvePathExecutable as me}from"../../core/runtime/spawn-env.js";import{listAllPlugins as de}from"../../core/resolver.js";import{SUBTREE_NAMES as pe,coreCommandPaths as fe,coreHookCatalog as he,extensibleCoreBranches as ge}from"../../build-root.js";import{processGeneration as $e,processLocatedGeneration as ye,runtimeManifest as y,selectedGeneration as ve}from"./shared.js";import{CONFIG_FILE as ke}from"../../types.js";function f(e,a,i){return{scope:e,name:a,status:"pass",message:i}}p(f,"pass");function d(e,a,i,l){return{scope:e,name:a,status:"fail",message:i,...l?{remediation:l}:{}}}p(d,"failCheck");async function k(e){try{switch(e.kind){case"remove_config_key":{if(!e.scope||!e.configKey)return!1;const a=e.configKey.split(".");return await K(e.scope,i=>{let l=i;for(let s=0;s<a.length-1;s++){const n=l[a[s]];if(typeof n!="object"||n===null)return;l=n}delete l[a[a.length-1]]}),!0}case"rm_path":return e.path?(Z(e.path),!0):!1;case"human_action":return!1;default:return!1}}catch{return!1}}p(k,"applyRemediation");function we(e){return[`${e.link} ${e.kind}`,`disposition=${e.retry.disposition}`,typeof e.retry.attempt=="number"&&typeof e.retry.max=="number"?`attempt=${e.retry.attempt}/${e.retry.max}`:void 0,typeof e.retry.nextAt=="string"?`nextAt=${e.retry.nextAt}`:void 0].filter(i=>i!==void 0).join(" \xB7 ")}p(we,"faultMessage");function N(e){return/^[A-Za-z0-9._:/@%+=,-]+$/u.test(e)?e:`'${e.replaceAll("'","'\\''")}'`}p(N,"shellArgument");function be(e,a){const i={connection:"restore connectivity","rate-limit":"wait for the rate limit to clear",overloaded:"wait for load to drop",auth:"re-authenticate",protocol:"fix the protocol mismatch","context-overflow":"shrink the task or loaded context, or choose a larger-context model before spawning the node again",other:"fix the underlying issue",wedged:"read `crtr canvas read wedged-child-on-runaway-bash` \u2014 if a runaway subprocess is to blame, kill it (not the node); if the broker had no subprocess, the daemon SIGTERM-kicks it and starts a fresh recovery cycle automatically","model-not-found":"pass a valid `--model provider/id` or configure credentials for an authenticated provider, then revive the node (`crtr node lifecycle revive <id>`)"},l=e.retry.disposition==="auto"?e.retry.by==="client"?"client redialing":typeof e.retry.attempt=="number"&&typeof e.retry.max=="number"?`auto-retrying, attempt ${e.retry.attempt}/${e.retry.max}`:"auto-retrying":e.retry.disposition==="fatal"?"parked fatal":"manual",s=`crtr sys logs --node ${N(a)} --operation ${N(e.operation_id)}`;return{kind:"human_action",description:`${i[e.kind]}; ${l}; inspect the correlated operation with ${s}`,command:s}}p(be,"faultRemediation");async function xe(e){const a=[],i=new Set(e),l=new Set(pe),[s,n]=await Promise.all([fe(),ge()]),u=ne(l,void 0,void 0,s,n);for(const c of u){const t=c.plugin;if(!i.has(t.scope))continue;const r=c.issues.filter(o=>o.code!=="command_collision");if(r.length===0){const o=c.contributions.map(m=>m.node.name).join(", ");a.push(f(t.scope,`plugin:${t.name}:commands`,o.length>0?`contributes top-level command(s): ${o}`:"command manifest valid"));continue}r.forEach((o,m)=>{const g={kind:"human_action",description:`${o.next} Inspect with \`pkg plugin show ${t.name}\`; then update (\`pkg plugin update --name ${t.name}\`) or remove (\`pkg plugin remove ${t.name}\`) the plugin.`},v=o.path!==void 0?` at ${o.path}`:"";a.push(d(t.scope,`plugin:${t.name}:commands[${m}]`,`${o.code}${v}: ${o.message} (received ${o.received}, expected ${o.expected})`,g))})}if(i.has("project")){const c=ie(te(u.flatMap(o=>o.contributions)).contributions,l),r=[...oe(c.contributions).validations,...ae(c.contributions)];for(const o of r){if(o.issue===void 0){a.push(f("project",`extension:${o.branch}`,`repository fragment valid: ${o.fragmentPath}`));continue}const m=o.issue;a.push(d("project",`extension:${o.branch}`,`${m.code} at ${m.path??o.fragmentPath}: ${m.message} (received ${m.received}, expected ${m.expected})`,{kind:"human_action",description:m.next}))}}return a}p(xe,"runCommandPluginChecks");async function _e(e){const a=new Set(e),i=se(await he()),l=[];for(const s of i.validations){const n=s.plugin;if(!a.has(n.scope))continue;const u=i.issues.filter(c=>c.plugin===n.name&&c.scope===n.scope);if(u.length===0){const c=s.hooks.map(r=>`${r.target} [${r.phase} ${r.op}]`),t=(s.manifest?.schemaVersion===2?s.manifest.lifecycle:[]).map(r=>`${r.event} [${r.phase} ${r.op}; effects: ${r.effects.join(", ")}]`);l.push(f(n.scope,`plugin:${n.name}:hooks`,`effective hook declaration(s): ${[...c,...t].join("; ")}`));continue}u.forEach((c,t)=>{const r=s.manifestPath??h(n.root,".crouter-plugin","plugin.json"),o=c.path!==void 0?`${r}:${c.path}`:r;l.push(d(n.scope,`plugin:${n.name}:hooks[${t}]`,`${c.code} at ${o}: ${c.message} (received ${c.received}, expected ${c.expected})`,{kind:"human_action",description:`${c.next} Inspect with \`crtr pkg plugin show ${n.name}\`; then disable (\`crtr pkg plugin disable ${n.name} --scope ${n.scope}\`), update (\`crtr pkg plugin update --name ${n.name}\`), or remove (\`crtr pkg plugin remove ${n.name}\`) the plugin.`}))})}return l}p(_e,"runHookPluginChecks");function Ee(e){const a=new Set(e),{bins:i,issues:l}=re(process.cwd(),b()||null),s=[];for(const n of l)a.has(n.scope)&&s.push(d(n.scope,`bin:${n.name}`,`${n.origin}: ${n.message}`,{kind:"human_action",description:`Fix the \`bin.${n.name}\` declaration in ${n.origin} \u2014 point it at an executable regular file inside that contributor's own root, or remove it. A same-precedence collision is resolved by renaming one of the claimants.`}));for(const n of i.values())a.has(n.scope)&&s.push(f(n.scope,`bin:${n.name}`,`${n.origin} \u2192 ${n.target}`));return s}p(Ee,"runBinContributionChecks");function Ce(e){const a=new Set(e),i=h(S("user"),ke),l=[],s=new Map;for(const n of ce(process.cwd())){if("kind"in n){if(n.kind==="unknown_action")continue;const r=n.configOrigin===i?"user":"project",o=n.block===!0,m=o?void 0:s.get(n.name);if(!o&&m===void 0&&s.set(n.name,n.configOrigin),!a.has(r))continue;const g=o?`humanActions:block@${n.configOrigin}`:`humanActions:${n.name}@${n.configOrigin}`,v={kind:"human_action",description:o?`Fix or remove the \`humanActions\` block in ${n.configOrigin}. Actions run argv directly without a shell.`:`Fix or remove the \`humanActions.${n.name}\` declaration in ${n.configOrigin}. Actions run argv directly without a shell.`};m!==void 0?l.push({scope:r,name:g,status:"pass",message:`warning: shadowed by ${m}; ${n.configOrigin}: ${n.reason}`,remediation:v}):l.push(d(r,g,`${n.configOrigin}: ${n.reason}`,v));continue}const u=n.configOrigin===i?"user":"project",c=s.get(n.name);if(c===void 0&&s.set(n.name,n.configOrigin),!a.has(u))continue;const t=c===void 0?"":`shadowed by ${c}; `;l.push(f(u,`humanActions:${n.name}@${n.configOrigin}`,`${t}${n.configOrigin} \u2192 ${n.argv[0]} (cwd ${n.cwd})`))}return l}p(Ce,"runHumanActionChecks");async function Ae(e){const a=new Set(e),i=b()??null,l=q()??"app:terminal",s=P(),n=p(t=>Object.fromEntries(t.map(r=>[r,""])),"present"),u=ue({argv:[],env:{CRTR_NODE_CWD:process.cwd(),CRTR_PROFILE_ID:i??"",CRTR_GRANTEE:l}},le(process.cwd(),i,l,n((await s.listAppEnv()).names),i===null?{}:n((await s.listProfileEnv(i)).names))),c=[];for(const t of de())if(!(!a.has(t.scope)||!t.enabled)){for(const r of I(t.manifest.env))u[r]!==void 0?c.push(f(t.scope,`env:${r}`,`plugin "${t.name}" has ${r}`)):c.push(d(t.scope,`env:${r}`,`plugin "${t.name}" needs ${r}`,{kind:"human_action",description:`crtr sys env set --name ${r}`}));if(t.manifest.requires!==void 0)for(const[r,o]of Object.entries(B(t.manifest.requires))){const m=me(r,u,process.cwd());m!==null?c.push(f(t.scope,`requires:${r}`,`plugin "${t.name}" \u2192 ${m}`)):c.push(d(t.scope,`requires:${r}`,`plugin "${t.name}" requires executable "${r}" on PATH`,{kind:"human_action",description:o}))}}return c}p(Ae,"runRequiresChecks");async function je(e){const a=[];async function i(l){for(const s of await _(l)){const n=h(l,s),u=H(e,n).replaceAll(M,"/");if(u==="generation.json")continue;const c=await w(n);if(c.isDirectory())await i(n);else if(c.isFile()){const t=await T(n);a.push({path:u,size:c.size,sha256:x("sha256").update(t).digest("hex")})}else if(c.isSymbolicLink()){const t=await E(n);if(A(t)||!y.contained(e,j(C(n),t)))throw new Error(`generation symlink escapes root: ${u}`);a.push({path:u,size:Buffer.byteLength(t),sha256:x("sha256").update(t).digest("hex"),symlink:!0})}else throw new Error(`unsupported generation file: ${u}`)}}return p(i,"walk"),await i(e),y.sortInventory(a)}p(je,"inventory");async function Se(e){async function a(i){const l=await w(i);if(!l.isDirectory()||l.isSymbolicLink())throw new Error(`generation directory is invalid: ${i}`);if((l.mode&146)!==0)throw new Error(`generation path is writable: ${i}`);for(const s of await _(i)){const n=h(i,s),u=await w(n);if(u.isDirectory())await a(n);else if(u.isFile()){if((u.mode&146)!==0)throw new Error(`generation path is writable: ${n}`)}else if(u.isSymbolicLink()){const c=await E(n);if(A(c)||!y.contained(e,j(C(n),c)))throw new Error(`generation symlink escapes root: ${n}`)}else throw new Error(`unsupported generation file: ${n}`)}}p(a,"walk"),await a(e)}p(Se,"verifySealed");function Oe(){const e=X();if(e.installed)return f("user","terminal:nerd-font",`installed (${e.evidence}) \u2014 if icons still render as boxes, ${R}`);const a=Y(ee());return d("user","terminal:nerd-font","no Nerd Font installed \u2014 viewer tool/command icons will render as tofu boxes",{kind:"human_action",description:`Install a Nerd Font (${a.hint}), ${R}`,...a.command?{command:a.command}:{}})}p(Oe,"runNerdFontCheck");async function D(e,a,i){if(a.generation===null){const u=a.error??"no runtime generation";return[i?f("canvas",`runtime:${e}`,u):d("canvas",`runtime:${e}`,u)]}const l=a.generation,s=[],n=y.validateManifestShape(l.manifest,{id:l.id,entryName:"cli"});if(!n.ok)return[d("canvas",`runtime:${e}:manifest`,n.error)];s.push(f("canvas",`runtime:${e}:manifest`,`${l.id} ${l.manifest.packageVersion}`));try{await Se(l.root),s.push(f("canvas",`runtime:${e}:sealing`,"generation tree is sealed and contained"))}catch(u){s.push(d("canvas",`runtime:${e}:sealing`,u instanceof Error?u.message:String(u)))}try{const u=await je(l.root),c=y.sortInventory(l.manifest.files);JSON.stringify(l.manifest.files)!==JSON.stringify(c)?s.push(d("canvas",`runtime:${e}:inventory`,"manifest inventory is not in canonical runtime-generation-manifest order")):JSON.stringify(u)!==JSON.stringify(c)?s.push(d("canvas",`runtime:${e}:inventory`,"full generation inventory hash does not match generation.json")):s.push(f("canvas",`runtime:${e}:inventory`,`${u.length} files hash-verified`))}catch(u){s.push(d("canvas",`runtime:${e}:inventory`,u instanceof Error?u.message:String(u)))}return s}p(D,"generationChecks");async function Pe(){const e=[];for(const a of await P().listNodes()){const i=a.fault??null;if(i===null)continue;const l=`fault:${a.node_id}:${i.link}`,s=be(i,a.node_id);e.push(d("canvas",l,we(i),s))}return e}p(Pe,"runFaultChecks");async function Re(e,a){const i=[];if(!S(e))return i;const s=G(e),n=W(e);for(const t of Object.keys(s.marketplaces)){if(!n){i.push(d(e,`marketplace:${t}:dir`,"marketplaces directory unavailable"));continue}const r=h(n,t);if($(r))i.push(f(e,`marketplace:${t}:dir`,"directory exists"));else{const o={kind:"remove_config_key",description:`Drop stale config entry config.${e}.marketplaces.${t}`,scope:e,configKey:`marketplaces.${t}`};a.fix&&await k(o)?i.push({scope:e,name:`marketplace:${t}:dir`,status:"fail",message:"directory missing \u2014 removed stale config entry",fixed:!0,remediation:o}):i.push(d(e,`marketplace:${t}:dir`,`directory missing: ${r}`,o))}}const u=J(e);for(const t of Object.keys(s.plugins)){if(!u){i.push(d(e,`plugin:${t}:dir`,"plugins directory unavailable"));continue}const r=h(u,t);if($(r))i.push(f(e,`plugin:${t}:dir`,"directory exists"));else{const o={kind:"remove_config_key",description:`Drop stale config entry config.${e}.plugins.${t}`,scope:e,configKey:`plugins.${t}`};a.fix&&await k(o)?i.push({scope:e,name:`plugin:${t}:dir`,status:"fail",message:"directory missing \u2014 removed stale config entry",fixed:!0,remediation:o}):i.push(d(e,`plugin:${t}:dir`,`directory missing: ${r}`,o))}}if(n&&$(n))for(const t of O(n)){const r=h(n,t),o=V(r);if(o){i.push(f(e,`marketplace:${t}:manifest`,"manifest valid"));for(const m of o.plugins){if(m.source.startsWith("http://")||m.source.startsWith("https://")||m.source.startsWith("git@"))continue;const g=h(r,m.source);$(g)?i.push(f(e,`marketplace:${t}:plugin-source:${m.name}`,"source resolves")):i.push(d(e,`marketplace:${t}:plugin-source:${m.name}`,`source path does not resolve: ${g}`))}}else{const m={kind:"rm_path",description:"Remove dangling marketplace directory (no valid .crouter-marketplace/marketplace.json)",path:r};a.fix&&await k(m)?i.push({scope:e,name:`marketplace:${t}:manifest`,status:"fail",message:"no valid marketplace.json \u2014 removed dangling directory",fixed:!0,remediation:m}):i.push(d(e,`marketplace:${t}:manifest`,`no valid marketplace.json in ${r}`,m))}}const c=new Map;if(u&&$(u))for(const t of O(u)){const r=h(u,t),o=U(r);if(!o){const m={kind:"rm_path",description:"Remove dangling plugin directory (no valid .crouter-plugin/plugin.json)",path:r};a.fix&&await k(m)?i.push({scope:e,name:`plugin:${t}:manifest`,status:"fail",message:"no valid plugin.json \u2014 removed dangling directory",fixed:!0,remediation:m}):i.push(d(e,`plugin:${t}:manifest`,`no valid plugin.json in ${r}`,m));continue}i.push(f(e,`plugin:${t}:manifest`,"manifest valid")),c.has(t)?i.push(d(e,`plugin:${t}:duplicate`,`duplicate plugin name within scope (also at ${c.get(t)})`)):c.set(t,r),a.remote&&o.source&&(Q(o.source).status!==0?i.push(d(e,`plugin:${t}:remote`,`git remote unreachable: ${o.source}`)):i.push(f(e,`plugin:${t}:remote`,"git remote reachable")))}return i}p(Re,"runChecksForScope");const rn=L({name:"doctor",description:"diagnose installation health",whenToUse:"something in your crtr install looks off and you want it diagnosed \u2014 a plugin or marketplace manifest is missing, a config entry points at a directory that no longer exists, or a plugin command/hook declaration is malformed. Reports each problem with a structured remediation, and can apply the safe repairs for you.",help:{name:"sys doctor",summary:"diagnose runtime integrity, missing manifests, and broken config entries",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Scope to check. Default: all scopes."},{kind:"flag",name:"fix",type:"bool",required:!1,constraint:"Apply each non-pass check's remediation and report what was fixed."},{kind:"flag",name:"remote",type:"bool",required:!1,constraint:"Check git remotes with ls-remote (slow \u2014 makes network calls)."}],output:[{name:"checks",type:"object[]",required:!0,constraint:"Each: {scope, name, status, message, fixed?, remediation?}. status: pass | fail. scope may be user | project | canvas. bin:<name> checks report bare-binary contributions (a `bin` block in a plugin manifest or a scope config.json): a pass names the contributor and the resolved target, a fail names an unsafe/reserved command name, malformed target declaration, missing/non-executable/escaping target, or same-precedence name collision that installed nothing; their remediation is always a human_action. humanActions:<name>@<config-origin> checks validate each user/project scope completion-action declaration: action-name and argv shape, cwd directory, argv[0] regular-file existence, and execute permission; they never execute an action. A malformed effective declaration fails; a malformed shadowed declaration is a pass-level warning that names the winning and shadowed origins. requires:<name> checks report each enabled plugin manifest PATH requirement: a pass names the plugin and resolved executable path; a fail names the plugin and carries its declared install hint as remediation. Runtime checks validate selected and process generation manifests, sealing/containment, and full SHA-256 inventories. terminal:nerd-font (user scope) reports whether a Nerd Font is installed for the viewer's icon glyphs; its remediation is always a human_action carrying the platform install command, never auto-fixed. Plugin checks appear as plugin:<name>:commands and plugin:<name>:hooks; both are validated statically and never execute plugin code. Hook failures identify manifest/path/target/event/collision problems and carry disable/update/remove/show remediation; healthy command hooks list effective target, phase, and op, while lifecycle hooks list event, phase, op, and effects. remediation (when present) is {kind, description, ...payload} where kind is remove_config_key | rm_path | human_action; fault human_action remediations include the canonical logs command. Sorted by scope then name."},{name:"ok",type:"boolean",required:!0,constraint:"True when no unresolved fail checks remain."}],outputKind:"object",effects:["Read-only unless --fix is passed. Runtime verification reads and hashes every selected/process generation file; plugin command and hook manifests, bare-binary (`bin`) declarations, humanActions declarations, and advisory `requires` PATH checks never execute plugin or contributed binaries.","With --fix: applies each non-pass check's `remediation` \u2014 removes stale config entries and deletes dangling plugin/marketplace directories; canvas fault findings and plugin command/hook findings stay read-only (--fix never chmods or rewrites third-party plugin content).","Each non-pass result carries a structured `remediation` describing the fix action (absolute paths, exact config keys, or a human_action with its command) so callers can apply it directly without --fix."]},run:p(async e=>{const a=e.scope,i=e.fix,l=e.remote,s=z(a),n=[];for(const o of s){const m=await Re(o,{fix:i,remote:l});n.push(...m)}s.includes("user")&&n.push(Oe()),n.push(...await xe(s)),n.push(...await _e(s)),n.push(...Ee(s)),n.push(...Ce(s)),n.push(...await Ae(s)),n.push(...await Pe());const u=$e();n.push(...await D("selected",ve(),u===null)),n.push(...await D("process",ye(),!0));const c={user:0,project:1,builtin:2,canvas:3};n.sort((o,m)=>o.scope!==m.scope?c[o.scope]-c[m.scope]:o.name.localeCompare(m.name));const t=!n.some(o=>o.status==="fail"&&o.fixed!==!0),r=n.filter(o=>o.status==="fail"&&o.fixed!==!0).length;return{checks:n,ok:t,failed:r}},"run")});export{xe as runCommandPluginChecks,rn as sysDoctorLeaf};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var m=Object.defineProperty;var o=(s,e)=>m(s,"name",{value:e,configurable:!0});import{Input as c,truncateToWidth as h}from"@earendil-works/pi-tui";import{readConfig as I,updateRawConfigAtomically as v}from"../../../core/config.js";import{BROKER_LIMIT_OVERRIDE_PRECEDENCE as d}from"../setup.js";import{padAnsi as C,theme as n,wrapText as p}from"../../../core/tui/panel.js";function f(s){return/^\d+$/.test(s)&&Number.isSafeInteger(Number(s))&&Number(s)>=1?Number(s):void 0}o(f,"positive");class R{static{o(this,"BrokerLimitsPanel")}onChange;warningInput=new c;hardCapInput=new c;index=0;notice;_focused=!1;constructor(e){this.onChange=e;const t=I("user").brokerThresholds;this.warningInput.setValue(String(t.warning)),this.warningInput.handleInput(""),this.hardCapInput.setValue(String(t.automaticReviveCap)),this.hardCapInput.handleInput(""),this.warningInput.onSubmit=()=>this.save(),this.hardCapInput.onSubmit=()=>this.save()}set focused(e){this._focused=e,this.warningInput.focused=e&&this.index===0,this.hardCapInput.focused=e&&this.index===1}selected(){return this.index===0?this.warningInput:this.hardCapInput}move(e){this.index=(this.index+e+2)%2,this.focused=this._focused,this.notice=void 0}save(){const e=f(this.warningInput.getValue()),t=f(this.hardCapInput.getValue());e===void 0||t===void 0?this.notice="Both broker limits must be positive integers.":e>t?this.notice="Warning threshold cannot exceed the automatic-revive hard cap.":(v("user",r=>({...r,brokerThresholds:{warning:e,automaticReviveCap:t}})),this.notice=`Saved to user config. Applies next tick. ${d}`),this.onChange()}handleInput(e,t){return/^\d+$/.test(e)?(this.selected().handleInput(e),this.notice=void 0,this.onChange(),!0):t?(t("crtr.setup.list-down",e)?this.move(1):t("crtr.setup.list-up",e)?this.move(-1):e.length>0&&[...e].every(r=>{const i=r.codePointAt(0);return i>=32&&i!==127})?this.notice="Broker limits accept digits only.":(this.selected().handleInput(e),e!=="\r"&&e!==`
|
|
1
|
+
var m=Object.defineProperty;var o=(s,e)=>m(s,"name",{value:e,configurable:!0});import{Input as c,truncateToWidth as h}from"@earendil-works/pi-tui";import{readConfig as I,updateRawConfigAtomically as v}from"../../../core/config.js";import{BROKER_LIMIT_OVERRIDE_PRECEDENCE as d}from"../setup.js";import{padAnsi as C,theme as n,wrapText as p}from"../../../core/tui/panel.js";function f(s){return/^\d+$/.test(s)&&Number.isSafeInteger(Number(s))&&Number(s)>=1?Number(s):void 0}o(f,"positive");class R{static{o(this,"BrokerLimitsPanel")}onChange;warningInput=new c;hardCapInput=new c;index=0;notice;_focused=!1;constructor(e){this.onChange=e;const t=I("user").brokerThresholds;this.warningInput.setValue(String(t.warning)),this.warningInput.handleInput(""),this.hardCapInput.setValue(String(t.automaticReviveCap)),this.hardCapInput.handleInput(""),this.warningInput.onSubmit=()=>this.save(),this.hardCapInput.onSubmit=()=>this.save()}set focused(e){this._focused=e,this.warningInput.focused=e&&this.index===0,this.hardCapInput.focused=e&&this.index===1}selected(){return this.index===0?this.warningInput:this.hardCapInput}move(e){this.index=(this.index+e+2)%2,this.focused=this._focused,this.notice=void 0}async save(){const e=f(this.warningInput.getValue()),t=f(this.hardCapInput.getValue());e===void 0||t===void 0?this.notice="Both broker limits must be positive integers.":e>t?this.notice="Warning threshold cannot exceed the automatic-revive hard cap.":(await v("user",r=>({...r,brokerThresholds:{warning:e,automaticReviveCap:t}})),this.notice=`Saved to user config. Applies next tick. ${d}`),this.onChange()}handleInput(e,t){return/^\d+$/.test(e)?(this.selected().handleInput(e),this.notice=void 0,this.onChange(),!0):t?(t("crtr.setup.list-down",e)?this.move(1):t("crtr.setup.list-up",e)?this.move(-1):e.length>0&&[...e].every(r=>{const i=r.codePointAt(0);return i>=32&&i!==127})?this.notice="Broker limits accept digits only.":(this.selected().handleInput(e),e!=="\r"&&e!==`
|
|
2
2
|
`&&(this.notice=void 0)),this.onChange(),!0):!1}render(e){const t=[h(`${n.fg("accent",n.bold("Broker limits"))}${n.fg("dim"," user scope \xB7 defaults 16 / 32")}`,e),h(n.fg("dim","These bounds govern daemon automatic revive; explicit node revives remain available."),e),""],r=o((i,u,l,g)=>{const a=i===this.index;t.push(h(`${a?n.fg("accent","\u25B6"):" "} ${C(a?n.fg("accent",u):u,24)} ${g.render(Math.max(8,e-28))[0]??""}`,e)),t.push(h(n.fg("dim",` ${l}`),e))},"line");r(0,"Warning threshold","Warn when live brokers reach this count. Default: 16.",this.warningInput),r(1,"Automatic-revive hard cap","Defer automatic revives at this count. Default: 32.",this.hardCapInput),t.push("");for(const i of p(`Configured limits apply next tick. ${d}`,e))t.push(n.fg("dim",i));if(this.notice)for(const i of p(this.notice,e))t.push(n.fg(this.notice.startsWith("Saved")?"success":"warning",i));return t}}export{R as BrokerLimitsPanel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var O=(M,e)=>Z(M,"name",{value:e,configurable:!0});import{Input as ee,matchesKey as ie,truncateToWidth as h,visibleWidth as u}from"@earendil-works/pi-tui";import{BINDING_CATALOG as j,formatBinding as S,formatGesture as te,formatMovePair as se,isAttachPaneBinding as ne,matchesPiTuiInput as re,persistUserKeybindings as de,KeybindingConcurrentEditError as oe,KeybindingValidationError as le}from"../../../core/keybindings/index.js";import{installTmuxBindings as ae,occupiedTmuxRootGestures as he}from"../../../core/runtime/tmux-chrome.js";import{SetupKeybindingsDraft as ce}from"../setup-core.js";import{padAnsi as q,theme as n}from"../../../core/tui/panel.js";function ue(){const M=new Map(j.map(i=>[i.id,i])),e=new Set,t=[];for(const i of j){if(e.has(i.id))continue;if(i.pair===void 0){t.push({group:i.group,name:i.name,description:i.description,fields:[{definition:i}]}),e.add(i.id);continue}const s=M.get(i.pair.pairedId);if(s?.pair?.pairedId!==i.id||s.pair.field===i.pair.field)throw new Error(`invalid keybinding pair metadata for ${i.id}`);const a=[i,s],o=a.find(l=>l.pair?.field==="menu"),f=a.find(l=>l.pair?.field!=="menu");if(o===void 0||f===void 0)throw new Error(`incomplete keybinding pair for ${i.id}`);const r=f.pair?.field==="global"?"Global":"Direct";t.push({group:o.group,name:o.name,description:o.description,fields:[{label:"Menu",definition:o},{label:r,definition:f}]}),e.add(o.id),e.add(f.id)}return t}O(ue,"buildSetupKeybindingRows");const W=ue();class be{static{O(this,"KeybindingsPanel")}openingBindings;terminalRows;onChange;keyInput=new ee;keyDraft;keyIndex=0;keyFieldIndex=0;keyScroll=0;keyFilter="";filteringKeys=!1;editingKey=null;keyEditError;keyDiagnostics=[];keyConfirm=null;keyNotice;keyTmuxOccupancy;_focused=!1;constructor(e,t,i,s,a){this.openingBindings=e,this.terminalRows=s,this.onChange=a,this.keyDraft=new ce(t,i),this.keyDiagnostics=this.keyDraft.resolution().diagnostics}set focused(e){this._focused=e,this.keyInput.focused=e&&this.editingKey!==null}get focused(){return this._focused}get dirty(){return this.keyDraft.dirty}badge(){return String(W.length)}get takesPriorityInput(){return this.editingKey!==null||this.filteringKeys||this.keyConfirm!==null}requestDiscard(){this.keyDraft.dirty&&(this.keyConfirm="discard",this.invalidate())}invalidate(){this.onChange()}matches(e,t){return re(this.openingBindings,e,t,(i,s)=>ie(i,s))}label(e){return S(this.openingBindings,e)}filteredKeybindings(){const e=this.keyDraft.resolution(),t=this.keyFilter.trim().toLowerCase();return t===""?W:W.filter(i=>[i.name,i.group,i.description,...i.fields.flatMap(s=>[s.label??"",s.definition.id,s.definition.name,s.definition.description,S(e,s.definition.id)])].some(s=>s.toLowerCase().includes(t)))}selectedKeyRow(){const e=this.filteredKeybindings();if(e.length===0)return;this.keyIndex=Math.max(0,Math.min(this.keyIndex,e.length-1));const t=e[this.keyIndex];return this.keyFieldIndex=Math.max(0,Math.min(this.keyFieldIndex,t.fields.length-1)),t}selectedKeybinding(){return this.selectedKeyRow()?.fields[this.keyFieldIndex]?.definition}tmuxPassthroughOffer(){const e=this.selectedKeybinding();if(e===void 0||!ne(e))return;const t=this.keyDraft.resolution().gestures(e.id),i=t.join("\0");(this.keyTmuxOccupancy?.id!==e.id||this.keyTmuxOccupancy.expression!==i)&&(this.keyTmuxOccupancy={id:e.id,expression:i,gestures:he(t)});const s=this.keyTmuxOccupancy.gestures[0];return s===void 0?void 0:{id:e.id,gesture:s}}toggleTmuxPassthrough(){const e=this.tmuxPassthroughOffer();return e===void 0?!1:(this.keyDraft.toggleTmuxPassthrough(e.id),this.keyTmuxOccupancy=void 0,this.keyNotice=void 0,this.invalidate(),!0)}moveKey(e){const t=this.filteredKeybindings();if(t.length===0)return;this.keyIndex=Math.max(0,Math.min(t.length-1,this.keyIndex+e)),this.keyFieldIndex=Math.min(this.keyFieldIndex,t[this.keyIndex].fields.length-1);const i=this.keyPageSize();this.keyIndex<this.keyScroll&&(this.keyScroll=this.keyIndex),this.keyIndex>=this.keyScroll+i&&(this.keyScroll=this.keyIndex-i+1),this.keyEditError=void 0,this.invalidate()}moveKeyField(e){const t=this.selectedKeyRow();t!==void 0&&(this.keyFieldIndex=Math.max(0,Math.min(t.fields.length-1,this.keyFieldIndex+e)),this.keyEditError=void 0,this.invalidate())}keyViewportLines(){return Math.max(4,this.terminalRows()-11)}keyPageSize(){return Math.max(3,this.keyViewportLines()-4)}beginKeyEdit(){const e=this.selectedKeybinding();if(e){if(this.keyDraft.hasInvalidRoot()){this.keyNotice=`Saved keybindings root is invalid \u2014 ${this.label("crtr.setup.keybindings.reset-all")} (reset all) before editing.`,this.invalidate();return}this.editingKey=e.id,this.keyInput.setValue(this.keyDraft.expression(e.id)),this.keyInput.handleInput(""),this.keyInput.focused=this.focused,this.keyEditError=void 0,this.invalidate()}}commitKeyEdit(){if(this.editingKey===null)return;const e=this.keyDraft.edit(this.editingKey,this.keyInput.getValue());if(!e.ok){this.keyEditError=e.diagnostics[0]?.message??"Invalid keybinding",this.keyDiagnostics=e.diagnostics,this.invalidate();return}this.editingKey=null,this.keyInput.focused=!1,this.keyEditError=void 0,this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.keyNotice=void 0,this.invalidate()}cancelKeyEdit(){this.editingKey=null,this.keyInput.focused=!1,this.keyEditError=void 0,this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.invalidate()}saveKeybindings(){const e=this.keyDraft.resolution();this.keyDiagnostics=e.diagnostics;const t=e.diagnostics.filter(i=>i.severity==="error");if(t.length>0){this.keyNotice=t[0].message,this.invalidate();return}try{const i=de(this.keyDraft.overrides(),{expectedKeybindings:this.keyDraft.expectedKeybindings(),tmuxPassthrough:this.keyDraft.tmuxPassthrough(),expectedTmuxPassthrough:this.keyDraft.expectedTmuxPassthrough()});this.keyDraft.markSaved(i.overrides,i.tmuxPassthrough),this.keyNotice="Saved. Reload attach and reopen active views to apply changes; refreshing tmux\u2026",this.refreshTmuxBindingsAfterSave().catch(s=>{this.keyNotice=`Saved, but tmux refresh failed: ${s instanceof Error?s.message:String(s)}`,this.invalidate()})}catch(i){i instanceof oe?this.keyNotice="Keybindings changed on disk. Reopen setup before saving.":i instanceof le?(this.keyDiagnostics=i.diagnostics,this.keyNotice=i.message):this.keyNotice=i instanceof Error?i.message:String(i)}this.invalidate()}async refreshTmuxBindingsAfterSave(){const e=ae(),t=e.diagnostics.filter(i=>i.code==="occupied");e.ok&&t.length===0?this.keyNotice="Saved and applied to tmux. Reload attach and reopen active views.":e.ok?this.keyNotice=`Saved; tmux kept occupied keys: ${t.map(i=>i.message).join("; ")}`:this.keyNotice=`Saved, but tmux refresh failed: ${e.diagnostics.find(i=>i.code==="tmux-error")?.message??"tmux unavailable"}`,this.invalidate()}confirmKeyAction(){this.keyConfirm==="reset-all"?(this.keyDraft.resetAll(),this.keyDiagnostics=[],this.keyNotice="All rows reset to catalog defaults. Press save to write."):this.keyConfirm==="discard"&&(this.keyDraft.discard(),this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.keyNotice="Unsaved keybinding changes discarded."),this.keyConfirm=null,this.invalidate()}handleKeybindingsInput(e){if(this.keyConfirm!==null){this.matches("crtr.setup.confirm.accept",e)?this.confirmKeyAction():this.matches("crtr.setup.confirm.back",e)&&(this.keyConfirm=null,this.invalidate());return}if(this.editingKey!==null){if(this.matches("crtr.setup.keybindings.commit-edit",e)){this.commitKeyEdit();return}if(this.matches("crtr.setup.keybindings.cancel-edit",e)){this.cancelKeyEdit();return}if(this.matches("crtr.setup.keybindings.page-up",e)){this.moveKey(-this.keyPageSize());return}if(this.matches("crtr.setup.keybindings.page-down",e)){this.moveKey(this.keyPageSize());return}this.keyInput.handleInput(e),this.invalidate();return}if(this.filteringKeys){this.matches("crtr.setup.keybindings.filter-clear",e)?(this.keyFilter="",this.filteringKeys=!1,this.keyIndex=0,this.keyScroll=0):this.matches("crtr.setup.keybindings.edit",e)?this.filteringKeys=!1:this.matches("crtr.setup.keybindings.filter-delete",e)?(this.keyFilter=this.keyFilter.slice(0,-1),this.keyIndex=0,this.keyScroll=0):e.length===1&&e.charCodeAt(0)>=32&&(this.keyFilter+=e,this.keyIndex=0,this.keyScroll=0),this.invalidate();return}if(this.matches("crtr.setup.keybindings.filter",e))this.filteringKeys=!0;else if(this.matches("crtr.setup.list-down",e)){this.moveKey(1);return}else if(this.matches("crtr.setup.list-up",e)){this.moveKey(-1);return}else if(this.matches("crtr.setup.cell-previous",e)){this.moveKeyField(-1);return}else if(this.matches("crtr.setup.cell-next",e)){this.moveKeyField(1);return}else if(this.matches("crtr.setup.keybindings.page-up",e)){this.moveKey(-this.keyPageSize());return}else if(this.matches("crtr.setup.keybindings.page-down",e)){this.moveKey(this.keyPageSize());return}else if(this.matches("crtr.setup.toggle",e)){if(this.toggleTmuxPassthrough())return}else if(this.matches("crtr.setup.keybindings.edit",e)){this.beginKeyEdit();return}else if(this.matches("crtr.setup.keybindings.unbind",e)){const t=this.selectedKeybinding();if(t){const i=this.keyDraft.unbind(t.id);this.keyDiagnostics=i.ok?this.keyDraft.resolution().diagnostics:i.diagnostics,this.keyEditError=i.ok?void 0:i.diagnostics[0]?.message}}else if(this.matches("crtr.setup.keybindings.reset-row",e)){const t=this.selectedKeybinding();t&&this.keyDraft.resetRow(t.id),this.keyDiagnostics=this.keyDraft.resolution().diagnostics}else if(this.matches("crtr.setup.keybindings.reset-all",e))this.keyConfirm="reset-all";else if(this.matches("crtr.setup.keybindings.save",e)){this.saveKeybindings();return}else if(this.matches("crtr.setup.keybindings.discard",e))this.keyFilter!==""?(this.keyFilter="",this.keyIndex=0,this.keyScroll=0,this.keyNotice=void 0):this.keyDraft.dirty?this.keyConfirm="discard":this.keyNotice="No unsaved keybinding changes.";else return;this.invalidate()}keyRowMessage(e,t){const i=e[t],s=t===this.keyIndex,a=i.fields.find(r=>r.definition.id===this.editingKey);if(!s&&a===void 0)return;if(a!==void 0&&this.keyEditError)return{label:a.label,message:this.keyEditError};const o=this.keyDiagnostics.find(r=>i.fields.some(l=>r.bindingId===l.definition.id||r.relatedBindingIds?.includes(l.definition.id)));return o===void 0?void 0:{label:i.fields.find(r=>o.bindingId===r.definition.id||o.relatedBindingIds?.includes(r.definition.id))?.label,message:o.message}}keyRowBodyLineCount(e,t,i,s){const a=e.fields.findIndex(x=>x.definition.id===this.editingKey);if(!(a>=0||t===this.keyIndex)||e.fields.length<2)return 1;const f=a>=0?a:Math.min(this.keyFieldIndex,e.fields.length-1),r=e.fields[f],l=a>=0?`> ${this.keyInput.getValue()}`:S(i,r.definition.id),C=`[${r.label}]`;return 3+u(e.name)+1+u(C)+1+u(l)<=s?1:u(r.label??"")+1+u(l)<=s?2:3}keyLineCost(e,t,i,s,a){let o=0,f;for(let r=t;r<=i;r+=1){const l=e[r];o+=(l.group!==f?1:0)+this.keyRowBodyLineCount(l,r,s,a)+(this.keyRowMessage(e,r)?1:0)+(r===this.keyIndex&&this.tmuxPassthroughOffer()!==void 0?1:0),f=l.group}return o}renderKeybindings(e){const t=[],i=this.filteredKeybindings(),s=this.keyDraft.resolution(),a=this.keyViewportLines(),o=this.keyPageSize(),f=Math.max(0,i.length-o);this.keyScroll=Math.max(0,Math.min(this.keyScroll,f)),this.keyIndex<this.keyScroll&&(this.keyScroll=this.keyIndex),this.keyIndex>=this.keyScroll+o&&(this.keyScroll=Math.max(0,this.keyIndex-o+1));const r=this.editingKey!==null?i.findIndex(y=>y.fields.some(d=>d.definition.id===this.editingKey)):-1,l=r>=0?r:this.keyIndex;if(l>=0)for(l<this.keyScroll&&(this.keyScroll=l);this.keyScroll<l&&this.keyLineCost(i,this.keyScroll,l,s,e)>a;)this.keyScroll+=1;const C=this.keyDraft.dirty?n.fg("warning","unsaved"):n.fg("success","saved");if(t.push(h(`${n.fg("accent",n.bold("Keybindings"))}${n.fg("dim"," crouter-owned only \xB7 ")}${C}`,e)),t.push(h(n.fg("dim",this.keyFilter===""?`${i.length} actions`:`${i.length} matches for \u201C${this.keyFilter}\u201D${this.filteringKeys?"":` \xB7 ${this.label("crtr.setup.keybindings.discard")} clears filter`}`),e)),s.diagnostics.some(y=>y.code==="invalid-keybindings")&&t.push(h(n.fg("warning",n.bold(`Saved keybindings root is invalid \u2014 ${this.label("crtr.setup.keybindings.reset-all")} (reset all) to clear it before editing.`)),e)),i.length===0)return t.push(""),t.push(h(n.fg("warning","No matching keybindings."),e)),t;const N=Math.max(10,Math.min(26,Math.floor(e*.28)));let E=0,x;for(let y=this.keyScroll;y<i.length;y+=1){const d=i[y],p=y===this.keyIndex,A=d.fields.findIndex(c=>c.definition.id===this.editingKey),R=A>=0?A:p?Math.min(this.keyFieldIndex,d.fields.length-1):0,v=this.keyRowMessage(i,y),F=p?this.tmuxPassthroughOffer():void 0,T=this.keyRowBodyLineCount(d,y,s,e),V=(d.group!==x?1:0)+T+(v?1:0)+(F?1:0);if(E>0&&E+V>a)break;d.group!==x&&(t.push(h(n.fg("accent",d.group),e)),x=d.group);const z=p?n.fg("accent","\u25B6"):" ",G=d.fields.some(c=>s.get(c.definition.id).source==="user")?n.fg("warning","\u2022"):" ";if(T>1){const c=d.fields[R],g=c.definition.id===this.editingKey,I=g?`> ${this.keyInput.getValue()}`:S(s,c.definition.id),m=`[${c.label}]`,B=c.label??"";if(t.push(h(`${z}${G} ${p?n.fg("accent",d.name):d.name}`,e)),T===2){const k=u(m)+1+u(I)<=e?m:B,$=n.fg("accent",n.bold(k)),D=" ".repeat(Math.max(0,Math.min(3,e-u(k)-1-u(I)))),w=Math.max(1,e-u(D)-u($)-1),L=g?this.keyInput.render(w)[0]??"":I;t.push(h(`${D}${$} ${L}`,e))}else{const K=n.fg("accent",n.bold(m)),k=" ".repeat(Math.max(0,Math.min(3,e-u(K)))),$=g?this.keyInput.render(e)[0]??"":I;t.push(h(`${k}${K}`,e)),t.push(h($,e))}}else{const c=q(p?n.fg("accent",d.name):d.name,N),g=Math.max(1,e-4-N),m=d.fields.length===1||g>=28?d.fields:[d.fields[R]],B=d.fields.length>1?Math.min(61,g):Math.min(34,g),K=Math.max(0,m.length-1),k=Math.max(1,Math.floor((B-K)/m.length)),D=m.map(b=>{const H=d.fields.indexOf(b),_=b.definition.id===this.editingKey,Y=_||p&&H===R,P=b.label===void 0?"":Y?n.fg("accent",n.bold(`[${b.label}]`)):n.fg("dim",`${b.label}:`),J=u(P),Q=Math.max(1,k-J-(P===""?0:1)),U=_?this.keyInput.render(Q)[0]??"":S(s,b.definition.id),X=P===""?U:`${P} ${U}`;return q(h(X,k),k)}).join(" "),w=g-u(D)-1,L=w>=16?` ${n.fg("dim",h(d.description,w))}`:"";t.push(h(`${z}${G} ${c} ${D}${L}`,e))}if(F){const c=this.keyDraft.usesTmuxPassthrough(F.id)?n.fg("success","[x]"):n.fg("dim","[ ]");t.push(h(` ${c} ${n.fg("accent",`Use tmux's ${te(F.gesture)} here`)}`,e))}if(v){const c=v.label===void 0?"":`${v.label}: `;t.push(h(n.fg("warning",` ${c}${v.message}`),e))}E+=V}return this.keyConfirm!==null&&(t.push(""),t.push(h(n.fg("warning",n.bold(this.keyConfirm==="reset-all"?"Reset every keybinding to its catalog default?":"Discard every unsaved keybinding change?")),e))),this.keyNotice&&t.push(h(n.fg("muted",this.keyNotice),e)),t}handleInput(e){return this.handleKeybindingsInput(e),!0}render(e,t){return this.renderKeybindings(e)}footerHint(){return this.keyConfirm!==null?`${this.label("crtr.setup.confirm.accept")} confirm \xB7 ${this.label("crtr.setup.confirm.back")} go back`:this.editingKey!==null?`type gestures separated by commas \xB7 ${this.label("crtr.setup.keybindings.commit-edit")} commit \xB7 ${this.label("crtr.setup.keybindings.cancel-edit")} cancel \xB7 ${this.label("crtr.setup.keybindings.page-up")}/${this.label("crtr.setup.keybindings.page-down")} scroll`:this.filteringKeys?`filter: ${this.keyFilter||"type to search"} \xB7 ${this.label("crtr.setup.keybindings.edit")} keep \xB7 ${this.label("crtr.setup.keybindings.filter-delete")} delete \xB7 ${this.label("crtr.setup.keybindings.filter-clear")} clear`:`${se(this.openingBindings,"crtr.setup.list-down","crtr.setup.list-up")} move \xB7 ${this.label("crtr.setup.cell-previous")}/${this.label("crtr.setup.cell-next")} field \xB7 ${this.label("crtr.setup.keybindings.edit")} edit \xB7 ${this.tmuxPassthroughOffer()===void 0?"":`${this.label("crtr.setup.toggle")} tmux \xB7 `}${this.label("crtr.setup.keybindings.unbind")} disable \xB7 ${this.label("crtr.setup.keybindings.reset-row")} reset \xB7 ${this.label("crtr.setup.keybindings.reset-all")} reset all \xB7 ${this.label("crtr.setup.keybindings.filter")} filter \xB7 ${this.label("crtr.setup.keybindings.save")} save \xB7 ${this.label("crtr.setup.keybindings.discard")} ${this.keyFilter!==""?"clear filter":"discard"}`}}export{be as KeybindingsPanel};
|
|
1
|
+
var Z=Object.defineProperty;var O=(M,e)=>Z(M,"name",{value:e,configurable:!0});import{Input as ee,matchesKey as ie,truncateToWidth as h,visibleWidth as u}from"@earendil-works/pi-tui";import{BINDING_CATALOG as j,formatBinding as S,formatGesture as te,formatMovePair as se,isAttachPaneBinding as ne,matchesPiTuiInput as re,persistUserKeybindings as de,KeybindingConcurrentEditError as oe,KeybindingValidationError as le}from"../../../core/keybindings/index.js";import{installTmuxBindings as ae,occupiedTmuxRootGestures as he}from"../../../core/runtime/tmux-chrome.js";import{SetupKeybindingsDraft as ce}from"../setup-core.js";import{padAnsi as q,theme as n}from"../../../core/tui/panel.js";function ue(){const M=new Map(j.map(i=>[i.id,i])),e=new Set,t=[];for(const i of j){if(e.has(i.id))continue;if(i.pair===void 0){t.push({group:i.group,name:i.name,description:i.description,fields:[{definition:i}]}),e.add(i.id);continue}const s=M.get(i.pair.pairedId);if(s?.pair?.pairedId!==i.id||s.pair.field===i.pair.field)throw new Error(`invalid keybinding pair metadata for ${i.id}`);const a=[i,s],o=a.find(l=>l.pair?.field==="menu"),f=a.find(l=>l.pair?.field!=="menu");if(o===void 0||f===void 0)throw new Error(`incomplete keybinding pair for ${i.id}`);const r=f.pair?.field==="global"?"Global":"Direct";t.push({group:o.group,name:o.name,description:o.description,fields:[{label:"Menu",definition:o},{label:r,definition:f}]}),e.add(o.id),e.add(f.id)}return t}O(ue,"buildSetupKeybindingRows");const W=ue();class be{static{O(this,"KeybindingsPanel")}openingBindings;terminalRows;onChange;keyInput=new ee;keyDraft;keyIndex=0;keyFieldIndex=0;keyScroll=0;keyFilter="";filteringKeys=!1;editingKey=null;keyEditError;keyDiagnostics=[];keyConfirm=null;keyNotice;keyTmuxOccupancy;_focused=!1;constructor(e,t,i,s,a){this.openingBindings=e,this.terminalRows=s,this.onChange=a,this.keyDraft=new ce(t,i),this.keyDiagnostics=this.keyDraft.resolution().diagnostics}set focused(e){this._focused=e,this.keyInput.focused=e&&this.editingKey!==null}get focused(){return this._focused}get dirty(){return this.keyDraft.dirty}badge(){return String(W.length)}get takesPriorityInput(){return this.editingKey!==null||this.filteringKeys||this.keyConfirm!==null}requestDiscard(){this.keyDraft.dirty&&(this.keyConfirm="discard",this.invalidate())}invalidate(){this.onChange()}matches(e,t){return re(this.openingBindings,e,t,(i,s)=>ie(i,s))}label(e){return S(this.openingBindings,e)}filteredKeybindings(){const e=this.keyDraft.resolution(),t=this.keyFilter.trim().toLowerCase();return t===""?W:W.filter(i=>[i.name,i.group,i.description,...i.fields.flatMap(s=>[s.label??"",s.definition.id,s.definition.name,s.definition.description,S(e,s.definition.id)])].some(s=>s.toLowerCase().includes(t)))}selectedKeyRow(){const e=this.filteredKeybindings();if(e.length===0)return;this.keyIndex=Math.max(0,Math.min(this.keyIndex,e.length-1));const t=e[this.keyIndex];return this.keyFieldIndex=Math.max(0,Math.min(this.keyFieldIndex,t.fields.length-1)),t}selectedKeybinding(){return this.selectedKeyRow()?.fields[this.keyFieldIndex]?.definition}tmuxPassthroughOffer(){const e=this.selectedKeybinding();if(e===void 0||!ne(e))return;const t=this.keyDraft.resolution().gestures(e.id),i=t.join("\0");(this.keyTmuxOccupancy?.id!==e.id||this.keyTmuxOccupancy.expression!==i)&&(this.keyTmuxOccupancy={id:e.id,expression:i,gestures:he(t)});const s=this.keyTmuxOccupancy.gestures[0];return s===void 0?void 0:{id:e.id,gesture:s}}toggleTmuxPassthrough(){const e=this.tmuxPassthroughOffer();return e===void 0?!1:(this.keyDraft.toggleTmuxPassthrough(e.id),this.keyTmuxOccupancy=void 0,this.keyNotice=void 0,this.invalidate(),!0)}moveKey(e){const t=this.filteredKeybindings();if(t.length===0)return;this.keyIndex=Math.max(0,Math.min(t.length-1,this.keyIndex+e)),this.keyFieldIndex=Math.min(this.keyFieldIndex,t[this.keyIndex].fields.length-1);const i=this.keyPageSize();this.keyIndex<this.keyScroll&&(this.keyScroll=this.keyIndex),this.keyIndex>=this.keyScroll+i&&(this.keyScroll=this.keyIndex-i+1),this.keyEditError=void 0,this.invalidate()}moveKeyField(e){const t=this.selectedKeyRow();t!==void 0&&(this.keyFieldIndex=Math.max(0,Math.min(t.fields.length-1,this.keyFieldIndex+e)),this.keyEditError=void 0,this.invalidate())}keyViewportLines(){return Math.max(4,this.terminalRows()-11)}keyPageSize(){return Math.max(3,this.keyViewportLines()-4)}beginKeyEdit(){const e=this.selectedKeybinding();if(e){if(this.keyDraft.hasInvalidRoot()){this.keyNotice=`Saved keybindings root is invalid \u2014 ${this.label("crtr.setup.keybindings.reset-all")} (reset all) before editing.`,this.invalidate();return}this.editingKey=e.id,this.keyInput.setValue(this.keyDraft.expression(e.id)),this.keyInput.handleInput(""),this.keyInput.focused=this.focused,this.keyEditError=void 0,this.invalidate()}}commitKeyEdit(){if(this.editingKey===null)return;const e=this.keyDraft.edit(this.editingKey,this.keyInput.getValue());if(!e.ok){this.keyEditError=e.diagnostics[0]?.message??"Invalid keybinding",this.keyDiagnostics=e.diagnostics,this.invalidate();return}this.editingKey=null,this.keyInput.focused=!1,this.keyEditError=void 0,this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.keyNotice=void 0,this.invalidate()}cancelKeyEdit(){this.editingKey=null,this.keyInput.focused=!1,this.keyEditError=void 0,this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.invalidate()}async saveKeybindings(){const e=this.keyDraft.resolution();this.keyDiagnostics=e.diagnostics;const t=e.diagnostics.filter(i=>i.severity==="error");if(t.length>0){this.keyNotice=t[0].message,this.invalidate();return}try{const i=await de(this.keyDraft.overrides(),{expectedKeybindings:this.keyDraft.expectedKeybindings(),tmuxPassthrough:this.keyDraft.tmuxPassthrough(),expectedTmuxPassthrough:this.keyDraft.expectedTmuxPassthrough()});this.keyDraft.markSaved(i.overrides,i.tmuxPassthrough),this.keyNotice="Saved. Reload attach and reopen active views to apply changes; refreshing tmux\u2026",this.refreshTmuxBindingsAfterSave().catch(s=>{this.keyNotice=`Saved, but tmux refresh failed: ${s instanceof Error?s.message:String(s)}`,this.invalidate()})}catch(i){i instanceof oe?this.keyNotice="Keybindings changed on disk. Reopen setup before saving.":i instanceof le?(this.keyDiagnostics=i.diagnostics,this.keyNotice=i.message):this.keyNotice=i instanceof Error?i.message:String(i)}this.invalidate()}async refreshTmuxBindingsAfterSave(){const e=ae(),t=e.diagnostics.filter(i=>i.code==="occupied");e.ok&&t.length===0?this.keyNotice="Saved and applied to tmux. Reload attach and reopen active views.":e.ok?this.keyNotice=`Saved; tmux kept occupied keys: ${t.map(i=>i.message).join("; ")}`:this.keyNotice=`Saved, but tmux refresh failed: ${e.diagnostics.find(i=>i.code==="tmux-error")?.message??"tmux unavailable"}`,this.invalidate()}confirmKeyAction(){this.keyConfirm==="reset-all"?(this.keyDraft.resetAll(),this.keyDiagnostics=[],this.keyNotice="All rows reset to catalog defaults. Press save to write."):this.keyConfirm==="discard"&&(this.keyDraft.discard(),this.keyDiagnostics=this.keyDraft.resolution().diagnostics,this.keyNotice="Unsaved keybinding changes discarded."),this.keyConfirm=null,this.invalidate()}handleKeybindingsInput(e){if(this.keyConfirm!==null){this.matches("crtr.setup.confirm.accept",e)?this.confirmKeyAction():this.matches("crtr.setup.confirm.back",e)&&(this.keyConfirm=null,this.invalidate());return}if(this.editingKey!==null){if(this.matches("crtr.setup.keybindings.commit-edit",e)){this.commitKeyEdit();return}if(this.matches("crtr.setup.keybindings.cancel-edit",e)){this.cancelKeyEdit();return}if(this.matches("crtr.setup.keybindings.page-up",e)){this.moveKey(-this.keyPageSize());return}if(this.matches("crtr.setup.keybindings.page-down",e)){this.moveKey(this.keyPageSize());return}this.keyInput.handleInput(e),this.invalidate();return}if(this.filteringKeys){this.matches("crtr.setup.keybindings.filter-clear",e)?(this.keyFilter="",this.filteringKeys=!1,this.keyIndex=0,this.keyScroll=0):this.matches("crtr.setup.keybindings.edit",e)?this.filteringKeys=!1:this.matches("crtr.setup.keybindings.filter-delete",e)?(this.keyFilter=this.keyFilter.slice(0,-1),this.keyIndex=0,this.keyScroll=0):e.length===1&&e.charCodeAt(0)>=32&&(this.keyFilter+=e,this.keyIndex=0,this.keyScroll=0),this.invalidate();return}if(this.matches("crtr.setup.keybindings.filter",e))this.filteringKeys=!0;else if(this.matches("crtr.setup.list-down",e)){this.moveKey(1);return}else if(this.matches("crtr.setup.list-up",e)){this.moveKey(-1);return}else if(this.matches("crtr.setup.cell-previous",e)){this.moveKeyField(-1);return}else if(this.matches("crtr.setup.cell-next",e)){this.moveKeyField(1);return}else if(this.matches("crtr.setup.keybindings.page-up",e)){this.moveKey(-this.keyPageSize());return}else if(this.matches("crtr.setup.keybindings.page-down",e)){this.moveKey(this.keyPageSize());return}else if(this.matches("crtr.setup.toggle",e)){if(this.toggleTmuxPassthrough())return}else if(this.matches("crtr.setup.keybindings.edit",e)){this.beginKeyEdit();return}else if(this.matches("crtr.setup.keybindings.unbind",e)){const t=this.selectedKeybinding();if(t){const i=this.keyDraft.unbind(t.id);this.keyDiagnostics=i.ok?this.keyDraft.resolution().diagnostics:i.diagnostics,this.keyEditError=i.ok?void 0:i.diagnostics[0]?.message}}else if(this.matches("crtr.setup.keybindings.reset-row",e)){const t=this.selectedKeybinding();t&&this.keyDraft.resetRow(t.id),this.keyDiagnostics=this.keyDraft.resolution().diagnostics}else if(this.matches("crtr.setup.keybindings.reset-all",e))this.keyConfirm="reset-all";else if(this.matches("crtr.setup.keybindings.save",e)){this.saveKeybindings();return}else if(this.matches("crtr.setup.keybindings.discard",e))this.keyFilter!==""?(this.keyFilter="",this.keyIndex=0,this.keyScroll=0,this.keyNotice=void 0):this.keyDraft.dirty?this.keyConfirm="discard":this.keyNotice="No unsaved keybinding changes.";else return;this.invalidate()}keyRowMessage(e,t){const i=e[t],s=t===this.keyIndex,a=i.fields.find(r=>r.definition.id===this.editingKey);if(!s&&a===void 0)return;if(a!==void 0&&this.keyEditError)return{label:a.label,message:this.keyEditError};const o=this.keyDiagnostics.find(r=>i.fields.some(l=>r.bindingId===l.definition.id||r.relatedBindingIds?.includes(l.definition.id)));return o===void 0?void 0:{label:i.fields.find(r=>o.bindingId===r.definition.id||o.relatedBindingIds?.includes(r.definition.id))?.label,message:o.message}}keyRowBodyLineCount(e,t,i,s){const a=e.fields.findIndex(x=>x.definition.id===this.editingKey);if(!(a>=0||t===this.keyIndex)||e.fields.length<2)return 1;const f=a>=0?a:Math.min(this.keyFieldIndex,e.fields.length-1),r=e.fields[f],l=a>=0?`> ${this.keyInput.getValue()}`:S(i,r.definition.id),C=`[${r.label}]`;return 3+u(e.name)+1+u(C)+1+u(l)<=s?1:u(r.label??"")+1+u(l)<=s?2:3}keyLineCost(e,t,i,s,a){let o=0,f;for(let r=t;r<=i;r+=1){const l=e[r];o+=(l.group!==f?1:0)+this.keyRowBodyLineCount(l,r,s,a)+(this.keyRowMessage(e,r)?1:0)+(r===this.keyIndex&&this.tmuxPassthroughOffer()!==void 0?1:0),f=l.group}return o}renderKeybindings(e){const t=[],i=this.filteredKeybindings(),s=this.keyDraft.resolution(),a=this.keyViewportLines(),o=this.keyPageSize(),f=Math.max(0,i.length-o);this.keyScroll=Math.max(0,Math.min(this.keyScroll,f)),this.keyIndex<this.keyScroll&&(this.keyScroll=this.keyIndex),this.keyIndex>=this.keyScroll+o&&(this.keyScroll=Math.max(0,this.keyIndex-o+1));const r=this.editingKey!==null?i.findIndex(y=>y.fields.some(d=>d.definition.id===this.editingKey)):-1,l=r>=0?r:this.keyIndex;if(l>=0)for(l<this.keyScroll&&(this.keyScroll=l);this.keyScroll<l&&this.keyLineCost(i,this.keyScroll,l,s,e)>a;)this.keyScroll+=1;const C=this.keyDraft.dirty?n.fg("warning","unsaved"):n.fg("success","saved");if(t.push(h(`${n.fg("accent",n.bold("Keybindings"))}${n.fg("dim"," crouter-owned only \xB7 ")}${C}`,e)),t.push(h(n.fg("dim",this.keyFilter===""?`${i.length} actions`:`${i.length} matches for \u201C${this.keyFilter}\u201D${this.filteringKeys?"":` \xB7 ${this.label("crtr.setup.keybindings.discard")} clears filter`}`),e)),s.diagnostics.some(y=>y.code==="invalid-keybindings")&&t.push(h(n.fg("warning",n.bold(`Saved keybindings root is invalid \u2014 ${this.label("crtr.setup.keybindings.reset-all")} (reset all) to clear it before editing.`)),e)),i.length===0)return t.push(""),t.push(h(n.fg("warning","No matching keybindings."),e)),t;const N=Math.max(10,Math.min(26,Math.floor(e*.28)));let E=0,x;for(let y=this.keyScroll;y<i.length;y+=1){const d=i[y],p=y===this.keyIndex,A=d.fields.findIndex(c=>c.definition.id===this.editingKey),R=A>=0?A:p?Math.min(this.keyFieldIndex,d.fields.length-1):0,v=this.keyRowMessage(i,y),F=p?this.tmuxPassthroughOffer():void 0,T=this.keyRowBodyLineCount(d,y,s,e),V=(d.group!==x?1:0)+T+(v?1:0)+(F?1:0);if(E>0&&E+V>a)break;d.group!==x&&(t.push(h(n.fg("accent",d.group),e)),x=d.group);const z=p?n.fg("accent","\u25B6"):" ",G=d.fields.some(c=>s.get(c.definition.id).source==="user")?n.fg("warning","\u2022"):" ";if(T>1){const c=d.fields[R],g=c.definition.id===this.editingKey,I=g?`> ${this.keyInput.getValue()}`:S(s,c.definition.id),m=`[${c.label}]`,B=c.label??"";if(t.push(h(`${z}${G} ${p?n.fg("accent",d.name):d.name}`,e)),T===2){const k=u(m)+1+u(I)<=e?m:B,$=n.fg("accent",n.bold(k)),D=" ".repeat(Math.max(0,Math.min(3,e-u(k)-1-u(I)))),w=Math.max(1,e-u(D)-u($)-1),L=g?this.keyInput.render(w)[0]??"":I;t.push(h(`${D}${$} ${L}`,e))}else{const K=n.fg("accent",n.bold(m)),k=" ".repeat(Math.max(0,Math.min(3,e-u(K)))),$=g?this.keyInput.render(e)[0]??"":I;t.push(h(`${k}${K}`,e)),t.push(h($,e))}}else{const c=q(p?n.fg("accent",d.name):d.name,N),g=Math.max(1,e-4-N),m=d.fields.length===1||g>=28?d.fields:[d.fields[R]],B=d.fields.length>1?Math.min(61,g):Math.min(34,g),K=Math.max(0,m.length-1),k=Math.max(1,Math.floor((B-K)/m.length)),D=m.map(b=>{const H=d.fields.indexOf(b),_=b.definition.id===this.editingKey,Y=_||p&&H===R,P=b.label===void 0?"":Y?n.fg("accent",n.bold(`[${b.label}]`)):n.fg("dim",`${b.label}:`),J=u(P),Q=Math.max(1,k-J-(P===""?0:1)),U=_?this.keyInput.render(Q)[0]??"":S(s,b.definition.id),X=P===""?U:`${P} ${U}`;return q(h(X,k),k)}).join(" "),w=g-u(D)-1,L=w>=16?` ${n.fg("dim",h(d.description,w))}`:"";t.push(h(`${z}${G} ${c} ${D}${L}`,e))}if(F){const c=this.keyDraft.usesTmuxPassthrough(F.id)?n.fg("success","[x]"):n.fg("dim","[ ]");t.push(h(` ${c} ${n.fg("accent",`Use tmux's ${te(F.gesture)} here`)}`,e))}if(v){const c=v.label===void 0?"":`${v.label}: `;t.push(h(n.fg("warning",` ${c}${v.message}`),e))}E+=V}return this.keyConfirm!==null&&(t.push(""),t.push(h(n.fg("warning",n.bold(this.keyConfirm==="reset-all"?"Reset every keybinding to its catalog default?":"Discard every unsaved keybinding change?")),e))),this.keyNotice&&t.push(h(n.fg("muted",this.keyNotice),e)),t}handleInput(e){return this.handleKeybindingsInput(e),!0}render(e,t){return this.renderKeybindings(e)}footerHint(){return this.keyConfirm!==null?`${this.label("crtr.setup.confirm.accept")} confirm \xB7 ${this.label("crtr.setup.confirm.back")} go back`:this.editingKey!==null?`type gestures separated by commas \xB7 ${this.label("crtr.setup.keybindings.commit-edit")} commit \xB7 ${this.label("crtr.setup.keybindings.cancel-edit")} cancel \xB7 ${this.label("crtr.setup.keybindings.page-up")}/${this.label("crtr.setup.keybindings.page-down")} scroll`:this.filteringKeys?`filter: ${this.keyFilter||"type to search"} \xB7 ${this.label("crtr.setup.keybindings.edit")} keep \xB7 ${this.label("crtr.setup.keybindings.filter-delete")} delete \xB7 ${this.label("crtr.setup.keybindings.filter-clear")} clear`:`${se(this.openingBindings,"crtr.setup.list-down","crtr.setup.list-up")} move \xB7 ${this.label("crtr.setup.cell-previous")}/${this.label("crtr.setup.cell-next")} field \xB7 ${this.label("crtr.setup.keybindings.edit")} edit \xB7 ${this.tmuxPassthroughOffer()===void 0?"":`${this.label("crtr.setup.toggle")} tmux \xB7 `}${this.label("crtr.setup.keybindings.unbind")} disable \xB7 ${this.label("crtr.setup.keybindings.reset-row")} reset \xB7 ${this.label("crtr.setup.keybindings.reset-all")} reset all \xB7 ${this.label("crtr.setup.keybindings.filter")} filter \xB7 ${this.label("crtr.setup.keybindings.save")} save \xB7 ${this.label("crtr.setup.keybindings.discard")} ${this.keyFilter!==""?"clear filter":"discard"}`}}export{be as KeybindingsPanel};
|
|
@@ -18,6 +18,8 @@ export declare class ModelsPanel implements SetupPanel {
|
|
|
18
18
|
* reachable pre-login). Recomputed with every reload/refresh so a login
|
|
19
19
|
* made on the Providers tab shows up when this tab is next entered. */
|
|
20
20
|
private providerKeys;
|
|
21
|
+
/** Managed providers that hold at least one account row, as last read. */
|
|
22
|
+
private managedWithAccounts;
|
|
21
23
|
private readonly targets;
|
|
22
24
|
private readonly precedenceRoots;
|
|
23
25
|
private targetIndex;
|
|
@@ -36,8 +38,10 @@ export declare class ModelsPanel implements SetupPanel {
|
|
|
36
38
|
private pendingRouteCell;
|
|
37
39
|
onChange?: () => void;
|
|
38
40
|
constructor(tui: TUI, runtime: ModelRuntime, registry: ModelRegistry, matches: (id: BindingId, data: string) => boolean, label: (id: BindingId) => string);
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
+
/** Reads which managed providers hold account rows, then re-offers providers. */
|
|
42
|
+
private loadAccountOccupancy;
|
|
43
|
+
/** Same signed-in rule as the broker's Alt+M cycle: account rows for a
|
|
44
|
+
* managed provider (as last read), else the registry's auth status. */
|
|
41
45
|
private offeredProviders;
|
|
42
46
|
private currentTarget;
|
|
43
47
|
private cycleTarget;
|
|
@@ -54,6 +58,8 @@ export declare class ModelsPanel implements SetupPanel {
|
|
|
54
58
|
isPickerOpen(): boolean;
|
|
55
59
|
private currentRow;
|
|
56
60
|
private columnsFor;
|
|
61
|
+
/** A settings write is asynchronous (rows, then the cache); the panel stays responsive and shows a failure as its notice. */
|
|
62
|
+
private settle;
|
|
57
63
|
private clampColumn;
|
|
58
64
|
handleInput(data: string): boolean;
|
|
59
65
|
private activateCell;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var ge=Object.defineProperty;var y=(p,e)=>ge(p,"name",{value:e,configurable:!0});import{envProfileId as pe}from"../../../shared/env.js";import{ModelSelectorComponent as $e,ThinkingSelectorComponent as ee}from"@earendil-works/pi-coding-agent";import{Input as ve,truncateToWidth as V}from"@earendil-works/pi-tui";import{basename as ye,dirname as ke,join as S}from"node:path";import{ANTHROPIC_PROVIDER_ID as be,OPENAI_CODEX_PROVIDER_ID as Re,isManagedProvider as we,readSubscriptionPool as xe}from"../../../core/subscription-state.js";import{readRawConfigAtRoot as P,persistDefaultKindModel as Ie,persistModelLadders as te,persistModelRoute as ie,readMergedLaunchConfig as re}from"../../../core/config.js";import{findProjectScopeRoots as Me,userScopeRoot as Se}from"../../../core/scope.js";import{loadProfileManifest as Pe,profileDir as Ce}from"../../../core/profiles/manifest.js";import{configuredLadderProviders as Te,inferPortableModelSelection as q,MODEL_PROVIDER_KEYS as oe,MODEL_STRENGTHS as k,THINKING_SUFFIXES as se}from"../../../core/runtime/model-selection.js";import{fitTail as U,padAnsi as f,renderRows as Fe,theme as o,wrapText as Oe}from"../../../core/tui/panel.js";const ne=3,Le={anthropic:be,openai:Re},ce={anthropic:"Anthropic",openai:"OpenAI"},de=[{mode:"base",dimension:"family"},{mode:"base",dimension:"strength"},{mode:"orchestrator",dimension:"family"},{mode:"orchestrator",dimension:"strength"}];function Ee(){const p=Se(),e=pe()||null;let t;if(e!==null)try{const s=Pe(e);t={label:`Profile: ${s.manifest.name}`,root:Ce(s.profileId)}}catch{}const i=Me(process.cwd(),e),r=i.map(s=>({label:`Project: ${ye(ke(s))}`,root:s}));return{targets:[{label:"User",root:p},...t?[t]:[],...r],precedenceRoots:[...i,...t?[t.root]:[],p]}}y(Ee,"modelConfigTargets");function j(p){if(!p)return{};const e=p.trim(),t=e.indexOf("/"),i=t===-1?void 0:e.slice(0,t),r=t===-1?e:e.slice(t+1),s=r.match(/^(.*?):(off|minimal|low|medium|high|xhigh|max)$/i);return{provider:i,id:s?s[1]:r,level:s?s[2].toLowerCase():void 0}}y(j,"parseModelRef");function G(p){if(!p)return"\u2014";const e=p.indexOf("/");return e===-1?p:p.slice(e+1)}y(G,"shortRef");class Xe{static{y(this,"ModelsPanel")}tui;runtime;registry;matches;label;mode="list";ladders;modelRoutes;legacyModelRoutes;kinds;providerKeys=oe;targets;precedenceRoots;targetIndex=0;rawTarget;rows=[];selected=0;column=0;notice="";modelSelector;thinkingSelector;pending;choice;choiceIndex=0;routeForm;routeInput=new ve;pendingRouteCell;onChange;constructor(e,t,i,r,s){this.tui=e,this.runtime=t,this.registry=i,this.matches=r,this.label=s;const n=Ee();this.targets=n.targets,this.precedenceRoots=n.precedenceRoots,this.rawTarget=P(this.currentTarget().root);const c=re();this.ladders=c.modelLadders,this.modelRoutes=c.modelRoutes??{},this.legacyModelRoutes=c.legacyModelRoutes??{},this.kinds=c.kinds,this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.routeInput.onSubmit=l=>{const u=this.routeForm;!u||u.editing===null||(u.text[u.editing]=l.trim(),u.editing=null,this.routeInput.focused=!1,this.notify())},this.routeInput.onEscape=()=>{this.routeForm&&(this.routeForm.editing=null,this.routeInput.focused=!1,this.notify())}}offeredProviders(){try{return Te(e=>we(e)&&xe(e).length>0||this.registry.getProviderAuthStatus(e).configured,this.ladders)}catch{return oe}}currentTarget(){return this.targets[this.targetIndex]}cycleTarget(){this.targetIndex=(this.targetIndex+1)%this.targets.length,this.rawTarget=P(this.currentTarget().root),this.notice="",this.notify()}shadowingPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)break;const i=P(t);if(i&&e(i))return S(t,"config.json")}}savedButShadowed(e){return`Saved to ${this.currentTarget().label}; overridden by ${e??"a more specific config"}`}routeIds(){return[...new Set([...Object.keys(this.modelRoutes),...Object.keys(this.legacyModelRoutes)])]}routeFamilies(){return[...new Set(this.routeIds().map(e=>(this.modelRoutes[e]??this.legacyModelRoutes[e]).family))]}buildRows(){const e=[{section:"ladder",kind:"defaultProvider"}];for(const t of k)e.push({section:"ladder",kind:"strength",strength:t});for(const t of this.routeIds())e.push({section:"route",routeId:t});e.push({section:"route",routeId:null});for(const t of Object.keys(this.kinds))t.includes("/")||e.push({section:"kind",name:t});return e}reload(){const e=re();this.ladders=e.modelLadders,this.modelRoutes=e.modelRoutes??{},this.legacyModelRoutes=e.legacyModelRoutes??{},this.kinds=e.kinds,this.rawTarget=P(this.currentTarget().root),this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.selected=Math.min(this.selected,this.rows.length-1),this.clampColumn()}refresh(){this.reload(),this.notify()}notify(){this.onChange?.(),this.tui.requestRender()}isPickerOpen(){return this.mode!=="list"}currentRow(){return this.rows[Math.min(this.selected,this.rows.length-1)]}columnsFor(e){return e.section==="ladder"?e.kind==="strength"?this.providerKeys.length:1:e.section==="route"?1:de.length}clampColumn(){this.column=Math.min(this.column,this.columnsFor(this.currentRow())-1)}handleInput(e){if(this.mode==="route-form"){const t=this.routeForm;return t?t.editing!==null?(this.routeInput.handleInput(e),!0):this.matches("crtr.setup.models.cancel",e)?(this.closeRouteForm(),!0):this.matches("crtr.setup.list-down",e)?(t.index=Math.min(t.fields.length-1,t.index+1),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(t.index=Math.max(0,t.index-1),this.notice="",!0):((this.matches("crtr.setup.models.open",e)||this.matches("crtr.setup.models.commit",e))&&this.activateRouteField(),!0):(this.mode="list",!0)}if(this.mode==="route-delete")return this.matches("crtr.setup.confirm.accept",e)?(this.deleteRoute(),!0):(this.matches("crtr.setup.confirm.back",e)&&(this.mode="route-form",this.notify()),!0);if(this.mode==="dimension-picker")return this.matches("crtr.setup.models.commit",e)?(this.commitChoice(),!0):this.matches("crtr.setup.models.cancel",e)?(this.closePicker(),!0):this.matches("crtr.setup.list-down",e)?(this.choiceIndex=Math.min((this.choice?.options.length??1)-1,this.choiceIndex+1),!0):(this.matches("crtr.setup.list-up",e)&&(this.choiceIndex=Math.max(0,this.choiceIndex-1)),!0);if(this.mode==="model-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.modelSelector?.handleInput(t),!0}if(this.mode==="thinking-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.thinkingSelector?.getSelectList().handleInput(t),!0}return this.matches("crtr.setup.list-down",e)?(this.selected=Math.min(this.rows.length-1,this.selected+1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(this.selected=Math.max(0,this.selected-1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.cell-previous",e)?(this.column=Math.max(0,this.column-1),!0):this.matches("crtr.setup.cell-next",e)?(this.column=Math.min(this.columnsFor(this.currentRow())-1,this.column+1),!0):this.matches("crtr.setup.models.scope-next",e)?(this.cycleTarget(),!0):this.matches("crtr.setup.models.open",e)?(this.activateCell(),!0):!1}activateCell(){const e=this.currentRow();if(this.notice="",e.section==="ladder"&&e.kind==="defaultProvider"){const t=q(void 0,this.ladders).provider,i=this.providerKeys,r=i[(Math.max(0,i.indexOf(t))+1)%i.length];if(r===t)return;try{te(this.currentTarget().root,{defaultProvider:r})}catch(s){this.notice=`Could not save: ${s instanceof Error?s.message:String(s)}`,this.notify();return}if(this.reload(),(this.ladders.defaultProvider??"anthropic")!==r){const s=this.shadowingPath(n=>n.modelLadders?.defaultProvider!==void 0);this.notice=this.savedButShadowed(s)}this.notify();return}if(e.section==="ladder"&&e.kind==="strength"){this.openModelPicker(this.providerKeys[this.column],e.strength);return}if(e.section==="route"){this.openRouteForm(e.routeId);return}if(e.section==="kind"){const t=de[this.column];if(!t)return;this.openDimensionPicker(e.name,t.mode,t.dimension)}}portableSelection(e){if(e!==void 0){const i=e.lastIndexOf("/"),r=i===-1?"":e.slice(0,i),s=i===-1?"":e.slice(i+1);if(this.routeFamilies().includes(r)&&k.includes(s))return{family:r,strength:s}}const t=q(e,this.ladders);return{family:t.provider,strength:t.strength}}familyLabel(e){return e==="anthropic"||e==="openai"?ce[e]:e}familyOptions(){return[...new Set([...this.providerKeys,...this.routeFamilies()])]}kindSelection(e,t){const i=this.kinds[e],r=t==="orchestrator"?i?.orchestratorModel??i?.model:i?.model;return this.portableSelection(r)}openDimensionPicker(e,t,i){const r=this.kindSelection(e,t),s=i==="family"?this.familyOptions():k,n=i==="family"?r.family:r.strength,c=t==="orchestrator"?"orchestrator":"base",l=i==="family"?`strength ${r.strength}`:`family ${this.familyLabel(r.family)}`;this.choice={title:`Choose ${c} ${i}`,subtitle:`${e} \xB7 keeps ${c} ${l}`,options:s,label:y(u=>i==="family"?this.familyLabel(u):u,"label"),commit:y(u=>{const m=i==="family"?u:r.family,a=i==="strength"?u:r.strength;this.writeKindSelection(e,t,m,a)},"commit")},this.choiceIndex=Math.max(0,s.indexOf(n)),this.mode="dimension-picker",this.notify()}writeKindSelection(e,t,i,r){const s=`${i}/${r}`;try{Ie({root:this.currentTarget().root,kind:e,model:s,orchestrator:t==="orchestrator"})}catch(l){this.notice=`Could not save: ${l instanceof Error?l.message:String(l)}`;return}this.reload();const n=this.kinds[e];if((t==="orchestrator"?n?.orchestratorModel:n?.model)!==s){const l=t==="orchestrator"?"orchestratorModel":"model",u=this.shadowingPath(m=>m.kinds?.[e]?.[l]!==void 0);this.notice=this.savedButShadowed(u)}}commitChoice(){const e=this.choice,t=e?.options[this.choiceIndex];e&&t!==void 0&&e.commit(t),this.closePicker()}openRouteForm(e){const t=e===null?void 0:this.modelRoutes[e]??this.legacyModelRoutes[e],i=[...e===null?[{kind:"text",key:"routeId",label:"Route id"}]:[],{kind:"text",key:"family",label:"Family"},{kind:"text",key:"credentialSource",label:"Credential source"},{kind:"provider",label:"Provider id"},...k.map(r=>({kind:"cell",strength:r})),{kind:"save"},...e===null?[]:[{kind:"delete"}]];this.routeForm={existing:e,text:{routeId:e??"",family:t?.family??"",credentialSource:t?.credentialSource??""},providerId:t?.providerId??"",models:{...t?.models},fields:i,index:0,editing:null},this.mode="route-form",this.notice="",this.notify()}closeRouteForm(){this.routeInput.focused=!1,this.routeForm=void 0,this.mode="list",this.notice="",this.notify()}activateRouteField(){const e=this.routeForm,t=e?.fields[e.index];if(!(!e||!t)){if(t.kind==="text"){e.editing=t.key,this.routeInput.setValue(e.text[t.key]),this.routeInput.handleInput(""),this.routeInput.focused=!0,this.notify();return}if(t.kind==="provider"){const i=[...new Set([...this.registry.getAll().map(r=>r.provider),...e.providerId?[e.providerId]:[]])].sort();this.choice={title:"Choose provider id",subtitle:"Route cells must all belong to this registry provider",options:i,label:y(r=>r,"label"),commit:y(r=>this.setRouteProvider(r),"commit")},this.choiceIndex=Math.max(0,i.indexOf(e.providerId)),this.mode="dimension-picker",this.notify();return}if(t.kind==="cell"){this.openRouteCell(t.strength);return}if(t.kind==="save"){this.saveRoute();return}this.mode="route-delete",this.notify()}}setRouteProvider(e){const t=this.routeForm;if(!t)return;t.providerId=e;const i=[];for(const r of k){const s=t.models[r];s!==void 0&&!s.startsWith(`${e}/`)&&(delete t.models[r],i.push(r))}this.notice=i.length===0?"":`Cleared ${i.join(", ")} because those models belong to another provider.`}openRouteCell(e){const t=this.routeForm;if(!t)return;if(!t.providerId){this.notice="Choose the provider id first",this.notify();return}const i=this.registry.getAll().filter(n=>n.provider===t.providerId);if(i.length===0){this.notice=`No models registered for provider "${t.providerId}" \u2014 this route cannot be edited here.`,this.notify();return}const r=j(t.models[e]),s=r.provider&&r.id?this.registry.find(r.provider,r.id):void 0;this.pendingRouteCell={strength:e},this.showModelSelector(i,s,n=>this.onRouteModelPicked(n))}onRouteModelPicked(e){const t=this.pendingRouteCell,i=this.routeForm;if(!t||!i){this.closePicker();return}if(e.provider!==i.providerId){this.notice=`That is a ${e.provider} model \u2014 pick a ${i.providerId} model for this route.`,this.closePicker();return}if(t.model=e,e.reasoning){const r=j(i.models[t.strength]).level??"medium";this.thinkingSelector=new ee(r,[...se],s=>this.onThinkingPicked(s),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}i.models[t.strength]=`${e.provider}/${e.id}`,this.closePicker()}saveRoute(){const e=this.routeForm;if(!e)return;const t=e.text.routeId.trim();if(!t){this.notice="Route id is required.",this.notify();return}if(!e.text.family.trim()){this.notice="Family is required.",this.notify();return}if(!e.text.credentialSource.trim()){this.notice="Credential source is required.",this.notify();return}if(!e.providerId.trim()){this.notice="Provider id is required.",this.notify();return}const i=k.filter(n=>e.models[n]===void 0);if(i.length>0){this.notice=`Choose models for ${i.join(", ")} before saving.`,this.notify();return}const r={family:e.text.family.trim(),credentialSource:e.text.credentialSource.trim(),providerId:e.providerId.trim(),models:{ultra:e.models.ultra,strong:e.models.strong,medium:e.models.medium,light:e.models.light}};try{ie(this.currentTarget().root,t,r)}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`,this.notify();return}this.routeForm=void 0,this.mode="list",this.reload();const s=this.rows.findIndex(n=>n.section==="route"&&n.routeId===t);if(s>=0&&(this.selected=s),JSON.stringify(this.modelRoutes[t])!==JSON.stringify(r)){const n=this.shadowingPath(c=>c.modelRoutes?.[t]!==void 0);this.notice=this.savedButShadowed(n)}else this.notice=`Saved route "${t}" to ${this.currentTarget().label}.`;this.notify()}declaringPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)continue;if(P(t)?.modelRoutes?.[e]!==void 0)return S(t,"config.json")}}deleteRoute(){const e=this.routeForm?.existing;if(!e){this.mode="route-form",this.notify();return}try{ie(this.currentTarget().root,e,void 0)}catch(i){this.mode="route-form",this.notice=`Could not delete: ${i instanceof Error?i.message:String(i)}`,this.notify();return}const t=this.declaringPath(e);this.routeForm=void 0,this.mode="list",this.reload(),this.notice=t?`Removed route "${e}" from ${this.currentTarget().label}; it is still declared by ${t}.`:`Removed route "${e}" from ${this.currentTarget().label}.`,this.notify()}showModelSelector(e,t,i){this.modelSelector=new $e(this.tui,t,this.runtime,e.map(r=>({model:r})),i,()=>this.closePicker()),this.modelSelector.focused=!0,this.mode="model-picker",this.notify()}openModelPicker(e,t){const i=Le[e],r=this.registry,s=j(this.ladders[e][t]),n=s.provider&&s.id?r.find(s.provider,s.id):void 0,c=r.getAvailable().filter(l=>l.provider===i);if(c.length===0){this.notice=`No ${e} models available \u2014 sign in first.`,this.notify();return}this.pending={providerKey:e,strength:t,expectedRuntime:i},this.showModelSelector(c,n,l=>this.onModelPicked(l))}onModelPicked(e){if(!this.pending){this.closePicker();return}if(e.provider!==this.pending.expectedRuntime){this.notice=`That is a ${e.provider} model \u2014 pick an ${this.pending.providerKey} model for this column.`,this.closePicker();return}if(this.pending.model=e,e.reasoning){const t=j(this.ladders[this.pending.providerKey][this.pending.strength]).level??"medium";this.thinkingSelector=new ee(t,[...se],i=>this.onThinkingPicked(i),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}this.writeLadder(e,void 0),this.closePicker()}onThinkingPicked(e){if(this.pendingRouteCell?.model&&this.routeForm){const{model:t,strength:i}=this.pendingRouteCell;this.routeForm.models[i]=`${t.provider}/${t.id}${e!=="off"?`:${e}`:""}`}else this.pending?.model&&this.writeLadder(this.pending.model,e);this.closePicker()}writeLadder(e,t){if(!this.pending)return;const i=`${e.provider}/${e.id}${t&&t!=="off"?`:${t}`:""}`,{providerKey:r,strength:s}=this.pending;try{te(this.currentTarget().root,{[r]:{[s]:i}})}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`;return}if(this.reload(),this.ladders[r][s]!==i){const n=this.shadowingPath(c=>c.modelLadders?.[r]?.[s]!==void 0);this.notice=this.savedButShadowed(n)}}closePicker(){this.mode=this.routeForm!==void 0?"route-form":"list",this.modelSelector=void 0,this.thinkingSelector=void 0,this.pending=void 0,this.pendingRouteCell=void 0,this.choice=void 0,this.notify()}dispose(){this.routeForm=void 0,this.closePicker()}targetLadderOverride(e,t){return this.rawTarget?.modelLadders?.[e]?.[t]!==void 0}targetKindOverride(e,t){const i=this.rawTarget?.kinds,r=t?"orchestratorModel":"model";return i?.[e]?.[r]!==void 0}footerHint(){return this.mode==="dimension-picker"?`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} choice \xB7 ${this.label("crtr.setup.models.commit")} save \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="model-picker"?`type to search \xB7 ${this.label("crtr.setup.models.commit")} select \xB7 tab all/scoped \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="thinking-picker"?`\u2191/\u2193 level \xB7 ${this.label("crtr.setup.models.commit")} set \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="route-delete"?`${this.label("crtr.setup.confirm.accept")} delete \xB7 ${this.label("crtr.setup.confirm.back")} cancel`:this.mode==="route-form"?typeof this.routeForm?.editing=="string"?"type a value \xB7 enter save \xB7 esc cancel":`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} field \xB7 ${this.label("crtr.setup.models.open")} edit \xB7 ${this.label("crtr.setup.models.cancel")} back`:""}renderChoicePicker(){const e=this.choice;if(!e)return[o.fg("warning","No setting selected.")];const t=[o.fg("accent",o.bold(e.title)),o.fg("dim",e.subtitle),""];for(const[i,r]of e.options.entries()){const s=i===this.choiceIndex,n=s?o.fg("accent","\u25B6"):" ",c=e.label(r);t.push(`${n} ${s?o.fg("accent",`[${c}]`):o.fg("dim",` ${c} `)}`)}return t}renderRouteForm(e){const t=this.routeForm;if(!t)return[o.fg("warning","No route selected.")];const i=this.currentTarget(),r=[o.fg("accent",o.bold(t.existing===null?"New provider route":`Provider route: ${t.existing}`)),o.fg("dim",`${i.label} \xB7 ${S(i.root,"config.json")}`),""],s=20;for(const[n,c]of t.fields.entries()){const l=n===t.index,u=l?o.fg("accent","\u25B6"):" ";let m,a;if(c.kind==="text"){if(m=c.label,a=t.text[c.key]||"\u2014",l&&t.editing===c.key){const K=Math.max(8,e-s-4);a=this.routeInput.render(K)[0]??""}}else c.kind==="provider"?(m=c.label,a=t.providerId||"\u2014"):c.kind==="cell"?(m=c.strength,a=G(t.models[c.strength])):c.kind==="save"?(m="",a="Save route"):(m="",a="Delete route");const C=a==="\u2014",T=l?o.fg("accent",`[${a}]`):o.fg(C?"warning":"dim",` ${a} `);r.push(`${u} ${f(m,s)}${T}`)}return this.notice&&r.push("",o.fg("warning",this.notice)),r}renderRouteDelete(e){const t=this.routeForm?.existing??"this route",i=this.currentTarget(),r=Oe(`This removes the entry from ${i.label}. Any "${t}" declared by a lower-precedence config becomes effective again; this does not guarantee the route is gone.`,e).map(s=>o.fg("warning",s));return[o.fg("accent",o.bold(`Delete provider route: ${t}`)),o.fg("dim",S(i.root,"config.json")),"",...r,"",o.fg("dim",`${this.label("crtr.setup.confirm.accept")} delete ${this.label("crtr.setup.confirm.back")} cancel`)]}fitBody(e,t,i){if(e.length<=i)return e;const r=Math.min(ne,e.length-i),s=e.slice(r);if(s.length<=i)return s;const n=Math.max(0,t-r),c=Math.max(0,Math.min(n-i+2,s.length-i));return s.slice(c,c+i)}render(e,t){const i=Math.max(1,t),r=y(d=>Fe(d,e),"frame");if(this.mode==="dimension-picker")return r(this.fitBody(this.renderChoicePicker(),ne+this.choiceIndex,i));if(this.mode==="model-picker")return r(U(this.modelSelector?.render(e)??[],i));if(this.mode==="thinking-picker")return r(U(this.thinkingSelector?.render(e)??[],i));if(this.mode==="route-form"){const d=3+(this.routeForm?.index??0);return r(this.fitBody(this.renderRouteForm(e),d,i))}if(this.mode==="route-delete")return r(U(this.renderRouteDelete(e),i));let s=0;const n=[o.fg("accent",o.bold("Models")),o.fg("dim","global user defaults \xB7 project/profile scopes can shadow these"),""],c=this.currentRow(),l=c.section==="ladder"&&c.kind==="strength",u=Math.max(1,e),m=Math.max(12,Math.min(20,Math.floor(u*.24))),a=this.providerKeys.length,C=Math.max(1,u-2-m),T=12,K=Math.max(1,Math.floor((C+1)/(T+1))),b=Math.min(a,K),N=Math.max(T,Math.floor((C-(b-1))/b)),J=l?Math.min(this.column,a-1):0,F=Math.max(0,Math.min(J>=b?J-b+1:0,a-b)),_=F+b,X=this.providerKeys.map((d,h)=>({provider:d,column:h})).slice(F,_),Y=F>0,z=_<a,he=Y||z?` ${o.fg("dim",`${Y?"\u2039 ":""}${F+1}\u2013${_} of ${a}${z?" \u203A":""}`)}`:"",O=this.currentTarget();n.push(`${o.fg("accent","Editing config")} ${o.fg("accent",`[${O.label}]`)}${o.fg("dim",` ${this.targetIndex+1}/${this.targets.length}`)}`),n.push(o.fg("muted",S(O.root,"config.json")),""),n.push(o.fg("accent","Strength ladders")+he);const le=q(void 0,this.ladders).provider,W=this.rows.find(d=>d.section==="ladder"&&d.kind==="defaultProvider")===c;W&&(s=n.length),n.push(`${W?o.fg("accent","\u25B6"):" "} ${f(W?o.fg("accent","Default provider"):"Default provider",20)} ${o.fg("success",le)}`);const ue=y((d,h)=>{const g=f(o.bold(ce[d]),N);return l&&this.column===h?o.fg("accent",g):o.fg("dim",g)},"providerHeading"),ae=y((d,h)=>{const g=f(V(d,Math.max(1,N-2)),Math.max(1,N-2));return h?o.fg("accent",`[${g}]`):o.fg("dim",` ${g} `)},"ladderCell");n.push(`${" ".repeat(2)}${f(o.fg("dim","Strength"),m)}${X.map(({provider:d,column:h})=>ue(d,h)).join(" ")}`);for(const d of this.rows){if(d.section!=="ladder"||d.kind!=="strength")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=X.map(({provider:v,column:R})=>{const w=this.targetLadderOverride(v,d.strength)?o.fg("warning","\u2022"):" ";return ae(`${w}${G(this.ladders[v][d.strength])}`,h&&R===this.column)});n.push(`${g} ${f(d.strength,m)}${$.join(" ")}`)}n.push("",o.fg("accent","Provider routes")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 a declared route replaces the ladders above at launch \xB7 id, then family \xB7 source \xB7 provider`));const A=Math.max(10,Math.floor((u-8)/4));n.push(`${" ".repeat(4)}${k.map(d=>f(o.fg("dim",d),A)).join(" ")}`),this.routeIds().length===0&&n.push(o.fg("dim"," No routes declared \u2014 the ladders above are used."));for(const d of this.rows){if(d.section!=="route"||d.routeId===null)continue;const h=d.routeId,g=this.modelRoutes[h],$=g??this.legacyModelRoutes[h],v=d===c;v&&(s=n.length);const R=v?o.fg("accent","\u25B6"):" ",w=this.rawTarget?.modelRoutes?.[h]!==void 0?o.fg("warning","\u2022"):" ",E=g?"":o.fg("warning"," \xB7 incomplete"),D=o.fg("dim",`${$.family} \xB7 ${$.credentialSource} \xB7 ${$.providerId}`);n.push(`${R} ${w}${o.fg(v?"accent":"dim",h)} ${D}${E}`);const x=k.map(fe=>{const Z=$.models[fe],me=f(V(G(Z),A),A);return o.fg(Z===void 0?"warning":"dim",me)});n.push(`${" ".repeat(4)}${x.join(" ")}`)}const B=this.rows.find(d=>d.section==="route"&&d.routeId===null)===c;B&&(s=n.length),n.push(`${B?o.fg("accent","\u25B6"):" "} ${o.fg(B?"accent":"dim","+ New route")}`),n.push("",o.fg("accent","Kind defaults")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 orchestrator inherits base until you set it`));const H=16,I=12,M=9,Q=I+1+M,L=y((d,h,g,$)=>{const v=f(V(d,Math.max(1,h-2)),Math.max(1,h-2));return g?o.fg("accent",`[${v}]`):$?o.fg("muted",` ${v} `):o.fg("dim",` ${v} `)},"cell");n.push(`${" ".repeat(2+H)}${f(o.bold(o.fg("dim","Base")),Q)} ${f(o.bold(o.fg("dim","Orchestrator")),Q)}`),n.push(`${" ".repeat(2)}${f(o.fg("dim","Kind"),H)}${f(o.fg("dim","Family"),I)} ${f(o.fg("dim","Strength"),M)} ${f(o.fg("dim","Family"),I)} ${f(o.fg("dim","Strength"),M)}`);for(const d of this.rows){if(d.section!=="kind")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=this.kinds[d.name],v=this.portableSelection($?.model),R=$?.orchestratorModel===void 0,w=this.portableSelection($?.orchestratorModel??$?.model),E=this.targetKindOverride(d.name,!1)?o.fg("warning","\u2022"):" ",D=this.targetKindOverride(d.name,!0)?o.fg("warning","\u2022"):" ",x=[L(`${E}${this.familyLabel(v.family)}`,I,h&&this.column===0,!1),L(`${E}${v.strength}`,M,h&&this.column===1,!1),L(`${D}${this.familyLabel(w.family)}`,I,h&&this.column===2,R),L(`${D}${w.strength}`,M,h&&this.column===3,R)];n.push(`${g} ${f(d.name,H)}${x[0]} ${x[1]} ${x[2]} ${x[3]}`)}return this.notice&&n.push("",o.fg("warning",this.notice)),r(this.fitBody(n,s,i))}}export{Xe as ModelsPanel};
|
|
1
|
+
var ge=Object.defineProperty;var v=(p,e)=>ge(p,"name",{value:e,configurable:!0});import{envProfileId as pe}from"../../../shared/env.js";import{ModelSelectorComponent as ye,ThinkingSelectorComponent as ee}from"@earendil-works/pi-coding-agent";import{Input as $e,truncateToWidth as V}from"@earendil-works/pi-tui";import{basename as ve,dirname as ke,join as I}from"node:path";import{ANTHROPIC_PROVIDER_ID as be,OPENAI_CODEX_PROVIDER_ID as we,isManagedProvider as Re}from"../../../core/managed-providers.js";import{cliClient as xe}from"../../api-client.js";import{readRawConfigAtRoot as P,persistDefaultKindModel as Se,persistModelLadders as te,persistModelRoute as ie,readMergedLaunchConfig as re}from"../../../core/config.js";import{findProjectScopeRoots as Me,userScopeRoot as Ie}from"../../../core/scope.js";import{loadProfileManifest as Pe,profileDir as Ce}from"../../../core/profiles/manifest.js";import{configuredLadderProviders as Te,inferPortableModelSelection as q,MODEL_PROVIDER_KEYS as oe,MODEL_STRENGTHS as k,THINKING_SUFFIXES as se}from"../../../core/runtime/model-selection.js";import{fitTail as U,padAnsi as f,renderRows as Fe,theme as o,wrapText as Oe}from"../../../core/tui/panel.js";const ne=3,Le={anthropic:be,openai:we},ce={anthropic:"Anthropic",openai:"OpenAI"},de=[{mode:"base",dimension:"family"},{mode:"base",dimension:"strength"},{mode:"orchestrator",dimension:"family"},{mode:"orchestrator",dimension:"strength"}];function Ee(){const p=Ie(),e=pe()||null;let t;if(e!==null)try{const s=Pe(e);t={label:`Profile: ${s.manifest.name}`,root:Ce(s.profileId)}}catch{}const i=Me(process.cwd(),e),r=i.map(s=>({label:`Project: ${ve(ke(s))}`,root:s}));return{targets:[{label:"User",root:p},...t?[t]:[],...r],precedenceRoots:[...i,...t?[t.root]:[],p]}}v(Ee,"modelConfigTargets");function K(p){if(!p)return{};const e=p.trim(),t=e.indexOf("/"),i=t===-1?void 0:e.slice(0,t),r=t===-1?e:e.slice(t+1),s=r.match(/^(.*?):(off|minimal|low|medium|high|xhigh|max)$/i);return{provider:i,id:s?s[1]:r,level:s?s[2].toLowerCase():void 0}}v(K,"parseModelRef");function G(p){if(!p)return"\u2014";const e=p.indexOf("/");return e===-1?p:p.slice(e+1)}v(G,"shortRef");class Ye{static{v(this,"ModelsPanel")}tui;runtime;registry;matches;label;mode="list";ladders;modelRoutes;legacyModelRoutes;kinds;providerKeys=oe;managedWithAccounts=new Set;targets;precedenceRoots;targetIndex=0;rawTarget;rows=[];selected=0;column=0;notice="";modelSelector;thinkingSelector;pending;choice;choiceIndex=0;routeForm;routeInput=new $e;pendingRouteCell;onChange;constructor(e,t,i,r,s){this.tui=e,this.runtime=t,this.registry=i,this.matches=r,this.label=s;const n=Ee();this.targets=n.targets,this.precedenceRoots=n.precedenceRoots,this.rawTarget=P(this.currentTarget().root);const c=re();this.ladders=c.modelLadders,this.modelRoutes=c.modelRoutes??{},this.legacyModelRoutes=c.legacyModelRoutes??{},this.kinds=c.kinds,this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.loadAccountOccupancy(),this.routeInput.onSubmit=l=>{const u=this.routeForm;!u||u.editing===null||(u.text[u.editing]=l.trim(),u.editing=null,this.routeInput.focused=!1,this.notify())},this.routeInput.onEscape=()=>{this.routeForm&&(this.routeForm.editing=null,this.routeInput.focused=!1,this.notify())}}async loadAccountOccupancy(){const e=new Set;try{for(const t of(await xe().listModelAuth()).providers)t.managed&&t.accounts.length>0&&e.add(t.provider)}catch{return}this.managedWithAccounts=e,this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.clampColumn(),this.notify()}offeredProviders(){try{return Te(e=>Re(e)&&this.managedWithAccounts.has(e)||this.registry.getProviderAuthStatus(e).configured,this.ladders)}catch{return oe}}currentTarget(){return this.targets[this.targetIndex]}cycleTarget(){this.targetIndex=(this.targetIndex+1)%this.targets.length,this.rawTarget=P(this.currentTarget().root),this.notice="",this.notify()}shadowingPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)break;const i=P(t);if(i&&e(i))return I(t,"config.json")}}savedButShadowed(e){return`Saved to ${this.currentTarget().label}; overridden by ${e??"a more specific config"}`}routeIds(){return[...new Set([...Object.keys(this.modelRoutes),...Object.keys(this.legacyModelRoutes)])]}routeFamilies(){return[...new Set(this.routeIds().map(e=>(this.modelRoutes[e]??this.legacyModelRoutes[e]).family))]}buildRows(){const e=[{section:"ladder",kind:"defaultProvider"}];for(const t of k)e.push({section:"ladder",kind:"strength",strength:t});for(const t of this.routeIds())e.push({section:"route",routeId:t});e.push({section:"route",routeId:null});for(const t of Object.keys(this.kinds))t.includes("/")||e.push({section:"kind",name:t});return e}reload(){const e=re();this.ladders=e.modelLadders,this.modelRoutes=e.modelRoutes??{},this.legacyModelRoutes=e.legacyModelRoutes??{},this.kinds=e.kinds,this.rawTarget=P(this.currentTarget().root),this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.selected=Math.min(this.selected,this.rows.length-1),this.clampColumn()}refresh(){this.reload(),this.notify(),this.loadAccountOccupancy()}notify(){this.onChange?.(),this.tui.requestRender()}isPickerOpen(){return this.mode!=="list"}currentRow(){return this.rows[Math.min(this.selected,this.rows.length-1)]}columnsFor(e){return e.section==="ladder"?e.kind==="strength"?this.providerKeys.length:1:e.section==="route"?1:de.length}settle(e){e.catch(t=>{this.notice=`Could not save: ${t instanceof Error?t.message:String(t)}`}).finally(()=>this.notify())}clampColumn(){this.column=Math.min(this.column,this.columnsFor(this.currentRow())-1)}handleInput(e){if(this.mode==="route-form"){const t=this.routeForm;return t?t.editing!==null?(this.routeInput.handleInput(e),!0):this.matches("crtr.setup.models.cancel",e)?(this.closeRouteForm(),!0):this.matches("crtr.setup.list-down",e)?(t.index=Math.min(t.fields.length-1,t.index+1),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(t.index=Math.max(0,t.index-1),this.notice="",!0):((this.matches("crtr.setup.models.open",e)||this.matches("crtr.setup.models.commit",e))&&this.settle(this.activateRouteField()),!0):(this.mode="list",!0)}if(this.mode==="route-delete")return this.matches("crtr.setup.confirm.accept",e)?(this.settle(this.deleteRoute()),!0):(this.matches("crtr.setup.confirm.back",e)&&(this.mode="route-form",this.notify()),!0);if(this.mode==="dimension-picker")return this.matches("crtr.setup.models.commit",e)?(this.commitChoice(),!0):this.matches("crtr.setup.models.cancel",e)?(this.closePicker(),!0):this.matches("crtr.setup.list-down",e)?(this.choiceIndex=Math.min((this.choice?.options.length??1)-1,this.choiceIndex+1),!0):(this.matches("crtr.setup.list-up",e)&&(this.choiceIndex=Math.max(0,this.choiceIndex-1)),!0);if(this.mode==="model-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.modelSelector?.handleInput(t),!0}if(this.mode==="thinking-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.thinkingSelector?.getSelectList().handleInput(t),!0}return this.matches("crtr.setup.list-down",e)?(this.selected=Math.min(this.rows.length-1,this.selected+1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(this.selected=Math.max(0,this.selected-1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.cell-previous",e)?(this.column=Math.max(0,this.column-1),!0):this.matches("crtr.setup.cell-next",e)?(this.column=Math.min(this.columnsFor(this.currentRow())-1,this.column+1),!0):this.matches("crtr.setup.models.scope-next",e)?(this.cycleTarget(),!0):this.matches("crtr.setup.models.open",e)?(this.settle(this.activateCell()),!0):!1}async activateCell(){const e=this.currentRow();if(this.notice="",e.section==="ladder"&&e.kind==="defaultProvider"){const t=q(void 0,this.ladders).provider,i=this.providerKeys,r=i[(Math.max(0,i.indexOf(t))+1)%i.length];if(r===t)return;try{await te(this.currentTarget().root,{defaultProvider:r})}catch(s){this.notice=`Could not save: ${s instanceof Error?s.message:String(s)}`,this.notify();return}if(this.reload(),(this.ladders.defaultProvider??"anthropic")!==r){const s=this.shadowingPath(n=>n.modelLadders?.defaultProvider!==void 0);this.notice=this.savedButShadowed(s)}this.notify();return}if(e.section==="ladder"&&e.kind==="strength"){this.openModelPicker(this.providerKeys[this.column],e.strength);return}if(e.section==="route"){this.openRouteForm(e.routeId);return}if(e.section==="kind"){const t=de[this.column];if(!t)return;this.openDimensionPicker(e.name,t.mode,t.dimension)}}portableSelection(e){if(e!==void 0){const i=e.lastIndexOf("/"),r=i===-1?"":e.slice(0,i),s=i===-1?"":e.slice(i+1);if(this.routeFamilies().includes(r)&&k.includes(s))return{family:r,strength:s}}const t=q(e,this.ladders);return{family:t.provider,strength:t.strength}}familyLabel(e){return e==="anthropic"||e==="openai"?ce[e]:e}familyOptions(){return[...new Set([...this.providerKeys,...this.routeFamilies()])]}kindSelection(e,t){const i=this.kinds[e],r=t==="orchestrator"?i?.orchestratorModel??i?.model:i?.model;return this.portableSelection(r)}openDimensionPicker(e,t,i){const r=this.kindSelection(e,t),s=i==="family"?this.familyOptions():k,n=i==="family"?r.family:r.strength,c=t==="orchestrator"?"orchestrator":"base",l=i==="family"?`strength ${r.strength}`:`family ${this.familyLabel(r.family)}`;this.choice={title:`Choose ${c} ${i}`,subtitle:`${e} \xB7 keeps ${c} ${l}`,options:s,label:v(u=>i==="family"?this.familyLabel(u):u,"label"),commit:v(async u=>{const m=i==="family"?u:r.family,a=i==="strength"?u:r.strength;await this.writeKindSelection(e,t,m,a)},"commit")},this.choiceIndex=Math.max(0,s.indexOf(n)),this.mode="dimension-picker",this.notify()}async writeKindSelection(e,t,i,r){const s=`${i}/${r}`;try{await Se({root:this.currentTarget().root,kind:e,model:s,orchestrator:t==="orchestrator"})}catch(l){this.notice=`Could not save: ${l instanceof Error?l.message:String(l)}`;return}this.reload();const n=this.kinds[e];if((t==="orchestrator"?n?.orchestratorModel:n?.model)!==s){const l=t==="orchestrator"?"orchestratorModel":"model",u=this.shadowingPath(m=>m.kinds?.[e]?.[l]!==void 0);this.notice=this.savedButShadowed(u)}}commitChoice(){const e=this.choice,t=e?.options[this.choiceIndex];e&&t!==void 0&&e.commit(t),this.closePicker()}openRouteForm(e){const t=e===null?void 0:this.modelRoutes[e]??this.legacyModelRoutes[e],i=[...e===null?[{kind:"text",key:"routeId",label:"Route id"}]:[],{kind:"text",key:"family",label:"Family"},{kind:"text",key:"credentialSource",label:"Credential source"},{kind:"provider",label:"Provider id"},...k.map(r=>({kind:"cell",strength:r})),{kind:"save"},...e===null?[]:[{kind:"delete"}]];this.routeForm={existing:e,text:{routeId:e??"",family:t?.family??"",credentialSource:t?.credentialSource??""},providerId:t?.providerId??"",models:{...t?.models},fields:i,index:0,editing:null},this.mode="route-form",this.notice="",this.notify()}closeRouteForm(){this.routeInput.focused=!1,this.routeForm=void 0,this.mode="list",this.notice="",this.notify()}async activateRouteField(){const e=this.routeForm,t=e?.fields[e.index];if(!(!e||!t)){if(t.kind==="text"){e.editing=t.key,this.routeInput.setValue(e.text[t.key]),this.routeInput.handleInput(""),this.routeInput.focused=!0,this.notify();return}if(t.kind==="provider"){const i=[...new Set([...this.registry.getAll().map(r=>r.provider),...e.providerId?[e.providerId]:[]])].sort();this.choice={title:"Choose provider id",subtitle:"Route cells must all belong to this registry provider",options:i,label:v(r=>r,"label"),commit:v(r=>this.setRouteProvider(r),"commit")},this.choiceIndex=Math.max(0,i.indexOf(e.providerId)),this.mode="dimension-picker",this.notify();return}if(t.kind==="cell"){this.openRouteCell(t.strength);return}if(t.kind==="save"){await this.saveRoute();return}this.mode="route-delete",this.notify()}}setRouteProvider(e){const t=this.routeForm;if(!t)return;t.providerId=e;const i=[];for(const r of k){const s=t.models[r];s!==void 0&&!s.startsWith(`${e}/`)&&(delete t.models[r],i.push(r))}this.notice=i.length===0?"":`Cleared ${i.join(", ")} because those models belong to another provider.`}openRouteCell(e){const t=this.routeForm;if(!t)return;if(!t.providerId){this.notice="Choose the provider id first",this.notify();return}const i=this.registry.getAll().filter(n=>n.provider===t.providerId);if(i.length===0){this.notice=`No models registered for provider "${t.providerId}" \u2014 this route cannot be edited here.`,this.notify();return}const r=K(t.models[e]),s=r.provider&&r.id?this.registry.find(r.provider,r.id):void 0;this.pendingRouteCell={strength:e},this.showModelSelector(i,s,n=>this.onRouteModelPicked(n))}onRouteModelPicked(e){const t=this.pendingRouteCell,i=this.routeForm;if(!t||!i){this.closePicker();return}if(e.provider!==i.providerId){this.notice=`That is a ${e.provider} model \u2014 pick a ${i.providerId} model for this route.`,this.closePicker();return}if(t.model=e,e.reasoning){const r=K(i.models[t.strength]).level??"medium";this.thinkingSelector=new ee(r,[...se],s=>this.onThinkingPicked(s),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}i.models[t.strength]=`${e.provider}/${e.id}`,this.closePicker()}async saveRoute(){const e=this.routeForm;if(!e)return;const t=e.text.routeId.trim();if(!t){this.notice="Route id is required.",this.notify();return}if(!e.text.family.trim()){this.notice="Family is required.",this.notify();return}if(!e.text.credentialSource.trim()){this.notice="Credential source is required.",this.notify();return}if(!e.providerId.trim()){this.notice="Provider id is required.",this.notify();return}const i=k.filter(n=>e.models[n]===void 0);if(i.length>0){this.notice=`Choose models for ${i.join(", ")} before saving.`,this.notify();return}const r={family:e.text.family.trim(),credentialSource:e.text.credentialSource.trim(),providerId:e.providerId.trim(),models:{ultra:e.models.ultra,strong:e.models.strong,medium:e.models.medium,light:e.models.light}};try{await ie(this.currentTarget().root,t,r)}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`,this.notify();return}this.routeForm=void 0,this.mode="list",this.reload();const s=this.rows.findIndex(n=>n.section==="route"&&n.routeId===t);if(s>=0&&(this.selected=s),JSON.stringify(this.modelRoutes[t])!==JSON.stringify(r)){const n=this.shadowingPath(c=>c.modelRoutes?.[t]!==void 0);this.notice=this.savedButShadowed(n)}else this.notice=`Saved route "${t}" to ${this.currentTarget().label}.`;this.notify()}declaringPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)continue;if(P(t)?.modelRoutes?.[e]!==void 0)return I(t,"config.json")}}async deleteRoute(){const e=this.routeForm?.existing;if(!e){this.mode="route-form",this.notify();return}try{await ie(this.currentTarget().root,e,void 0)}catch(i){this.mode="route-form",this.notice=`Could not delete: ${i instanceof Error?i.message:String(i)}`,this.notify();return}const t=this.declaringPath(e);this.routeForm=void 0,this.mode="list",this.reload(),this.notice=t?`Removed route "${e}" from ${this.currentTarget().label}; it is still declared by ${t}.`:`Removed route "${e}" from ${this.currentTarget().label}.`,this.notify()}showModelSelector(e,t,i){this.modelSelector=new ye(this.tui,t,this.runtime,e.map(r=>({model:r})),i,()=>this.closePicker()),this.modelSelector.focused=!0,this.mode="model-picker",this.notify()}openModelPicker(e,t){const i=Le[e],r=this.registry,s=K(this.ladders[e][t]),n=s.provider&&s.id?r.find(s.provider,s.id):void 0,c=r.getAvailable().filter(l=>l.provider===i);if(c.length===0){this.notice=`No ${e} models available \u2014 sign in first.`,this.notify();return}this.pending={providerKey:e,strength:t,expectedRuntime:i},this.showModelSelector(c,n,l=>this.onModelPicked(l))}async onModelPicked(e){if(!this.pending){this.closePicker();return}if(e.provider!==this.pending.expectedRuntime){this.notice=`That is a ${e.provider} model \u2014 pick an ${this.pending.providerKey} model for this column.`,this.closePicker();return}if(this.pending.model=e,e.reasoning){const t=K(this.ladders[this.pending.providerKey][this.pending.strength]).level??"medium";this.thinkingSelector=new ee(t,[...se],i=>this.onThinkingPicked(i),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}await this.writeLadder(e,void 0),this.closePicker()}async onThinkingPicked(e){if(this.pendingRouteCell?.model&&this.routeForm){const{model:t,strength:i}=this.pendingRouteCell;this.routeForm.models[i]=`${t.provider}/${t.id}${e!=="off"?`:${e}`:""}`}else this.pending?.model&&await this.writeLadder(this.pending.model,e);this.closePicker()}async writeLadder(e,t){if(!this.pending)return;const i=`${e.provider}/${e.id}${t&&t!=="off"?`:${t}`:""}`,{providerKey:r,strength:s}=this.pending;try{await te(this.currentTarget().root,{[r]:{[s]:i}})}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`;return}if(this.reload(),this.ladders[r][s]!==i){const n=this.shadowingPath(c=>c.modelLadders?.[r]?.[s]!==void 0);this.notice=this.savedButShadowed(n)}}closePicker(){this.mode=this.routeForm!==void 0?"route-form":"list",this.modelSelector=void 0,this.thinkingSelector=void 0,this.pending=void 0,this.pendingRouteCell=void 0,this.choice=void 0,this.notify()}dispose(){this.routeForm=void 0,this.closePicker()}targetLadderOverride(e,t){return this.rawTarget?.modelLadders?.[e]?.[t]!==void 0}targetKindOverride(e,t){const i=this.rawTarget?.kinds,r=t?"orchestratorModel":"model";return i?.[e]?.[r]!==void 0}footerHint(){return this.mode==="dimension-picker"?`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} choice \xB7 ${this.label("crtr.setup.models.commit")} save \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="model-picker"?`type to search \xB7 ${this.label("crtr.setup.models.commit")} select \xB7 tab all/scoped \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="thinking-picker"?`\u2191/\u2193 level \xB7 ${this.label("crtr.setup.models.commit")} set \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="route-delete"?`${this.label("crtr.setup.confirm.accept")} delete \xB7 ${this.label("crtr.setup.confirm.back")} cancel`:this.mode==="route-form"?typeof this.routeForm?.editing=="string"?"type a value \xB7 enter save \xB7 esc cancel":`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} field \xB7 ${this.label("crtr.setup.models.open")} edit \xB7 ${this.label("crtr.setup.models.cancel")} back`:""}renderChoicePicker(){const e=this.choice;if(!e)return[o.fg("warning","No setting selected.")];const t=[o.fg("accent",o.bold(e.title)),o.fg("dim",e.subtitle),""];for(const[i,r]of e.options.entries()){const s=i===this.choiceIndex,n=s?o.fg("accent","\u25B6"):" ",c=e.label(r);t.push(`${n} ${s?o.fg("accent",`[${c}]`):o.fg("dim",` ${c} `)}`)}return t}renderRouteForm(e){const t=this.routeForm;if(!t)return[o.fg("warning","No route selected.")];const i=this.currentTarget(),r=[o.fg("accent",o.bold(t.existing===null?"New provider route":`Provider route: ${t.existing}`)),o.fg("dim",`${i.label} \xB7 ${I(i.root,"config.json")}`),""],s=20;for(const[n,c]of t.fields.entries()){const l=n===t.index,u=l?o.fg("accent","\u25B6"):" ";let m,a;if(c.kind==="text"){if(m=c.label,a=t.text[c.key]||"\u2014",l&&t.editing===c.key){const j=Math.max(8,e-s-4);a=this.routeInput.render(j)[0]??""}}else c.kind==="provider"?(m=c.label,a=t.providerId||"\u2014"):c.kind==="cell"?(m=c.strength,a=G(t.models[c.strength])):c.kind==="save"?(m="",a="Save route"):(m="",a="Delete route");const C=a==="\u2014",T=l?o.fg("accent",`[${a}]`):o.fg(C?"warning":"dim",` ${a} `);r.push(`${u} ${f(m,s)}${T}`)}return this.notice&&r.push("",o.fg("warning",this.notice)),r}renderRouteDelete(e){const t=this.routeForm?.existing??"this route",i=this.currentTarget(),r=Oe(`This removes the entry from ${i.label}. Any "${t}" declared by a lower-precedence config becomes effective again; this does not guarantee the route is gone.`,e).map(s=>o.fg("warning",s));return[o.fg("accent",o.bold(`Delete provider route: ${t}`)),o.fg("dim",I(i.root,"config.json")),"",...r,"",o.fg("dim",`${this.label("crtr.setup.confirm.accept")} delete ${this.label("crtr.setup.confirm.back")} cancel`)]}fitBody(e,t,i){if(e.length<=i)return e;const r=Math.min(ne,e.length-i),s=e.slice(r);if(s.length<=i)return s;const n=Math.max(0,t-r),c=Math.max(0,Math.min(n-i+2,s.length-i));return s.slice(c,c+i)}render(e,t){const i=Math.max(1,t),r=v(d=>Fe(d,e),"frame");if(this.mode==="dimension-picker")return r(this.fitBody(this.renderChoicePicker(),ne+this.choiceIndex,i));if(this.mode==="model-picker")return r(U(this.modelSelector?.render(e)??[],i));if(this.mode==="thinking-picker")return r(U(this.thinkingSelector?.render(e)??[],i));if(this.mode==="route-form"){const d=3+(this.routeForm?.index??0);return r(this.fitBody(this.renderRouteForm(e),d,i))}if(this.mode==="route-delete")return r(U(this.renderRouteDelete(e),i));let s=0;const n=[o.fg("accent",o.bold("Models")),o.fg("dim","global user defaults \xB7 project/profile scopes can shadow these"),""],c=this.currentRow(),l=c.section==="ladder"&&c.kind==="strength",u=Math.max(1,e),m=Math.max(12,Math.min(20,Math.floor(u*.24))),a=this.providerKeys.length,C=Math.max(1,u-2-m),T=12,j=Math.max(1,Math.floor((C+1)/(T+1))),b=Math.min(a,j),A=Math.max(T,Math.floor((C-(b-1))/b)),J=l?Math.min(this.column,a-1):0,F=Math.max(0,Math.min(J>=b?J-b+1:0,a-b)),N=F+b,X=this.providerKeys.map((d,h)=>({provider:d,column:h})).slice(F,N),Y=F>0,z=N<a,he=Y||z?` ${o.fg("dim",`${Y?"\u2039 ":""}${F+1}\u2013${N} of ${a}${z?" \u203A":""}`)}`:"",O=this.currentTarget();n.push(`${o.fg("accent","Editing config")} ${o.fg("accent",`[${O.label}]`)}${o.fg("dim",` ${this.targetIndex+1}/${this.targets.length}`)}`),n.push(o.fg("muted",I(O.root,"config.json")),""),n.push(o.fg("accent","Strength ladders")+he);const le=q(void 0,this.ladders).provider,W=this.rows.find(d=>d.section==="ladder"&&d.kind==="defaultProvider")===c;W&&(s=n.length),n.push(`${W?o.fg("accent","\u25B6"):" "} ${f(W?o.fg("accent","Default provider"):"Default provider",20)} ${o.fg("success",le)}`);const ue=v((d,h)=>{const g=f(o.bold(ce[d]),A);return l&&this.column===h?o.fg("accent",g):o.fg("dim",g)},"providerHeading"),ae=v((d,h)=>{const g=f(V(d,Math.max(1,A-2)),Math.max(1,A-2));return h?o.fg("accent",`[${g}]`):o.fg("dim",` ${g} `)},"ladderCell");n.push(`${" ".repeat(2)}${f(o.fg("dim","Strength"),m)}${X.map(({provider:d,column:h})=>ue(d,h)).join(" ")}`);for(const d of this.rows){if(d.section!=="ladder"||d.kind!=="strength")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const y=X.map(({provider:$,column:w})=>{const R=this.targetLadderOverride($,d.strength)?o.fg("warning","\u2022"):" ";return ae(`${R}${G(this.ladders[$][d.strength])}`,h&&w===this.column)});n.push(`${g} ${f(d.strength,m)}${y.join(" ")}`)}n.push("",o.fg("accent","Provider routes")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 a declared route replaces the ladders above at launch \xB7 id, then family \xB7 source \xB7 provider`));const _=Math.max(10,Math.floor((u-8)/4));n.push(`${" ".repeat(4)}${k.map(d=>f(o.fg("dim",d),_)).join(" ")}`),this.routeIds().length===0&&n.push(o.fg("dim"," No routes declared \u2014 the ladders above are used."));for(const d of this.rows){if(d.section!=="route"||d.routeId===null)continue;const h=d.routeId,g=this.modelRoutes[h],y=g??this.legacyModelRoutes[h],$=d===c;$&&(s=n.length);const w=$?o.fg("accent","\u25B6"):" ",R=this.rawTarget?.modelRoutes?.[h]!==void 0?o.fg("warning","\u2022"):" ",E=g?"":o.fg("warning"," \xB7 incomplete"),D=o.fg("dim",`${y.family} \xB7 ${y.credentialSource} \xB7 ${y.providerId}`);n.push(`${w} ${R}${o.fg($?"accent":"dim",h)} ${D}${E}`);const x=k.map(fe=>{const Z=y.models[fe],me=f(V(G(Z),_),_);return o.fg(Z===void 0?"warning":"dim",me)});n.push(`${" ".repeat(4)}${x.join(" ")}`)}const B=this.rows.find(d=>d.section==="route"&&d.routeId===null)===c;B&&(s=n.length),n.push(`${B?o.fg("accent","\u25B6"):" "} ${o.fg(B?"accent":"dim","+ New route")}`),n.push("",o.fg("accent","Kind defaults")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 orchestrator inherits base until you set it`));const H=16,S=12,M=9,Q=S+1+M,L=v((d,h,g,y)=>{const $=f(V(d,Math.max(1,h-2)),Math.max(1,h-2));return g?o.fg("accent",`[${$}]`):y?o.fg("muted",` ${$} `):o.fg("dim",` ${$} `)},"cell");n.push(`${" ".repeat(2+H)}${f(o.bold(o.fg("dim","Base")),Q)} ${f(o.bold(o.fg("dim","Orchestrator")),Q)}`),n.push(`${" ".repeat(2)}${f(o.fg("dim","Kind"),H)}${f(o.fg("dim","Family"),S)} ${f(o.fg("dim","Strength"),M)} ${f(o.fg("dim","Family"),S)} ${f(o.fg("dim","Strength"),M)}`);for(const d of this.rows){if(d.section!=="kind")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const y=this.kinds[d.name],$=this.portableSelection(y?.model),w=y?.orchestratorModel===void 0,R=this.portableSelection(y?.orchestratorModel??y?.model),E=this.targetKindOverride(d.name,!1)?o.fg("warning","\u2022"):" ",D=this.targetKindOverride(d.name,!0)?o.fg("warning","\u2022"):" ",x=[L(`${E}${this.familyLabel($.family)}`,S,h&&this.column===0,!1),L(`${E}${$.strength}`,M,h&&this.column===1,!1),L(`${D}${this.familyLabel(R.family)}`,S,h&&this.column===2,w),L(`${D}${R.strength}`,M,h&&this.column===3,w)];n.push(`${g} ${f(d.name,H)}${x[0]} ${x[1]} ${x[2]} ${x[3]}`)}return this.notice&&n.push("",o.fg("warning",this.notice)),r(this.fitBody(n,s,i))}}export{Ye as ModelsPanel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $=Object.defineProperty;var p=(g,t)=>$(g,"name",{value:t,configurable:!0});import{truncateToWidth as l}from"@earendil-works/pi-tui";import{readConfig as b,updateConfig as w}from"../../../core/config.js";import{userSettingsInGroup as v}from"../../../core/user-settings.js";import{padAnsi as u,theme as r,visibleRange as y,wrapText as d}from"../../../core/tui/panel.js";const x=26;class B{static{p(this,"PrefsPanel")}title;subtitle;onChange;rows;index=0;notice;constructor(t,e,o,s){this.title=e,this.subtitle=o,this.onChange=s,this.rows=v(t)}badge(){return String(this.rows.length)}handleInput(t,e){if(!e||this.rows.length===0)return!1;if(e("crtr.setup.list-down",t))this.index=(this.index+1)%this.rows.length;else if(e("crtr.setup.list-up",t))this.index=(this.index-1+this.rows.length)%this.rows.length;else if(e("crtr.setup.toggle",t)||e("crtr.setup.cell-next",t))this.cycle(1);else if(e("crtr.setup.cell-previous",t))this.cycle(-1);else return!1;return this.onChange(),!0}cycle(t){this.save(t)}async save(t){const e=this.rows[this.index];let o;try{o=await w("user",s=>e.cycle(s,t))}catch(s){this.notice=`Could not save: ${s instanceof Error?s.message:String(s)}`,this.onChange();return}this.notice=`Saved ${e.key} = ${e.format(o)}. ${e.applies}`,this.onChange()}render(t,e){const o=b("user"),s=Math.max(1,e-6),h=y(this.rows.length,this.index,s),i=[l(r.fg("accent",r.bold(this.title)),t),l(r.fg("dim",this.subtitle),t),""];for(let n=h.start;n<h.end;n+=1){const c=this.rows[n],a=c.format(o);if(n===this.index){const m=`\u25B8 ${u(c.label,x)} ${a}`;i.push(r.bg("selectedBg",u(m,t)))}else i.push(l(` ${u(c.label,x)} ${r.fg("success",a)}`,t))}(h.start>0||h.end<this.rows.length)&&i.push(l(r.fg("dim",` ${this.index+1}/${this.rows.length}`),t));const f=this.rows[this.index];if(f){i.push("");for(const n of d(f.description,Math.max(1,t-2)))i.push(l(r.fg("dim",` ${n}`),t))}if(this.notice){i.push("");for(const n of d(this.notice,t))i.push(l(r.fg("success",n),t))}return i}}export{B as PrefsPanel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var O=Object.defineProperty;var g=(m,e)=>O(m,"name",{value:e,configurable:!0});import{envNodeId as E}from"../../../shared/env.js";import{Input as D,matchesKey as I,truncateToWidth as a}from"@earendil-works/pi-tui";import{basename as F}from"node:path";import{PROFILE_PROJECT_MEMORY_VALUES as _}from"../../../api/dto/profiles.js";import{cliClient as x}from"../../api-client.js";import{clearDefaultProfile as N,defaultProfileDirs as S,getDefaultProfileId as H,setDefaultProfileId as V}from"../../../core/profiles/default-binding.js";import{ROOT_PROFILE_ID as j,addProfileProject as M,createProfile as A,listProfiles as U,removeProfileProject as W,setProfileDeliveryLimit as B}from"../../../core/profiles/manifest.js";import{tildify as c}from"../../../core/fs-utils.js";import{profileCoversCwd as T}from"../../../core/profiles/select.js";import{padAnsi as f,theme as o,visibleRange as k,wrapText as L}from"../../../core/tui/panel.js";function C(m){if(m===null)return"never used";const e=Date.parse(m);if(Number.isNaN(e))return"used recently";const t=Math.max(0,Math.floor((Date.now()-e)/1e3));if(t<60)return"used just now";const i=Math.floor(t/60);if(i<60)return`used ${i}m ago`;const n=Math.floor(i/60);if(n<24)return`used ${n}h ago`;const r=Math.floor(n/24);if(r<30)return`used ${r}d ago`;const s=Math.floor(r/30);return s<12?`used ${s}mo ago`:`used ${Math.floor(s/12)}y ago`}g(C,"relativeUsed");function b(m){return m instanceof Error?m.message:String(m)}g(b,"messageOf");class ie{static{g(this,"ProfilesPanel")}cwd;onChange;nameInput=new D;dirInput=new D;limitInput=new D;entries=[];listIndex=0;dirIndex=0;mode="list";openId=null;notice;noticeTone="success";mutating=!1;constructor(e,t){this.cwd=e,this.onChange=t,this.nameInput.onSubmit=()=>{this.submitName(),this.onChange()},this.nameInput.onEscape=()=>{this.mode=this.openId!==null?"detail":"list",this.leaveField(),this.onChange()},this.dirInput.onSubmit=()=>{this.submitDir(),this.onChange()},this.dirInput.onEscape=()=>{this.mode="detail",this.leaveField(),this.onChange()},this.limitInput.onSubmit=()=>{this.submitLimit(),this.onChange()},this.limitInput.onEscape=()=>{this.mode="detail",this.leaveField(),this.onChange()},this.reload()}badge(){return String(this.entries.length)}ownsInput(e){return this.mode==="list"?!1:this.mode==="detail"?I(e,"escape")||I(e,"left"):!0}reload(){const e=g(n=>n.profileId===j?2:T(n,this.cwd)?0:1,"rank"),t=g(n=>n.manifest.last_used_at??"","used");this.entries=U().sort((n,r)=>{const s=e(n)-e(r);if(s!==0)return s;const d=t(r).localeCompare(t(n));return d!==0?d:n.manifest.name.localeCompare(r.manifest.name)}),this.listIndex>=this.entries.length&&(this.listIndex=Math.max(0,this.entries.length-1));const i=this.open();i===null?(this.mode!=="list"&&(this.mode="list"),this.openId=null):this.dirIndex>=i.manifest.projects.length&&(this.dirIndex=Math.max(0,i.manifest.projects.length-1))}selected(){return this.entries[this.listIndex]??null}open(){return this.openId===null?null:this.entries.find(e=>e.profileId===this.openId)??null}currentDir(){return this.open()?.manifest.projects[this.dirIndex]??null}pinnedHere(){return H(this.cwd)}say(e,t="success"){this.notice=e,this.noticeTone=t}attempt(e,t){try{e(),this.reload(),this.say(t)}catch(i){this.say(b(i),"warning")}}handleInput(e,t){const i=this.route(e,t);return i&&this.onChange(),i}route(e,t){switch(this.mode){case"new-name":case"rename":return this.routeText(this.nameInput,e);case"add-dir":return this.routeText(this.dirInput,e);case"set-limit":return this.routeText(this.limitInput,e);case"confirm-delete":return this.routeConfirm(e,()=>{this.deleteOpen()});case"confirm-remove-dir":return this.routeConfirm(e,()=>this.removeDir());case"detail":return this.routeDetail(e,t);case"list":return this.routeList(e,t)}}routeText(e,t){return e.handleInput(t),!0}routeConfirm(e,t){const i=e.toLowerCase();return i==="y"?t():(i==="n"||I(e,"escape")||I(e,"return"))&&(this.mode=this.openId!==null?"detail":"list",this.say("Cancelled.")),!0}routeList(e,t){if(t?.("crtr.setup.list-down",e)===!0)return this.move(1),!0;if(t?.("crtr.setup.list-up",e)===!0)return this.move(-1),!0;if(t?.("crtr.setup.advance",e)===!0){const i=this.selected();return i!==null&&(this.openId=i.profileId,this.dirIndex=0,this.mode="detail",this.notice=void 0),!0}switch(e.toLowerCase()){case"a":return this.addCwdTo(this.selected());case"d":return this.togglePin();case"n":return this.mode="new-name",this.edit(this.nameInput,F(this.cwd)||"profile"),!0;case"x":return this.askDelete();default:return!1}}routeDetail(e,t){const i=this.open();if(i===null)return this.mode="list",!0;if(I(e,"escape")||I(e,"left"))return this.mode="list",this.notice=void 0,!0;if(t?.("crtr.setup.list-down",e)===!0)return this.moveDir(1),!0;if(t?.("crtr.setup.list-up",e)===!0)return this.moveDir(-1),!0;switch(e.toLowerCase()){case"a":return this.mode="add-dir",this.edit(this.dirInput,this.cwd),!0;case"l":return this.mode="set-limit",this.edit(this.limitInput,""),!0;case"x":return this.askRemoveDir();case"r":return this.mode="rename",this.edit(this.nameInput,i.manifest.name),!0;case"p":return this.togglePause();default:return!1}}edit(e,t){e.setValue(t),e.handleInput(""),e.focused=!0,this.notice=void 0}leaveField(){this.nameInput.focused=!1,this.dirInput.focused=!1,this.limitInput.focused=!1}move(e){this.entries.length!==0&&(this.listIndex=(this.listIndex+e+this.entries.length)%this.entries.length,this.notice=void 0)}moveDir(e){const t=this.open()?.manifest.projects.length??0;t!==0&&(this.dirIndex=(this.dirIndex+e+t)%t,this.notice=void 0)}addCwdTo(e){return e===null||this.attempt(()=>M(e.profileId,this.cwd),`${c(this.cwd)} now belongs to "${e.manifest.name}".`),!0}togglePin(){const e=this.selected();return e===null||(this.pinnedHere()===e.profileId?(N(this.cwd),this.say(`${c(this.cwd)} no longer defaults to any profile.`)):(V(this.cwd,e.profileId),this.say(`${c(this.cwd)} now starts as "${e.manifest.name}" without asking.`)),this.onChange()),!0}submitName(){const e=this.nameInput.getValue().trim(),t=this.mode==="new-name";if(this.mode=t?"list":"detail",this.leaveField(),e===""){this.say("A profile name cannot be empty.","warning");return}if(t){let n=null;this.attempt(()=>{n=A(e,[{path:this.cwd}]).profileId},`Created "${e}", owning ${c(this.cwd)}.`);const r=this.entries.findIndex(s=>s.profileId===n);r>=0&&(this.listIndex=r);return}const i=this.open();i!==null&&this.rename(i.profileId,e)}async rename(e,t){if(!this.mutating){this.mutating=!0;try{await x().updateProfile(e,{name:t}),this.reload(),this.say(`Renamed to "${t}".`)}catch(i){this.say(b(i),"warning")}finally{this.mutating=!1,this.onChange()}}}submitDir(){const e=this.dirInput.getValue().trim(),t=this.open();if(this.mode="detail",this.leaveField(),t!==null){if(e===""){this.say("Type the directory to add.","warning");return}this.attempt(()=>M(t.profileId,e),`${c(e)} added to "${t.manifest.name}".`)}}submitLimit(){const e=this.limitInput.getValue().trim().split(/\s+/),t=this.open();if(this.mode="detail",this.leaveField(),t===null)return;const[i,n]=e;if(e.length!==2||i===void 0||n===void 0||!_.includes(n)){this.say(`Type an owner handle and a level (${_.join(", ")}), e.g. "crouter preview".`,"warning");return}this.attempt(()=>B(t.profileId,i,n),n==="content"?`${i} is no longer limited.`:`${i} is delivered automatically up to "${n}".`)}askRemoveDir(){return this.currentDir()===null?(this.say("This profile owns no directories.","warning"),!0):(this.mode="confirm-remove-dir",this.notice=void 0,!0)}removeDir(){const e=this.open(),t=this.currentDir();this.mode="detail",!(e===null||t===null)&&this.attempt(()=>W(e.profileId,t.path),`${c(t.path)} removed from "${e.manifest.name}".`)}togglePause(){const e=this.open();return e!==null&&!this.mutating&&this.changePause(e),!0}async changePause(e){this.mutating=!0;const t=e.manifest.paused_at!==null;try{t?await x().resumeProfile(e.profileId):await x().pauseProfile(e.profileId),this.reload(),this.say(t?`"${e.manifest.name}" is active again.`:`"${e.manifest.name}" is paused \u2014 no new nodes start under it.`)}catch(i){this.say(b(i),"warning")}finally{this.mutating=!1,this.onChange()}}askDelete(){const e=this.selected();return e===null?!0:e.profileId===j?(this.say("The root profile is the fallback identity for uncovered directories and cannot be deleted.","warning"),!0):(this.openId=e.profileId,this.mode="confirm-delete",this.notice=void 0,!0)}async deleteOpen(){const e=this.open();if(this.mode="list",!(e===null||this.mutating)){this.mutating=!0,this.say(`Deleting "${e.manifest.name}"\u2026`),this.onChange();try{const t=await x().deleteProfile(e.profileId,{force:!0,caller_node_id:E()??null});this.openId=null,this.reload(),this.say(`Deleted "${t.name}" \u2014 ${t.nodes} nodes, ${t.crons} crons, ${t.default_pins} pins.`)}catch(t){this.say(b(t),"warning")}finally{this.mutating=!1,this.onChange()}}}footerHint(){switch(this.mode){case"new-name":return"type a name \xB7 enter create \xB7 esc cancel";case"rename":return"type a name \xB7 enter save \xB7 esc cancel";case"add-dir":return"type a directory \xB7 enter add \xB7 esc cancel";case"set-limit":return"type <owner> <level> \xB7 enter save \xB7 esc cancel";case"confirm-delete":case"confirm-remove-dir":return"y confirm \xB7 n cancel";case"detail":return"\u2191\u2193 dir \xB7 a add \xB7 l limit \xB7 x remove \xB7 r rename \xB7 p pause \xB7 esc back";case"list":return"\u2191\u2193 move \xB7 enter open \xB7 a add this dir \xB7 d pin here \xB7 n new \xB7 x delete"}}render(e,t){const n=this.mode==="list"||this.mode==="new-name"||this.mode==="confirm-delete"?this.renderList(e,t):this.renderDetail(e,t);if(this.notice!==void 0){n.push("");for(const r of L(this.notice,e))n.push(a(o.fg(this.noticeTone,r),e))}return n}header(e,t,i){const n=o.fg("accent",o.bold(t)),r=o.fg("dim",i);return a(`${n} ${r}`,e)}renderList(e,t){const i=[this.header(e,"Profiles",`${this.entries.length} profile${this.entries.length===1?"":"s"}`),a(o.fg("dim",`Identities your sessions run under. Here: ${c(this.cwd)}`),e),""];if(this.mode==="new-name")return[...i,...this.field(e,"New profile name",this.nameInput)];if(this.mode==="confirm-delete")return[...i,...this.deletePrompt(e)];if(this.entries.length===0)return i.push(a(o.fg("dim"," No profiles yet \u2014 press n to create one here."),e)),i;const n=this.pinnedHere(),r=Math.min(22,Math.max(...this.entries.map(u=>u.manifest.name.length))),s=Math.max(1,t-8),d=k(this.entries.length,this.listIndex,s);for(let u=d.start;u<d.end;u+=1){const h=this.entries[u],y=h.manifest.projects.length,v=y===0?"\u2014":`${y} dir${y===1?"":"s"}`,p=[];T(h,this.cwd)&&p.push("covers here"),n===h.profileId&&p.push("pinned"),h.manifest.paused_at!==null&&p.push("paused");const l=`${f(h.manifest.name,r)} ${f(v,8)} ${f(C(h.manifest.last_used_at),14)} ${p.join(" \xB7 ")}`;u===this.listIndex?i.push(o.bg("selectedBg",f(`\u25B8 ${l}`,e))):i.push(a(` ${f(h.manifest.name,r)} ${o.fg("dim",`${f(v,8)} ${f(C(h.manifest.last_used_at),14)}`)} ${o.fg("muted",p.join(" \xB7 "))}`,e))}return(d.start>0||d.end<this.entries.length)&&i.push(a(o.fg("dim",` ${this.listIndex+1}/${this.entries.length}`),e)),i}renderDetail(e,t){const i=this.open();if(i===null)return this.renderList(e,t);const n=i.manifest,r=[`app ${n.grantee??"app:terminal"}`,`kind ${n.default_kind??"general"}`,C(n.last_used_at),...n.paused_at!==null?["paused"]:[]],s=[this.header(e,n.name,i.profileId),a(o.fg("dim",r.join(" \xB7 ")),e),""];if(this.mode==="rename")return[...s,...this.field(e,"Rename profile",this.nameInput)];if(this.mode==="add-dir")return[...s,...this.field(e,"Directory to add to this profile",this.dirInput)];if(this.mode==="set-limit")return[...s,...this.field(e,"Delivery limit: owner handle and level (content removes it)",this.limitInput)];const d=Object.entries(n.delivery_limits??{});d.length>0&&(s.push(a(o.fg("dim",`limits ${d.map(([l,$])=>`${l} ${$}`).join(" \xB7 ")}`),e)),s.push(""));const u=n.projects;if(u.length===0)return s.push(a(o.fg("dim"," Owns no directories \u2014 press a to add one."),e)),s;const h=new Set(S(i.profileId)),y=Math.min(Math.max(20,e-16),Math.max(...u.map(l=>c(l.path).length))),v=Math.max(1,t-8),p=k(u.length,this.dirIndex,v);for(let l=p.start;l<p.end;l+=1){const $=u[l],w=[];h.has($.path)&&w.push("pinned"),$.path===this.cwd&&w.push("\u2190 here");const P=f(c($.path),y),R=`${P} ${w.join(" \xB7 ")}`;l===this.dirIndex?s.push(o.bg("selectedBg",f(`\u25B8 ${R}`,e))):s.push(a(` ${P} ${o.fg("muted",w.join(" \xB7 "))}`,e))}if(this.mode==="confirm-remove-dir"){const l=this.currentDir();s.push(""),s.push(a(o.fg("warning",`Remove ${l!==null?c(l.path):"this directory"} from "${n.name}"? Its files are untouched. y / n`),e))}return s}field(e,t,i){return[a(o.fg("accent",t),e),a(` ${i.render(Math.max(8,e-2))[0]??""}`,e)]}deletePrompt(e){const i=this.open()?.manifest.name??"",n=[];for(const r of L(`Delete "${i}" and everything it owns \u2014 every node, cron, inbox ticket and review under it, plus its documents (one that another document links to is kept until that link goes) and directory pins? This cannot be undone.`,e))n.push(a(o.fg("warning",r),e));return n.push(""),n.push(a(o.fg("dim"," y delete n cancel"),e)),n}}export{ie as ProfilesPanel};
|
|
1
|
+
var O=Object.defineProperty;var g=(m,e)=>O(m,"name",{value:e,configurable:!0});import{envNodeId as E}from"../../../shared/env.js";import{Input as D,matchesKey as I,truncateToWidth as a}from"@earendil-works/pi-tui";import{basename as F}from"node:path";import{PROFILE_PROJECT_MEMORY_VALUES as _}from"../../../api/dto/profiles.js";import{cliClient as x}from"../../api-client.js";import{clearDefaultProfile as N,defaultProfileDirs as S,getDefaultProfileId as H,setDefaultProfileId as V}from"../../../core/profiles/default-binding.js";import{ROOT_PROFILE_ID as j,addProfileProject as M,createProfile as A,listProfiles as U,removeProfileProject as W,setProfileDeliveryLimit as B}from"../../../core/profiles/manifest.js";import{tildify as c}from"../../../core/fs-utils.js";import{profileCoversCwd as T}from"../../../core/profiles/select.js";import{padAnsi as f,theme as o,visibleRange as k,wrapText as L}from"../../../core/tui/panel.js";function C(m){if(m===null)return"never used";const e=Date.parse(m);if(Number.isNaN(e))return"used recently";const t=Math.max(0,Math.floor((Date.now()-e)/1e3));if(t<60)return"used just now";const i=Math.floor(t/60);if(i<60)return`used ${i}m ago`;const n=Math.floor(i/60);if(n<24)return`used ${n}h ago`;const r=Math.floor(n/24);if(r<30)return`used ${r}d ago`;const s=Math.floor(r/30);return s<12?`used ${s}mo ago`:`used ${Math.floor(s/12)}y ago`}g(C,"relativeUsed");function b(m){return m instanceof Error?m.message:String(m)}g(b,"messageOf");class ie{static{g(this,"ProfilesPanel")}cwd;onChange;nameInput=new D;dirInput=new D;limitInput=new D;entries=[];listIndex=0;dirIndex=0;mode="list";openId=null;notice;noticeTone="success";mutating=!1;constructor(e,t){this.cwd=e,this.onChange=t,this.nameInput.onSubmit=()=>{this.submitName(),this.onChange()},this.nameInput.onEscape=()=>{this.mode=this.openId!==null?"detail":"list",this.leaveField(),this.onChange()},this.dirInput.onSubmit=()=>{this.submitDir(),this.onChange()},this.dirInput.onEscape=()=>{this.mode="detail",this.leaveField(),this.onChange()},this.limitInput.onSubmit=()=>{this.submitLimit(),this.onChange()},this.limitInput.onEscape=()=>{this.mode="detail",this.leaveField(),this.onChange()},this.reload()}badge(){return String(this.entries.length)}ownsInput(e){return this.mode==="list"?!1:this.mode==="detail"?I(e,"escape")||I(e,"left"):!0}reload(){const e=g(n=>n.profileId===j?2:T(n,this.cwd)?0:1,"rank"),t=g(n=>n.manifest.last_used_at??"","used");this.entries=U().sort((n,r)=>{const s=e(n)-e(r);if(s!==0)return s;const h=t(r).localeCompare(t(n));return h!==0?h:n.manifest.name.localeCompare(r.manifest.name)}),this.listIndex>=this.entries.length&&(this.listIndex=Math.max(0,this.entries.length-1));const i=this.open();i===null?(this.mode!=="list"&&(this.mode="list"),this.openId=null):this.dirIndex>=i.manifest.projects.length&&(this.dirIndex=Math.max(0,i.manifest.projects.length-1))}selected(){return this.entries[this.listIndex]??null}open(){return this.openId===null?null:this.entries.find(e=>e.profileId===this.openId)??null}currentDir(){return this.open()?.manifest.projects[this.dirIndex]??null}pinnedHere(){return H(this.cwd)}say(e,t="success"){this.notice=e,this.noticeTone=t}attempt(e,t){this.mutating||(this.mutating=!0,(async()=>{try{await e(),this.reload(),this.say(t)}catch(i){this.say(b(i),"warning")}finally{this.mutating=!1,this.onChange()}})())}handleInput(e,t){const i=this.route(e,t);return i&&this.onChange(),i}route(e,t){switch(this.mode){case"new-name":case"rename":return this.routeText(this.nameInput,e);case"add-dir":return this.routeText(this.dirInput,e);case"set-limit":return this.routeText(this.limitInput,e);case"confirm-delete":return this.routeConfirm(e,()=>{this.deleteOpen()});case"confirm-remove-dir":return this.routeConfirm(e,()=>this.removeDir());case"detail":return this.routeDetail(e,t);case"list":return this.routeList(e,t)}}routeText(e,t){return e.handleInput(t),!0}routeConfirm(e,t){const i=e.toLowerCase();return i==="y"?t():(i==="n"||I(e,"escape")||I(e,"return"))&&(this.mode=this.openId!==null?"detail":"list",this.say("Cancelled.")),!0}routeList(e,t){if(t?.("crtr.setup.list-down",e)===!0)return this.move(1),!0;if(t?.("crtr.setup.list-up",e)===!0)return this.move(-1),!0;if(t?.("crtr.setup.advance",e)===!0){const i=this.selected();return i!==null&&(this.openId=i.profileId,this.dirIndex=0,this.mode="detail",this.notice=void 0),!0}switch(e.toLowerCase()){case"a":return this.addCwdTo(this.selected());case"d":return this.togglePin();case"n":return this.mode="new-name",this.edit(this.nameInput,F(this.cwd)||"profile"),!0;case"x":return this.askDelete();default:return!1}}routeDetail(e,t){const i=this.open();if(i===null)return this.mode="list",!0;if(I(e,"escape")||I(e,"left"))return this.mode="list",this.notice=void 0,!0;if(t?.("crtr.setup.list-down",e)===!0)return this.moveDir(1),!0;if(t?.("crtr.setup.list-up",e)===!0)return this.moveDir(-1),!0;switch(e.toLowerCase()){case"a":return this.mode="add-dir",this.edit(this.dirInput,this.cwd),!0;case"l":return this.mode="set-limit",this.edit(this.limitInput,""),!0;case"x":return this.askRemoveDir();case"r":return this.mode="rename",this.edit(this.nameInput,i.manifest.name),!0;case"p":return this.togglePause();default:return!1}}edit(e,t){e.setValue(t),e.handleInput(""),e.focused=!0,this.notice=void 0}leaveField(){this.nameInput.focused=!1,this.dirInput.focused=!1,this.limitInput.focused=!1}move(e){this.entries.length!==0&&(this.listIndex=(this.listIndex+e+this.entries.length)%this.entries.length,this.notice=void 0)}moveDir(e){const t=this.open()?.manifest.projects.length??0;t!==0&&(this.dirIndex=(this.dirIndex+e+t)%t,this.notice=void 0)}addCwdTo(e){return e===null||this.attempt(()=>M(e.profileId,this.cwd),`${c(this.cwd)} now belongs to "${e.manifest.name}".`),!0}togglePin(){const e=this.selected();return e===null||(this.pinnedHere()===e.profileId?this.attempt(()=>N(this.cwd),`${c(this.cwd)} no longer defaults to any profile.`):this.attempt(()=>V(this.cwd,e.profileId),`${c(this.cwd)} now starts as "${e.manifest.name}" without asking.`)),!0}submitName(){const e=this.nameInput.getValue().trim(),t=this.mode==="new-name";if(this.mode=t?"list":"detail",this.leaveField(),e===""){this.say("A profile name cannot be empty.","warning");return}if(t){this.attempt(async()=>{const n=(await A(e,[{path:this.cwd}])).profileId;this.reload();const r=this.entries.findIndex(s=>s.profileId===n);r>=0&&(this.listIndex=r)},`Created "${e}", owning ${c(this.cwd)}.`);return}const i=this.open();i!==null&&this.rename(i.profileId,e)}async rename(e,t){if(!this.mutating){this.mutating=!0;try{await x().updateProfile(e,{name:t}),this.reload(),this.say(`Renamed to "${t}".`)}catch(i){this.say(b(i),"warning")}finally{this.mutating=!1,this.onChange()}}}submitDir(){const e=this.dirInput.getValue().trim(),t=this.open();if(this.mode="detail",this.leaveField(),t!==null){if(e===""){this.say("Type the directory to add.","warning");return}this.attempt(()=>M(t.profileId,e),`${c(e)} added to "${t.manifest.name}".`)}}submitLimit(){const e=this.limitInput.getValue().trim().split(/\s+/),t=this.open();if(this.mode="detail",this.leaveField(),t===null)return;const[i,n]=e;if(e.length!==2||i===void 0||n===void 0||!_.includes(n)){this.say(`Type an owner handle and a level (${_.join(", ")}), e.g. "crouter preview".`,"warning");return}this.attempt(()=>B(t.profileId,i,n),n==="content"?`${i} is no longer limited.`:`${i} is delivered automatically up to "${n}".`)}askRemoveDir(){return this.currentDir()===null?(this.say("This profile owns no directories.","warning"),!0):(this.mode="confirm-remove-dir",this.notice=void 0,!0)}removeDir(){const e=this.open(),t=this.currentDir();this.mode="detail",!(e===null||t===null)&&this.attempt(()=>W(e.profileId,t.path),`${c(t.path)} removed from "${e.manifest.name}".`)}togglePause(){const e=this.open();return e!==null&&!this.mutating&&this.changePause(e),!0}async changePause(e){this.mutating=!0;const t=e.manifest.paused_at!==null;try{t?await x().resumeProfile(e.profileId):await x().pauseProfile(e.profileId),this.reload(),this.say(t?`"${e.manifest.name}" is active again.`:`"${e.manifest.name}" is paused \u2014 no new nodes start under it.`)}catch(i){this.say(b(i),"warning")}finally{this.mutating=!1,this.onChange()}}askDelete(){const e=this.selected();return e===null?!0:e.profileId===j?(this.say("The root profile is the fallback identity for uncovered directories and cannot be deleted.","warning"),!0):(this.openId=e.profileId,this.mode="confirm-delete",this.notice=void 0,!0)}async deleteOpen(){const e=this.open();if(this.mode="list",!(e===null||this.mutating)){this.mutating=!0,this.say(`Deleting "${e.manifest.name}"\u2026`),this.onChange();try{const t=await x().deleteProfile(e.profileId,{force:!0,caller_node_id:E()??null});this.openId=null,this.reload(),this.say(`Deleted "${t.name}" \u2014 ${t.nodes} nodes, ${t.crons} crons, ${t.default_pins} pins.`)}catch(t){this.say(b(t),"warning")}finally{this.mutating=!1,this.onChange()}}}footerHint(){switch(this.mode){case"new-name":return"type a name \xB7 enter create \xB7 esc cancel";case"rename":return"type a name \xB7 enter save \xB7 esc cancel";case"add-dir":return"type a directory \xB7 enter add \xB7 esc cancel";case"set-limit":return"type <owner> <level> \xB7 enter save \xB7 esc cancel";case"confirm-delete":case"confirm-remove-dir":return"y confirm \xB7 n cancel";case"detail":return"\u2191\u2193 dir \xB7 a add \xB7 l limit \xB7 x remove \xB7 r rename \xB7 p pause \xB7 esc back";case"list":return"\u2191\u2193 move \xB7 enter open \xB7 a add this dir \xB7 d pin here \xB7 n new \xB7 x delete"}}render(e,t){const n=this.mode==="list"||this.mode==="new-name"||this.mode==="confirm-delete"?this.renderList(e,t):this.renderDetail(e,t);if(this.notice!==void 0){n.push("");for(const r of L(this.notice,e))n.push(a(o.fg(this.noticeTone,r),e))}return n}header(e,t,i){const n=o.fg("accent",o.bold(t)),r=o.fg("dim",i);return a(`${n} ${r}`,e)}renderList(e,t){const i=[this.header(e,"Profiles",`${this.entries.length} profile${this.entries.length===1?"":"s"}`),a(o.fg("dim",`Identities your sessions run under. Here: ${c(this.cwd)}`),e),""];if(this.mode==="new-name")return[...i,...this.field(e,"New profile name",this.nameInput)];if(this.mode==="confirm-delete")return[...i,...this.deletePrompt(e)];if(this.entries.length===0)return i.push(a(o.fg("dim"," No profiles yet \u2014 press n to create one here."),e)),i;const n=this.pinnedHere(),r=Math.min(22,Math.max(...this.entries.map(u=>u.manifest.name.length))),s=Math.max(1,t-8),h=k(this.entries.length,this.listIndex,s);for(let u=h.start;u<h.end;u+=1){const d=this.entries[u],y=d.manifest.projects.length,v=y===0?"\u2014":`${y} dir${y===1?"":"s"}`,p=[];T(d,this.cwd)&&p.push("covers here"),n===d.profileId&&p.push("pinned"),d.manifest.paused_at!==null&&p.push("paused");const l=`${f(d.manifest.name,r)} ${f(v,8)} ${f(C(d.manifest.last_used_at),14)} ${p.join(" \xB7 ")}`;u===this.listIndex?i.push(o.bg("selectedBg",f(`\u25B8 ${l}`,e))):i.push(a(` ${f(d.manifest.name,r)} ${o.fg("dim",`${f(v,8)} ${f(C(d.manifest.last_used_at),14)}`)} ${o.fg("muted",p.join(" \xB7 "))}`,e))}return(h.start>0||h.end<this.entries.length)&&i.push(a(o.fg("dim",` ${this.listIndex+1}/${this.entries.length}`),e)),i}renderDetail(e,t){const i=this.open();if(i===null)return this.renderList(e,t);const n=i.manifest,r=[`app ${n.grantee??"app:terminal"}`,`kind ${n.default_kind??"general"}`,C(n.last_used_at),...n.paused_at!==null?["paused"]:[]],s=[this.header(e,n.name,i.profileId),a(o.fg("dim",r.join(" \xB7 ")),e),""];if(this.mode==="rename")return[...s,...this.field(e,"Rename profile",this.nameInput)];if(this.mode==="add-dir")return[...s,...this.field(e,"Directory to add to this profile",this.dirInput)];if(this.mode==="set-limit")return[...s,...this.field(e,"Delivery limit: owner handle and level (content removes it)",this.limitInput)];const h=Object.entries(n.delivery_limits??{});h.length>0&&(s.push(a(o.fg("dim",`limits ${h.map(([l,$])=>`${l} ${$}`).join(" \xB7 ")}`),e)),s.push(""));const u=n.projects;if(u.length===0)return s.push(a(o.fg("dim"," Owns no directories \u2014 press a to add one."),e)),s;const d=new Set(S(i.profileId)),y=Math.min(Math.max(20,e-16),Math.max(...u.map(l=>c(l.path).length))),v=Math.max(1,t-8),p=k(u.length,this.dirIndex,v);for(let l=p.start;l<p.end;l+=1){const $=u[l],w=[];d.has($.path)&&w.push("pinned"),$.path===this.cwd&&w.push("\u2190 here");const P=f(c($.path),y),R=`${P} ${w.join(" \xB7 ")}`;l===this.dirIndex?s.push(o.bg("selectedBg",f(`\u25B8 ${R}`,e))):s.push(a(` ${P} ${o.fg("muted",w.join(" \xB7 "))}`,e))}if(this.mode==="confirm-remove-dir"){const l=this.currentDir();s.push(""),s.push(a(o.fg("warning",`Remove ${l!==null?c(l.path):"this directory"} from "${n.name}"? Its files are untouched. y / n`),e))}return s}field(e,t,i){return[a(o.fg("accent",t),e),a(` ${i.render(Math.max(8,e-2))[0]??""}`,e)]}deletePrompt(e){const i=this.open()?.manifest.name??"",n=[];for(const r of L(`Delete "${i}" and everything it owns \u2014 every node, cron, inbox ticket and review under it, plus its documents (one that another document links to is kept until that link goes) and directory pins? This cannot be undone.`,e))n.push(a(o.fg("warning",r),e));return n.push(""),n.push(a(o.fg("dim"," y delete n cancel"),e)),n}}export{ie as ProfilesPanel};
|
|
@@ -37,6 +37,8 @@ export declare class ProviderPanel implements SetupPanel {
|
|
|
37
37
|
private loginProvider;
|
|
38
38
|
private loginFlow;
|
|
39
39
|
private mutationInFlight;
|
|
40
|
+
/** Managed-provider pools as last read from the credential rows; reload() renders from this. */
|
|
41
|
+
private pools;
|
|
40
42
|
onChange?: () => void;
|
|
41
43
|
constructor(tui: TUI, agentDir: string, runtime: ModelRuntime, registry: ModelRegistry);
|
|
42
44
|
private notifyChange;
|
|
@@ -46,6 +48,8 @@ export declare class ProviderPanel implements SetupPanel {
|
|
|
46
48
|
* `refresh()` is local-only here (the runtime is composed with model network access
|
|
47
49
|
* off), so this is a credential re-list, not a catalog fetch. */
|
|
48
50
|
private reloadAfterAuthChange;
|
|
51
|
+
/** Re-reads every managed provider's account rows, then rebuilds the roster. */
|
|
52
|
+
private reloadPools;
|
|
49
53
|
private reload;
|
|
50
54
|
private selectedRow;
|
|
51
55
|
private openBrowser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var g=(u,e)=>R(u,"name",{value:e,configurable:!0});import{LoginDialogComponent as E}from"@earendil-works/pi-coding-agent";import{Input as B,matchesKey as d,truncateToWidth as D}from"@earendil-works/pi-tui";import{execFile as N}from"node:child_process";import{dialogAuthInteraction as T}from"../../../core/auth-interaction.js";import{isProviderCredentialConfigured as F}from"../../../core/provider-credential.js";import{formatCooldowns as _,isManagedProvider as L,promoteSubscription as U,readSubscriptionPool as S,renameSubscription as V}from"../../../core/subscription-state.js";import{removeProviderRow as j}from"../../../core/provider-management.js";import{cliClient as q}from"../../api-client.js";import{loginProvider as K}from"../provider-login.js";import{fitTail as P,theme as a,visibleRange as k}from"../../../core/tui/panel.js";function M(u,e){const t=u.getProviders().filter(i=>i.auth.oauth!==void 0),n=new Set(t.map(i=>i.id)),o=new Set;return[...t.map(i=>({providerId:i.id,name:i.name,managed:L(i.id),authType:"oauth"})),...e.getAll().map(i=>({providerId:i.provider,name:e.getProviderDisplayName(i.provider),managed:L(i.provider),authType:n.has(i.provider)?"oauth":"api_key"}))].filter(i=>!o.has(i.providerId)&&!!o.add(i.providerId))}g(M,"buildProviderCatalog");function O(u,e){return e||u instanceof Error&&u.name==="AbortError"?"Login cancelled":`Login error: ${u instanceof Error?u.message:String(u)}`}g(O,"classifyLoginFailure");class ie{static{g(this,"ProviderPanel")}tui;agentDir;runtime;registry;renameInput=new B;rows=[];selected=0;mode="list";notice="";catalog=[];catalogIndex=0;loginProvider;loginFlow;mutationInFlight=!1;onChange;constructor(e,t,n,o){this.tui=e,this.agentDir=t,this.runtime=n,this.registry=o,this.renameInput.onSubmit=()=>this.rename(),this.renameInput.onEscape=()=>{this.mode="list"},this.reload()}notifyChange(){this.onChange?.(),this.tui.requestRender()}reloadAfterAuthChange(){this.reload(),this.registry.refresh().then(()=>{this.reload(),this.notifyChange()})}reload(){const e=this.registry,t=[];for(const n of M(this.runtime,e)){const o=e.getProviderAuthStatus(n.providerId);if(n.managed){const i=S(n.providerId);if(i.length===0&&!o.source)continue;t.push({...n,configured:F(n.providerId,e),source:o.source,sourceLabel:o.label}),i.forEach((r,c)=>t.push({...n,label:r.label,preferred:c===0,cooldownUntil:r.rateLimitedUntil,...r.modelCooldowns?{modelCooldowns:r.modelCooldowns}:{},accountId:r.accountId}));continue}o.source&&t.push({...n,configured:F(n.providerId,e),source:o.source,sourceLabel:o.label})}this.rows=t,this.selected=Math.min(this.selected,Math.max(0,t.length))}selectedRow(){return this.rows[this.selected]}openBrowser(e){const t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",n=process.platform==="win32"?["/c","start","",e]:[e];N(t,n,{timeout:5e3},()=>{})}openCatalog(){this.catalog=M(this.runtime,this.registry),this.catalogIndex=0,this.mode="catalog"}startLogin(e,t){this.loginProvider=e;let n;const o=g(s=>{!n.active||this.loginFlow!==n||(n.active=!1,this.loginFlow=void 0,this.mode="list",this.notice=s,this.reloadAfterAuthChange(),this.notifyChange())},"complete"),i=new E(this.tui,e.providerId,(s,p)=>{s||o(p?`Login failed: ${p}`:"Login cancelled")},e.name);n={dialog:i,active:!0,committing:!1},this.loginFlow=n,this.mode="login";const r=i.handleInput.bind(i);i.handleInput=s=>{n.committing||r(s)};const c=g(()=>n.active&&this.loginFlow===n&&!i.signal.aborted,"isActive"),h=g(()=>c()?(n.committing=!0,i.showDetails(["Saving credentials\u2026"]),!0):!1,"beginCommit"),m=g(s=>{o(O(s,i.signal.aborted))},"fail");if(e.authType==="api_key"){i.showPrompt(`Enter API key for ${e.name}:`,"sk-...").then(async s=>{if(c()){if(!s.trim()){o("No key entered \u2014 login cancelled");return}h()&&(await q().installCredential(e.providerId,{kind:"api_key",api_key:s.trim()}),o(`API key saved for ${e.name}`))}}).catch(m);return}const $=T(i,{openBrowser:g(s=>this.openBrowser(s),"openBrowser"),onChange:g(()=>this.notifyChange(),"onChange"),isActive:c});K(this.runtime,e.providerId,$,{label:t,beforeInstall:h}).then(s=>{s&&c()&&o(`Logged in to ${e.name}`)}).catch(m)}isAddRowSelected(){return this.mode==="list"&&this.selectedRow()===void 0}isNestedFlowActive(){return this.mode==="login"}dispose(){const e=this.loginFlow;e&&(e.active=!1,this.loginFlow=void 0,e.dialog.handleInput("\x1B"))}runMutation(e){this.mutationInFlight||(this.mutationInFlight=!0,e().catch(t=>{this.notice=t instanceof Error?t.message:String(t)}).finally(()=>{this.mutationInFlight=!1,this.notifyChange()}))}suggestedAccountLabel(e){const t=new Set((L(e)?S(e):[]).map(o=>o.label.toLowerCase()));if(!t.has("default"))return"default";let n=2;for(;t.has(`account-${n}`);)n+=1;return`account-${n}`}rename(){const e=this.selectedRow();!e?.managed||!e.label||(this.mode="list",this.runMutation(async()=>{await V(e.providerId,e.label,this.renameInput.getValue()),this.notice=`Renamed ${e.name} account`,this.reload()}))}remove(e){this.runMutation(async()=>{this.notice=await j(this.agentDir,e),this.reloadAfterAuthChange()})}promote(e){this.runMutation(async()=>{await U(e.providerId,e.label),this.notice=`Preferred ${e.name} account`,this.reload()})}handleInput(e){if(this.mode==="login")return this.loginFlow?.dialog.handleInput(e),!0;if(this.mutationInFlight)return!0;if(this.mode==="rename"||this.mode==="label")return this.renameInput.handleInput(e),!0;if(this.mode==="catalog"){if(d(e,"escape"))return this.mode="list",!0;if(d(e,"j")||d(e,"down"))return this.catalogIndex=Math.min(this.catalog.length-1,this.catalogIndex+1),!0;if(d(e,"k")||d(e,"up"))return this.catalogIndex=Math.max(0,this.catalogIndex-1),!0;if(d(e,"enter")){const o=this.catalog[this.catalogIndex];if(o.managed){this.loginProvider=o;const i=this.suggestedAccountLabel(o.providerId);this.renameInput.setValue(i),this.renameInput.onSubmit=()=>{this.startLogin(o,this.renameInput.getValue().trim()||i)},this.mode="label"}else this.startLogin(o);return!0}return!0}if(this.mode==="remove"){if(d(e,"y")||d(e,"enter")){const o=this.selectedRow();o&&(o.managed||o.configured)&&this.remove(o)}return this.mode="list",!0}if(d(e,"j")||d(e,"down"))return this.selected=Math.min(this.rows.length,this.selected+1),!0;if(d(e,"k")||d(e,"up"))return this.selected=Math.max(0,this.selected-1),!0;const t=this.selectedRow();if(d(e,"p")&&t?.managed&&t.label)return this.promote(t),!0;if(d(e,"r")&&t?.managed&&t.label)return this.renameInput.setValue(t.label),this.renameInput.onSubmit=()=>this.rename(),this.mode="rename",!0;if(d(e,"x")&&(t?.managed||t?.configured))return this.mode="remove",!0;if(d(e,"a"))return this.openCatalog(),!0;const n=t?.source==="environment"||t?.source==="runtime";return d(e,"l")&&t&&!n?(t.managed&&t.label?this.startLogin(t,t.label):this.startLogin(t),!0):d(e,"enter")&&!t?(this.openCatalog(),!0):!1}badge(){return String(this.rows.filter(e=>e.label===void 0).length)}render(e,t){const n=a.fg("accent",a.bold("Providers")),o=a.fg("dim",`${this.rows.filter(c=>c.label===void 0).length} configured`),i=Math.max(1,t);let r;if(this.mode==="login")r=P(this.loginFlow?.dialog.render(e)??[],i);else if(this.mode==="rename"||this.mode==="label")r=P([n,o,"",a.fg("accent",this.mode==="rename"?"Rename account":`Name the ${this.loginProvider?.name??"provider"} account`),...this.mode==="label"?[a.fg("dim","Names this credential \u2014 keep work and personal accounts on one provider apart")]:[],...this.renameInput.render(Math.max(1,e-8))],i);else if(this.mode==="remove"){const c=this.selectedRow(),h=c?.label??(c?.managed?`${c.name} \xB7 all accounts`:c?.name??"account");r=P([n,o,"",a.fg("warning",`Remove ${h}? y/enter confirm \xB7 any other key cancel`)],i)}else{const c=[n,o].slice(0,Math.max(0,i-1)),h=i-c.length;if(r=[...c],this.mode==="catalog"){const m=this.catalog.length,s=m>h&&h>=2,p=s?h-1:h,{start:I,end:v}=k(m,this.catalogIndex,p);let w=h-(v-I)-(s?1:0);w>0&&(r.push(a.fg("dim","Select a provider to add \xB7 esc back")),w-=1);for(let f=I;f<v;f++){const C=this.catalog[f];r.push(`${f===this.catalogIndex?a.fg("accent","\u25B6"):" "} ${C.name}`)}s&&r.push(a.fg("dim",`(${this.catalogIndex+1}/${m})`))}else{const m=this.rows.length+1,s=m>h&&h>=2,p=s?h-1:h,{start:I,end:v}=k(m,this.selected,p);let w=h-(v-I)-(s?1:0);const f=this.rows.length===0&&w>0;f&&(w-=1);const C=!!this.notice&&w>0;f&&r.push(a.fg("warning","No provider configured yet."));for(let b=I;b<v;b++){if(b===this.rows.length){r.push(`${this.selected===this.rows.length?a.fg("accent","\u25B6"):" "} ${a.fg("accent","\uFF0B Add provider")}`);continue}const l=this.rows[b],A=b===this.selected?a.fg("accent","\u25B6"):" ";if(l.label){const x=_({rateLimitedUntil:l.cooldownUntil??0,modelCooldowns:l.modelCooldowns})??"ready",y=l.accountId?` \xB7 ${l.accountId.length>12?`${l.accountId.slice(0,8)}\u2026${l.accountId.slice(-4)}`:l.accountId}`:"";r.push(`${A} ${l.preferred?a.fg("success","\u25CF"):" "} ${l.label}${a.fg("dim",y)} ${a.fg("dim",x)}${l.preferred?a.fg("accent"," preferred"):""}`)}else{const x=l.managed?`${this.rows.filter(y=>y.providerId===l.providerId&&y.label).length} accounts`:l.source==="environment"||l.source==="runtime"?`${l.source}${l.sourceLabel?` \xB7 ${l.sourceLabel}`:""} (view only)`:"configured";r.push(`${A} ${a.fg("accent",l.name)}${a.fg("dim",` ${x}`)}`)}}s&&r.push(a.fg("dim",`(${this.selected+1}/${m})`)),C&&r.push(a.fg("dim",this.notice))}}return r.map(c=>D(c,e))}}export{ie as ProviderPanel,M as buildProviderCatalog,O as classifyLoginFailure};
|
|
1
|
+
var _=Object.defineProperty;var m=(l,e)=>_(l,"name",{value:e,configurable:!0});import{LoginDialogComponent as R}from"@earendil-works/pi-coding-agent";import{Input as S,matchesKey as h,truncateToWidth as E}from"@earendil-works/pi-tui";import{execFile as D}from"node:child_process";import{dialogAuthInteraction as B}from"../../../core/auth-interaction.js";import{formatCooldowns as N,isManagedProvider as M}from"../../../core/managed-providers.js";import{removeProviderRow as T}from"../../../core/provider-management.js";import{cliClient as y}from"../../api-client.js";import{loginProvider as U}from"../provider-login.js";import{fitTail as L,theme as a,visibleRange as F}from"../../../core/tui/panel.js";function j(l){const e=Object.fromEntries((l.model_cooldowns??[]).map(t=>[t.family,Date.parse(t.rate_limited_until)]));return{label:l.label,refresh:"",access:"",expires:Date.parse(l.expires_at),rateLimitedUntil:Date.parse(l.rate_limited_until),lastAttemptAt:0,lastRateLimitedAt:0,...l.auth_failure?{authFailure:l.auth_failure}:{},...l.account_id?{accountId:l.account_id}:{},...Object.keys(e).length?{modelCooldowns:e}:{}}}m(j,"accountOf");function k(l,e){const t=l.getProviders().filter(i=>i.auth.oauth!==void 0),o=new Set(t.map(i=>i.id)),n=new Set;return[...t.map(i=>({providerId:i.id,name:i.name,managed:M(i.id),authType:"oauth"})),...e.getAll().map(i=>({providerId:i.provider,name:e.getProviderDisplayName(i.provider),managed:M(i.provider),authType:o.has(i.provider)?"oauth":"api_key"}))].filter(i=>!n.has(i.providerId)&&!!n.add(i.providerId))}m(k,"buildProviderCatalog");function O(l,e){return e||l instanceof Error&&l.name==="AbortError"?"Login cancelled":`Login error: ${l instanceof Error?l.message:String(l)}`}m(O,"classifyLoginFailure");class Y{static{m(this,"ProviderPanel")}tui;agentDir;runtime;registry;renameInput=new S;rows=[];selected=0;mode="list";notice="";catalog=[];catalogIndex=0;loginProvider;loginFlow;mutationInFlight=!1;pools=new Map;onChange;constructor(e,t,o,n){this.tui=e,this.agentDir=t,this.runtime=o,this.registry=n,this.renameInput.onSubmit=()=>this.rename(),this.renameInput.onEscape=()=>{this.mode="list"},this.reloadPools().then(()=>this.notifyChange()).catch(i=>{this.notice=i instanceof Error?i.message:String(i),this.notifyChange()})}notifyChange(){this.onChange?.(),this.tui.requestRender()}async reloadAfterAuthChange(){await this.reloadPools(),await this.registry.refresh(),this.reload(),this.notifyChange()}async reloadPools(){const e=new Map;for(const t of(await y().listModelAuth()).providers)t.managed&&e.set(t.provider,t.accounts.map(j));this.pools=e,this.reload()}reload(){const e=this.registry,t=[];for(const o of k(this.runtime,e)){const n=e.getProviderAuthStatus(o.providerId);if(o.managed){const i=this.pools.get(o.providerId)??[];if(i.length===0&&!n.source)continue;t.push({...o,configured:n.configured||i.length>0,source:n.source,sourceLabel:n.label}),i.forEach((s,d)=>t.push({...o,label:s.label,preferred:d===0,cooldownUntil:s.rateLimitedUntil,...s.modelCooldowns?{modelCooldowns:s.modelCooldowns}:{},accountId:s.accountId}));continue}n.source&&t.push({...o,configured:n.configured,source:n.source,sourceLabel:n.label})}this.rows=t,this.selected=Math.min(this.selected,Math.max(0,t.length))}selectedRow(){return this.rows[this.selected]}openBrowser(e){const t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",o=process.platform==="win32"?["/c","start","",e]:[e];D(t,o,{timeout:5e3},()=>{})}openCatalog(){this.catalog=k(this.runtime,this.registry),this.catalogIndex=0,this.mode="catalog"}startLogin(e,t){this.loginProvider=e;let o;const n=m(r=>{!o.active||this.loginFlow!==o||(o.active=!1,this.loginFlow=void 0,this.mode="list",this.notice=r,this.reloadAfterAuthChange())},"complete"),i=new R(this.tui,e.providerId,(r,p)=>{r||n(p?`Login failed: ${p}`:"Login cancelled")},e.name);o={dialog:i,active:!0,committing:!1},this.loginFlow=o,this.mode="login";const s=i.handleInput.bind(i);i.handleInput=r=>{o.committing||s(r)};const d=m(()=>o.active&&this.loginFlow===o&&!i.signal.aborted,"isActive"),u=m(()=>d()?(o.committing=!0,i.showDetails(["Saving credentials\u2026"]),!0):!1,"beginCommit"),g=m(r=>{n(O(r,i.signal.aborted))},"fail");if(e.authType==="api_key"){i.showPrompt(`Enter API key for ${e.name}:`,"sk-...").then(async r=>{if(d()){if(!r.trim()){n("No key entered \u2014 login cancelled");return}u()&&(await y().installCredential(e.providerId,{kind:"api_key",api_key:r.trim()}),n(`API key saved for ${e.name}`))}}).catch(g);return}const x=B(i,{openBrowser:m(r=>this.openBrowser(r),"openBrowser"),onChange:m(()=>this.notifyChange(),"onChange"),isActive:d});U(this.runtime,e.providerId,x,{label:t,beforeInstall:u}).then(r=>{r&&d()&&n(`Logged in to ${e.name}`)}).catch(g)}isAddRowSelected(){return this.mode==="list"&&this.selectedRow()===void 0}isNestedFlowActive(){return this.mode==="login"}dispose(){const e=this.loginFlow;e&&(e.active=!1,this.loginFlow=void 0,e.dialog.handleInput("\x1B"))}runMutation(e){this.mutationInFlight||(this.mutationInFlight=!0,e().catch(t=>{this.notice=t instanceof Error?t.message:String(t)}).finally(()=>{this.mutationInFlight=!1,this.notifyChange()}))}suggestedAccountLabel(e){const t=new Set((this.pools.get(e)??[]).map(n=>n.label.toLowerCase()));if(!t.has("default"))return"default";let o=2;for(;t.has(`account-${o}`);)o+=1;return`account-${o}`}rename(){const e=this.selectedRow();!e?.managed||!e.label||(this.mode="list",this.runMutation(async()=>{await y().patchModelAuthAccount(e.providerId,e.label,{label:this.renameInput.getValue()}),this.notice=`Renamed ${e.name} account`,await this.reloadPools()}))}remove(e){this.runMutation(async()=>{this.notice=await T(y(),e),await this.reloadAfterAuthChange()})}promote(e){this.runMutation(async()=>{await y().patchModelAuthAccount(e.providerId,e.label,{preferred:!0}),this.notice=`Preferred ${e.name} account`,await this.reloadPools()})}handleInput(e){if(this.mode==="login")return this.loginFlow?.dialog.handleInput(e),!0;if(this.mutationInFlight)return!0;if(this.mode==="rename"||this.mode==="label")return this.renameInput.handleInput(e),!0;if(this.mode==="catalog"){if(h(e,"escape"))return this.mode="list",!0;if(h(e,"j")||h(e,"down"))return this.catalogIndex=Math.min(this.catalog.length-1,this.catalogIndex+1),!0;if(h(e,"k")||h(e,"up"))return this.catalogIndex=Math.max(0,this.catalogIndex-1),!0;if(h(e,"enter")){const n=this.catalog[this.catalogIndex];if(n.managed){this.loginProvider=n;const i=this.suggestedAccountLabel(n.providerId);this.renameInput.setValue(i),this.renameInput.onSubmit=()=>{this.startLogin(n,this.renameInput.getValue().trim()||i)},this.mode="label"}else this.startLogin(n);return!0}return!0}if(this.mode==="remove"){if(h(e,"y")||h(e,"enter")){const n=this.selectedRow();n&&(n.managed||n.configured)&&this.remove(n)}return this.mode="list",!0}if(h(e,"j")||h(e,"down"))return this.selected=Math.min(this.rows.length,this.selected+1),!0;if(h(e,"k")||h(e,"up"))return this.selected=Math.max(0,this.selected-1),!0;const t=this.selectedRow();if(h(e,"p")&&t?.managed&&t.label)return this.promote(t),!0;if(h(e,"r")&&t?.managed&&t.label)return this.renameInput.setValue(t.label),this.renameInput.onSubmit=()=>this.rename(),this.mode="rename",!0;if(h(e,"x")&&(t?.managed||t?.configured))return this.mode="remove",!0;if(h(e,"a"))return this.openCatalog(),!0;const o=t?.source==="environment"||t?.source==="runtime";return h(e,"l")&&t&&!o?(t.managed&&t.label?this.startLogin(t,t.label):this.startLogin(t),!0):h(e,"enter")&&!t?(this.openCatalog(),!0):!1}badge(){return String(this.rows.filter(e=>e.label===void 0).length)}render(e,t){const o=a.fg("accent",a.bold("Providers")),n=a.fg("dim",`${this.rows.filter(d=>d.label===void 0).length} configured`),i=Math.max(1,t);let s;if(this.mode==="login")s=L(this.loginFlow?.dialog.render(e)??[],i);else if(this.mode==="rename"||this.mode==="label")s=L([o,n,"",a.fg("accent",this.mode==="rename"?"Rename account":`Name the ${this.loginProvider?.name??"provider"} account`),...this.mode==="label"?[a.fg("dim","Names this credential \u2014 keep work and personal accounts on one provider apart")]:[],...this.renameInput.render(Math.max(1,e-8))],i);else if(this.mode==="remove"){const d=this.selectedRow(),u=d?.label??(d?.managed?`${d.name} \xB7 all accounts`:d?.name??"account");s=L([o,n,"",a.fg("warning",`Remove ${u}? y/enter confirm \xB7 any other key cancel`)],i)}else{const d=[o,n].slice(0,Math.max(0,i-1)),u=i-d.length;if(s=[...d],this.mode==="catalog"){const g=this.catalog.length,r=g>u&&u>=2,p=r?u-1:u,{start:I,end:v}=F(g,this.catalogIndex,p);let w=u-(v-I)-(r?1:0);w>0&&(s.push(a.fg("dim","Select a provider to add \xB7 esc back")),w-=1);for(let f=I;f<v;f++){const C=this.catalog[f];s.push(`${f===this.catalogIndex?a.fg("accent","\u25B6"):" "} ${C.name}`)}r&&s.push(a.fg("dim",`(${this.catalogIndex+1}/${g})`))}else{const g=this.rows.length+1,r=g>u&&u>=2,p=r?u-1:u,{start:I,end:v}=F(g,this.selected,p);let w=u-(v-I)-(r?1:0);const f=this.rows.length===0&&w>0;f&&(w-=1);const C=!!this.notice&&w>0;f&&s.push(a.fg("warning","No provider configured yet."));for(let b=I;b<v;b++){if(b===this.rows.length){s.push(`${this.selected===this.rows.length?a.fg("accent","\u25B6"):" "} ${a.fg("accent","\uFF0B Add provider")}`);continue}const c=this.rows[b],P=b===this.selected?a.fg("accent","\u25B6"):" ";if(c.label){const A=N({rateLimitedUntil:c.cooldownUntil??0,modelCooldowns:c.modelCooldowns})??"ready",$=c.accountId?` \xB7 ${c.accountId.length>12?`${c.accountId.slice(0,8)}\u2026${c.accountId.slice(-4)}`:c.accountId}`:"";s.push(`${P} ${c.preferred?a.fg("success","\u25CF"):" "} ${c.label}${a.fg("dim",$)} ${a.fg("dim",A)}${c.preferred?a.fg("accent"," preferred"):""}`)}else{const A=c.managed?`${this.rows.filter($=>$.providerId===c.providerId&&$.label).length} accounts`:c.source==="environment"||c.source==="runtime"?`${c.source}${c.sourceLabel?` \xB7 ${c.sourceLabel}`:""} (view only)`:"configured";s.push(`${P} ${a.fg("accent",c.name)}${a.fg("dim",` ${A}`)}`)}}r&&s.push(a.fg("dim",`(${this.selected+1}/${g})`)),C&&s.push(a.fg("dim",this.notice))}}return s.map(d=>E(d,e))}}export{Y as ProviderPanel,k as buildProviderCatalog,O as classifyLoginFailure};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g=Object.defineProperty;var o=(e,r)=>g(e,"name",{value:r,configurable:!0});import{execFile as h}from"node:child_process";import{createInterface as w}from"node:readline/promises";import{join as y}from"node:path";import{ModelRuntime as $}from"@earendil-works/pi-coding-agent";import{cliClient as s}from"../api-client.js";import{InputError as l}from"../../core/io.js";import{ApiCredentialStore as b}from"../../core/model-auth/api-credential-store.js";import{isManagedProvider as _}from"../../core/managed-providers.js";import{daemonManagedLogin as x}from"../../core/model-auth/daemon-login.js";import{piAgentDir as k}from"../../core/runtime/package-health.js";async function E(){const e=k();return $.create({credentials:new b(()=>s()),modelsPath:y(e,"models.json")})}o(E,"openProviderLoginRuntime");async function U(e,r,n,t={}){if(_(r))return await x(s(),r,n,t.label?{label:t.label}:{}),!0;const a=e.getProvider(r)?.auth.oauth;if(a===void 0)throw new l({error:"provider_login_unavailable",message:`provider ${r} has no OAuth login flow`,next:"Configure an API key through `crtr sys setup`, then run `crtr sys connect` again."});const f=await a.login(n);if(t.beforeInstall?.()===!1)return!1;const{type:A,access:d,refresh:p,expires:m,...c}=f;return await s().installCredential(r,{kind:"oauth",access_token:d,refresh_token:p,expires_at:new Date(m).toISOString(),...Object.keys(c).length>0?{extra:c}:{}}),!0}o(U,"loginProvider");function u(e){const r=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",n=process.platform==="win32"?["/c","start","",e]:[e];h(r,n,{timeout:5e3},()=>{})}o(u,"openBrowser");function C(e){switch(e.type){case"auth_url":process.stderr.write(`${e.instructions}
|
|
2
2
|
${e.url}
|
|
3
|
-
`),
|
|
3
|
+
`),u(e.url);break;case"device_code":process.stderr.write(`${e.verificationUri}
|
|
4
4
|
Code: ${e.userCode}
|
|
5
|
-
`),
|
|
6
|
-
`);break}}o(
|
|
5
|
+
`),u(e.verificationUri);break;case"info":case"progress":process.stderr.write(`${e.message}
|
|
6
|
+
`);break}}o(C,"notify");async function v(e){if(!process.stdin.isTTY||!process.stderr.isTTY)throw new l({error:"interactive_login_required",message:"provider login requires an interactive terminal",next:"Run `crtr sys connect` from an interactive terminal, or configure the provider credential through `crtr sys setup` first."});const r=e.type==="select"?`${e.message}
|
|
7
7
|
${e.options.map((t,i)=>`${i+1}. ${t.label}`).join(`
|
|
8
8
|
`)}
|
|
9
|
-
Select: `:`${e.message}${e.placeholder?` (${e.placeholder})`:""}: `,n=
|
|
9
|
+
Select: `:`${e.message}${e.placeholder?` (${e.placeholder})`:""}: `,n=w({input:process.stdin,output:process.stderr});try{const t=await n.question(r,{signal:e.signal});if(e.type!=="select")return t;const i=e.options[Number.parseInt(t,10)-1]?.id;if(i===void 0)throw new Error("Login cancelled");return i}finally{n.close()}}o(v,"promptTerminal");function Y(){return{signal:new AbortController().signal,notify:C,prompt:v}}o(Y,"terminalAuthInteraction");export{U as loginProvider,E as openProviderLoginRuntime,Y as terminalAuthInteraction};
|