@pugi/cli 0.1.0-beta.99 → 1.0.0-alpha.2
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/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -195
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
|
@@ -1,595 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, resolve } from 'node:path';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
import { loadMcpRegistry } from '../../core/mcp/registry.js';
|
|
6
|
-
import { listMcpTrust, setMcpTrust, } from '../../core/mcp/trust.js';
|
|
7
|
-
import { request } from 'undici';
|
|
8
|
-
import { trustWorkspace } from '../../core/trust.js';
|
|
9
|
-
import { resolveActiveCredential } from '../../core/credentials.js';
|
|
10
|
-
/**
|
|
11
|
-
* `pugi config` — operator-level configuration surface.
|
|
12
|
-
*
|
|
13
|
-
* Subcommands:
|
|
14
|
-
* - `pugi config get <key>` read a value from `~/.pugi/config.json`
|
|
15
|
-
* - `pugi config set <key> <value>` write a value
|
|
16
|
-
* - `pugi config list` dump all values
|
|
17
|
-
* - `pugi config trust .` trust the current workspace (delegates to core/trust.ts)
|
|
18
|
-
* - `pugi config mcp trust <name>` flip MCP server to trusted
|
|
19
|
-
* - `pugi config mcp deny <name>` flip MCP server to denied
|
|
20
|
-
* - `pugi config mcp list` show declared servers + their trust state
|
|
21
|
-
*
|
|
22
|
-
* Schema (pugi-config-v1):
|
|
23
|
-
* {
|
|
24
|
-
* "permissionMode": "ask" | "acceptEdits" | "auto" | "plan" | "dontAsk" | "bypassPermissions",
|
|
25
|
-
* "privacy": "local-only" | "metadata" | "full",
|
|
26
|
-
* "model": "<id>" | null,
|
|
27
|
-
* "preferredEndpoint": "https://api.pugi.io"
|
|
28
|
-
* }
|
|
29
|
-
*
|
|
30
|
-
* The config file lives at `~/.pugi/config.json` (PUGI_HOME-aware) and uses
|
|
31
|
-
* mode 0o600. Unknown keys are rejected by `set` so a typo never silently
|
|
32
|
-
* persists.
|
|
33
|
-
*/
|
|
34
|
-
const configSchema = z
|
|
35
|
-
.object({
|
|
36
|
-
permissionMode: z
|
|
37
|
-
.enum(['plan', 'ask', 'acceptEdits', 'auto', 'dontAsk', 'bypassPermissions'])
|
|
38
|
-
.optional(),
|
|
39
|
-
privacy: z.enum(['local-only', 'metadata', 'full']).optional(),
|
|
40
|
-
model: z.string().nullable().optional(),
|
|
41
|
-
preferredEndpoint: z.string().url().optional(),
|
|
42
|
-
// PUGI-260 — persistent default for the 1M context tier opt-in.
|
|
43
|
-
// `pugi config set contextTier 1m` (or the dotted form
|
|
44
|
-
// `context.tier 1m`) writes this; per-invocation `--context-tier=...`
|
|
45
|
-
// flags override it at request time. Closed enum mirrors the CLI
|
|
46
|
-
// flag и the admin-api DTO so a typo here surfaces as a Zod parse
|
|
47
|
-
// error при load, not a silent fallback. Stored on the flat user-
|
|
48
|
-
// level config (~/.pugi/config.json) so all workspaces inherit the
|
|
49
|
-
// same default — operators с consistent long-context workloads
|
|
50
|
-
// (large monorepos, audits) set it once instead of remembering к
|
|
51
|
-
// pass --context-tier=1m on every dispatch.
|
|
52
|
-
contextTier: z.enum(['1m', 'standard']).optional(),
|
|
53
|
-
})
|
|
54
|
-
.strict();
|
|
55
|
-
const CONFIG_KEYS = [
|
|
56
|
-
'permissionMode',
|
|
57
|
-
'privacy',
|
|
58
|
-
'model',
|
|
59
|
-
'preferredEndpoint',
|
|
60
|
-
// PUGI-260 — exposed на `pugi config list` so operators see the
|
|
61
|
-
// current default. Hidden synonym `context.tier` accepted by
|
|
62
|
-
// runConfigSet / runConfigGet for a dotted-key familiar UX.
|
|
63
|
-
'contextTier',
|
|
64
|
-
];
|
|
65
|
-
/**
|
|
66
|
-
* PUGI-260: legacy / nested key aliasing. `pugi config set context.tier 1m`
|
|
67
|
-
* is the documented form в the feat doc; we normalise it onto the flat
|
|
68
|
-
* `contextTier` key before the strict-schema validation так future
|
|
69
|
-
* settings.json migrations keep one canonical key. Mirrors the
|
|
70
|
-
* legacy privacy-mode aliasing that already lives in the file.
|
|
71
|
-
*/
|
|
72
|
-
function normaliseConfigKey(raw) {
|
|
73
|
-
if (raw === 'context.tier')
|
|
74
|
-
return 'contextTier';
|
|
75
|
-
return raw;
|
|
76
|
-
}
|
|
77
|
-
export async function runConfigCommand(args, ctx) {
|
|
78
|
-
const sub = args[0];
|
|
79
|
-
if (!sub || sub === '--help' || sub === '-h') {
|
|
80
|
-
ctx.writeOutput({
|
|
81
|
-
command: 'config',
|
|
82
|
-
usage: [
|
|
83
|
-
'pugi config get <key>',
|
|
84
|
-
'pugi config set <key> <value>',
|
|
85
|
-
'pugi config list',
|
|
86
|
-
'pugi config trust .',
|
|
87
|
-
'pugi config mcp trust <name>',
|
|
88
|
-
'pugi config mcp deny <name>',
|
|
89
|
-
'pugi config mcp list',
|
|
90
|
-
'pugi config get routing',
|
|
91
|
-
'pugi config set routing.<tag>.<budget>=<model>',
|
|
92
|
-
'pugi config unset routing.<tag>.<budget>',
|
|
93
|
-
'pugi config get privacy',
|
|
94
|
-
'pugi config set privacy=strict|balanced|permissive',
|
|
95
|
-
],
|
|
96
|
-
}, [
|
|
97
|
-
'Usage:',
|
|
98
|
-
' pugi config get <key> Read a config value.',
|
|
99
|
-
' pugi config set <key> <value> Write a config value.',
|
|
100
|
-
' pugi config list Show all config values.',
|
|
101
|
-
' pugi config trust . Trust the current workspace for hooks + MCP.',
|
|
102
|
-
' pugi config mcp trust <name> Mark an MCP server as trusted.',
|
|
103
|
-
' pugi config mcp deny <name> Block an MCP server.',
|
|
104
|
-
' pugi config mcp list Show declared MCP servers + trust state.',
|
|
105
|
-
' pugi config get routing Show effective routing table (defaults + tenant overrides).',
|
|
106
|
-
' pugi config set routing.<tag>.<budget>=<model> Override the model for one (tag, budget) lane.',
|
|
107
|
-
' pugi config unset routing.<tag>.<budget> Remove a routing override (revert to default).',
|
|
108
|
-
' pugi config get privacy Show current tenant privacy mode + last-flip metadata.',
|
|
109
|
-
' pugi config set privacy=<mode> Flip privacy mode (strict | balanced | permissive).',
|
|
110
|
-
].join('\n'));
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
switch (sub) {
|
|
114
|
-
case 'get':
|
|
115
|
-
// Special form: `pugi config get routing` hits the admin-api surface,
|
|
116
|
-
// not the local config file. Anything else is a local-config read.
|
|
117
|
-
if (args[1] === 'routing') {
|
|
118
|
-
return runRoutingGet(ctx);
|
|
119
|
-
}
|
|
120
|
-
// alpha 6.13: `pugi config get privacy` hits the admin-api
|
|
121
|
-
// /api/admin/privacy/mode surface. The privacy mode is a tenant-
|
|
122
|
-
// scoped server-side setting (so the Anvil filter can enforce it),
|
|
123
|
-
// not a local-only preference.
|
|
124
|
-
if (args[1] === 'privacy') {
|
|
125
|
-
return runPrivacyGet(ctx);
|
|
126
|
-
}
|
|
127
|
-
return runConfigGet(args.slice(1), ctx);
|
|
128
|
-
case 'set':
|
|
129
|
-
// Special form: `pugi config set routing.<tag>.<budget>=<model>` hits
|
|
130
|
-
// the admin-api routing override surface.
|
|
131
|
-
if (args[1] && args[1].startsWith('routing.')) {
|
|
132
|
-
return runRoutingSet(args.slice(1), ctx);
|
|
133
|
-
}
|
|
134
|
-
// alpha 6.13: `pugi config set privacy=<mode>` (or `privacy <mode>`)
|
|
135
|
-
// flips the tenant-scoped server-side mode IFF <mode> is one of
|
|
136
|
-
// the new closed set (strict | balanced | permissive). Legacy
|
|
137
|
-
// values (local-only | metadata | full) still hit the local
|
|
138
|
-
// config schema via runConfigSet so existing operators do not
|
|
139
|
-
// get a surprise 4xx when their playbook still uses the old
|
|
140
|
-
// names. The unit spec for config.ts has a regression test for
|
|
141
|
-
// both code paths.
|
|
142
|
-
//
|
|
143
|
-
// Triple-review P2 fix : the prior disambiguation
|
|
144
|
-
// only excluded the bare form (`privacy local-only`) - the `=`
|
|
145
|
-
// form (`privacy=local-only`) still routed to runPrivacySet and
|
|
146
|
-
// 4xx'd. We now check the value AFTER `=` and route legacy local
|
|
147
|
-
// values to runConfigSet; new-style values + unknown values
|
|
148
|
-
// continue to runPrivacySet so its client-side validator surfaces
|
|
149
|
-
// a structured "Unknown privacy mode" error (preserves the
|
|
150
|
-
// existing UX for typos like `privacy=paranoid`).
|
|
151
|
-
if (args[1]) {
|
|
152
|
-
const equalsForm = args[1].startsWith('privacy=');
|
|
153
|
-
const bareForm = args[1] === 'privacy';
|
|
154
|
-
if (equalsForm) {
|
|
155
|
-
const valueAfterEquals = args[1].slice('privacy='.length);
|
|
156
|
-
if (isLegacyLocalPrivacyValue(valueAfterEquals)) {
|
|
157
|
-
// Legacy local form (`privacy=local-only|metadata|full`) -
|
|
158
|
-
// split into `['privacy', '<value>']` so runConfigSet sees
|
|
159
|
-
// the same shape it would for the bare form.
|
|
160
|
-
return runConfigSet(['privacy', valueAfterEquals], ctx);
|
|
161
|
-
}
|
|
162
|
-
return runPrivacySet(args.slice(1), ctx);
|
|
163
|
-
}
|
|
164
|
-
if (bareForm && isNewPrivacyModeValue(args[2])) {
|
|
165
|
-
return runPrivacySet(args.slice(1), ctx);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return runConfigSet(args.slice(1), ctx);
|
|
169
|
-
case 'unset':
|
|
170
|
-
if (args[1] && args[1].startsWith('routing.')) {
|
|
171
|
-
return runRoutingUnset(args.slice(1), ctx);
|
|
172
|
-
}
|
|
173
|
-
throw new Error(`Unknown sub-command "pugi config unset ${args[1] ?? ''}". Only routing.<tag>.<budget> is supported today.`);
|
|
174
|
-
case 'list':
|
|
175
|
-
return runConfigList(ctx);
|
|
176
|
-
case 'trust':
|
|
177
|
-
return runConfigTrust(args.slice(1), ctx);
|
|
178
|
-
case 'mcp':
|
|
179
|
-
return runConfigMcp(args.slice(1), ctx);
|
|
180
|
-
default:
|
|
181
|
-
throw new Error(`Unknown sub-command "pugi config ${sub}". Expected get, set, unset, list, trust, or mcp.`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function configPath() {
|
|
185
|
-
const home = process.env.PUGI_HOME ?? resolve(homedir(), '.pugi');
|
|
186
|
-
return resolve(home, 'config.json');
|
|
187
|
-
}
|
|
188
|
-
export function readConfig() {
|
|
189
|
-
const path = configPath();
|
|
190
|
-
if (!existsSync(path))
|
|
191
|
-
return {};
|
|
192
|
-
const raw = readFileSync(path, 'utf8');
|
|
193
|
-
if (raw.trim() === '')
|
|
194
|
-
return {};
|
|
195
|
-
const parsed = JSON.parse(raw);
|
|
196
|
-
return configSchema.parse(parsed);
|
|
197
|
-
}
|
|
198
|
-
function writeConfig(config) {
|
|
199
|
-
const path = configPath();
|
|
200
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
201
|
-
// 0o600 — `preferredEndpoint` could be a private self-hosted URL; the
|
|
202
|
-
// config does not contain secrets, but file mode parity with the trust
|
|
203
|
-
// ledger keeps the audit surface uniform.
|
|
204
|
-
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, {
|
|
205
|
-
encoding: 'utf8',
|
|
206
|
-
mode: 0o600,
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
function isConfigKey(value) {
|
|
210
|
-
return CONFIG_KEYS.includes(value);
|
|
211
|
-
}
|
|
212
|
-
function runConfigGet(args, ctx) {
|
|
213
|
-
const rawKey = args[0];
|
|
214
|
-
if (!rawKey)
|
|
215
|
-
throw new Error('pugi config get requires a key.');
|
|
216
|
-
const key = normaliseConfigKey(rawKey);
|
|
217
|
-
if (!isConfigKey(key)) {
|
|
218
|
-
throw new Error(`Unknown config key "${rawKey}". Allowed: ${CONFIG_KEYS.join(', ')}.`);
|
|
219
|
-
}
|
|
220
|
-
const config = readConfig();
|
|
221
|
-
const value = config[key] ?? null;
|
|
222
|
-
ctx.writeOutput({ command: 'config.get', key, value }, value === null || value === undefined ? `${key} = (unset)` : `${key} = ${String(value)}`);
|
|
223
|
-
}
|
|
224
|
-
function runConfigSet(args, ctx) {
|
|
225
|
-
const rawKey = args[0];
|
|
226
|
-
const value = args.slice(1).join(' ');
|
|
227
|
-
if (!rawKey)
|
|
228
|
-
throw new Error('pugi config set requires a key.');
|
|
229
|
-
if (value.length === 0)
|
|
230
|
-
throw new Error('pugi config set requires a value.');
|
|
231
|
-
const key = normaliseConfigKey(rawKey);
|
|
232
|
-
if (!isConfigKey(key)) {
|
|
233
|
-
throw new Error(`Unknown config key "${rawKey}". Allowed: ${CONFIG_KEYS.join(', ')}.`);
|
|
234
|
-
}
|
|
235
|
-
const current = readConfig();
|
|
236
|
-
// Build the candidate and validate via the schema so an invalid value
|
|
237
|
-
// (e.g. `permissionMode = nonsense`) is rejected before persistence.
|
|
238
|
-
const candidate = { ...current };
|
|
239
|
-
candidate[key] = coerceValue(key, value);
|
|
240
|
-
const validated = configSchema.parse(candidate);
|
|
241
|
-
writeConfig(validated);
|
|
242
|
-
ctx.writeOutput({
|
|
243
|
-
command: 'config.set',
|
|
244
|
-
key,
|
|
245
|
-
value: validated[key] ?? null,
|
|
246
|
-
}, `${key} = ${String(validated[key] ?? '')}`);
|
|
247
|
-
}
|
|
248
|
-
function coerceValue(key, raw) {
|
|
249
|
-
if (key === 'model') {
|
|
250
|
-
return raw === 'null' || raw === '' ? null : raw;
|
|
251
|
-
}
|
|
252
|
-
return raw;
|
|
253
|
-
}
|
|
254
|
-
function runConfigList(ctx) {
|
|
255
|
-
const config = readConfig();
|
|
256
|
-
const entries = CONFIG_KEYS.map((key) => ({
|
|
257
|
-
key,
|
|
258
|
-
value: config[key] ?? null,
|
|
259
|
-
}));
|
|
260
|
-
ctx.writeOutput({ command: 'config.list', config, entries }, entries
|
|
261
|
-
.map((entry) => entry.value === null || entry.value === undefined
|
|
262
|
-
? `${entry.key} = (unset)`
|
|
263
|
-
: `${entry.key} = ${String(entry.value)}`)
|
|
264
|
-
.join('\n'));
|
|
265
|
-
}
|
|
266
|
-
async function runConfigTrust(args, ctx) {
|
|
267
|
-
const target = args[0];
|
|
268
|
-
if (!target)
|
|
269
|
-
throw new Error('pugi config trust requires a path (use "." for cwd).');
|
|
270
|
-
const root = target === '.' ? ctx.workspaceRoot : resolve(ctx.workspaceRoot, target);
|
|
271
|
-
// Identity for the audit entry: prefer the explicit env override
|
|
272
|
-
// (`PUGI_TRUSTED_BY`), then `USER` from the shell, then literal
|
|
273
|
-
// 'cli'. The trust ledger requires a non-empty string and we want
|
|
274
|
-
// it to mean something for a future audit replay.
|
|
275
|
-
const by = process.env.PUGI_TRUSTED_BY?.trim() ||
|
|
276
|
-
process.env.USER?.trim() ||
|
|
277
|
-
process.env.USERNAME?.trim() ||
|
|
278
|
-
'cli';
|
|
279
|
-
await trustWorkspace(root, by);
|
|
280
|
-
ctx.writeOutput({ command: 'config.trust', workspaceRoot: root, trustedBy: by }, `Trusted workspace: ${root}`);
|
|
281
|
-
}
|
|
282
|
-
async function runConfigMcp(args, ctx) {
|
|
283
|
-
const sub = args[0];
|
|
284
|
-
if (!sub) {
|
|
285
|
-
throw new Error('pugi config mcp requires a sub-command: trust, deny, or list.');
|
|
286
|
-
}
|
|
287
|
-
switch (sub) {
|
|
288
|
-
case 'list':
|
|
289
|
-
return runConfigMcpList(ctx);
|
|
290
|
-
case 'trust':
|
|
291
|
-
return runConfigMcpFlip(args.slice(1), ctx, 'trusted');
|
|
292
|
-
case 'deny':
|
|
293
|
-
return runConfigMcpFlip(args.slice(1), ctx, 'denied');
|
|
294
|
-
default:
|
|
295
|
-
throw new Error(`Unknown sub-command "pugi config mcp ${sub}". Expected trust, deny, or list.`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
async function runConfigMcpList(ctx) {
|
|
299
|
-
const registry = await loadMcpRegistry(ctx.workspaceRoot, { connect: false });
|
|
300
|
-
const declared = Array.from(registry.servers.values()).map((state) => ({
|
|
301
|
-
name: state.name,
|
|
302
|
-
command: state.config.command,
|
|
303
|
-
args: state.config.args,
|
|
304
|
-
trust: state.trust,
|
|
305
|
-
surfacedTools: state.surfacedTools.length,
|
|
306
|
-
lastError: state.lastError ?? null,
|
|
307
|
-
}));
|
|
308
|
-
const ledger = await listMcpTrust();
|
|
309
|
-
await registry.shutdown();
|
|
310
|
-
if (declared.length === 0) {
|
|
311
|
-
ctx.writeOutput({ command: 'config.mcp.list', servers: [], ledger }, 'No MCP servers declared. Add one to .pugi/mcp.json or ~/.pugi/mcp.json.');
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
ctx.writeOutput({ command: 'config.mcp.list', servers: declared, ledger }, [
|
|
315
|
-
'MCP servers:',
|
|
316
|
-
...declared.map((server) => ` ${server.name.padEnd(20)} ${server.trust.padEnd(8)} ${server.command} ${server.args.join(' ')}`),
|
|
317
|
-
].join('\n'));
|
|
318
|
-
}
|
|
319
|
-
async function runConfigMcpFlip(args, ctx, state) {
|
|
320
|
-
const name = args[0];
|
|
321
|
-
if (!name) {
|
|
322
|
-
throw new Error(`pugi config mcp ${state === 'trusted' ? 'trust' : 'deny'} requires a server name.`);
|
|
323
|
-
}
|
|
324
|
-
const by = process.env.PUGI_TRUSTED_BY?.trim() ||
|
|
325
|
-
process.env.USER?.trim() ||
|
|
326
|
-
process.env.USERNAME?.trim() ||
|
|
327
|
-
'cli';
|
|
328
|
-
await setMcpTrust(name, state, by);
|
|
329
|
-
ctx.writeOutput({ command: `config.mcp.${state === 'trusted' ? 'trust' : 'deny'}`, name, state, decidedBy: by }, state === 'trusted'
|
|
330
|
-
? `MCP server "${name}" is now trusted.`
|
|
331
|
-
: `MCP server "${name}" is now denied.`);
|
|
332
|
-
}
|
|
333
|
-
/* ------------------------------------------------------------------ */
|
|
334
|
-
/* multi-model routing — config.routing.* subcommands */
|
|
335
|
-
/* ------------------------------------------------------------------ */
|
|
336
|
-
/**
|
|
337
|
-
* Closed sets — match
|
|
338
|
-
* `apps/admin-api/src/pugi/routing/dispatch-tag.ts` verbatim. Pinning
|
|
339
|
-
* them in the CLI lets us reject typos client-side before round-tripping
|
|
340
|
-
* to the admin-api (better UX, smaller blast radius for a wrong typo on
|
|
341
|
-
* a flaky network).
|
|
342
|
-
*/
|
|
343
|
-
const ROUTING_TAGS = [
|
|
344
|
-
'classify',
|
|
345
|
-
'reason',
|
|
346
|
-
'codegen',
|
|
347
|
-
'summarize',
|
|
348
|
-
'vision',
|
|
349
|
-
'embed',
|
|
350
|
-
];
|
|
351
|
-
const ROUTING_BUDGETS = ['min', 'std', 'max'];
|
|
352
|
-
function isRoutingTag(value) {
|
|
353
|
-
return ROUTING_TAGS.includes(value);
|
|
354
|
-
}
|
|
355
|
-
function isRoutingBudget(value) {
|
|
356
|
-
return ROUTING_BUDGETS.includes(value);
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Resolve the admin-api host + bearer token from the CLI credential
|
|
360
|
-
* store. Throws a structured "anonymous" error when the operator has
|
|
361
|
-
* not logged in — same shape as `pugi whoami` so the harness exit
|
|
362
|
-
* codes stay aligned.
|
|
363
|
-
*/
|
|
364
|
-
function resolveAdminApi() {
|
|
365
|
-
const credential = resolveActiveCredential();
|
|
366
|
-
if (!credential) {
|
|
367
|
-
throw new Error('pugi config routing requires authentication. Run `pugi login` first.');
|
|
368
|
-
}
|
|
369
|
-
return { apiUrl: credential.apiUrl, apiKey: credential.apiKey };
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* `pugi config get routing` — fetch the static default table + the
|
|
373
|
-
* tenant's override table, merge, and render as `routing.<tag>.<budget> = <model>`.
|
|
374
|
-
* Shows which lanes are overridden vs default.
|
|
375
|
-
*/
|
|
376
|
-
async function runRoutingGet(ctx) {
|
|
377
|
-
const { apiUrl, apiKey } = resolveAdminApi();
|
|
378
|
-
const [defaults, overrides] = await Promise.all([
|
|
379
|
-
fetchJson(`${apiUrl}/api/admin/model-routing/defaults`, apiKey),
|
|
380
|
-
fetchJson(`${apiUrl}/api/admin/model-routing/overrides`, apiKey),
|
|
381
|
-
]);
|
|
382
|
-
const overrideMap = new Map();
|
|
383
|
-
for (const row of overrides.overrides) {
|
|
384
|
-
overrideMap.set(`${row.tag}.${row.budgetHint}`, row.model);
|
|
385
|
-
}
|
|
386
|
-
const cells = defaults.defaults.map((cell) => {
|
|
387
|
-
const overridden = overrideMap.get(`${cell.tag}.${cell.budgetHint}`);
|
|
388
|
-
return {
|
|
389
|
-
tag: cell.tag,
|
|
390
|
-
budgetHint: cell.budgetHint,
|
|
391
|
-
model: overridden ?? cell.model,
|
|
392
|
-
source: overridden ? 'override' : 'default',
|
|
393
|
-
};
|
|
394
|
-
});
|
|
395
|
-
const text = [
|
|
396
|
-
'Routing table (effective = override | default):',
|
|
397
|
-
...cells.map((cell) => ` routing.${cell.tag.padEnd(10)}.${cell.budgetHint.padEnd(3)} = ${cell.model.padEnd(28)} (${cell.source})`),
|
|
398
|
-
].join('\n');
|
|
399
|
-
ctx.writeOutput({
|
|
400
|
-
command: 'config.routing.get',
|
|
401
|
-
apiUrl,
|
|
402
|
-
cells,
|
|
403
|
-
}, text);
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* `pugi config set routing.<tag>.<budget>=<model>` — PUT to the admin-api
|
|
407
|
-
* override surface. Validates tag + budget client-side before round-tripping
|
|
408
|
-
* so a typo fails fast.
|
|
409
|
-
*/
|
|
410
|
-
async function runRoutingSet(args, ctx) {
|
|
411
|
-
// The original arg is `routing.<tag>.<budget>=<model>` — args[0] holds
|
|
412
|
-
// everything up to the first whitespace, but the value may have been
|
|
413
|
-
// split. Re-join and re-split on `=` to be robust against a
|
|
414
|
-
// model slug containing `/` or `-`.
|
|
415
|
-
const raw = args.join(' ').trim();
|
|
416
|
-
const eqIdx = raw.indexOf('=');
|
|
417
|
-
if (eqIdx === -1) {
|
|
418
|
-
throw new Error('pugi config set routing.<tag>.<budget>=<model> requires an =<model> suffix.');
|
|
419
|
-
}
|
|
420
|
-
const lhs = raw.slice(0, eqIdx).trim();
|
|
421
|
-
const value = raw.slice(eqIdx + 1).trim();
|
|
422
|
-
const lhsParts = lhs.split('.');
|
|
423
|
-
if (lhsParts.length !== 3 || lhsParts[0] !== 'routing') {
|
|
424
|
-
throw new Error(`Expected routing.<tag>.<budget>, got "${lhs}".`);
|
|
425
|
-
}
|
|
426
|
-
const tag = lhsParts[1] ?? '';
|
|
427
|
-
const budget = lhsParts[2] ?? '';
|
|
428
|
-
if (!isRoutingTag(tag)) {
|
|
429
|
-
throw new Error(`Unknown routing tag "${tag}". Allowed: ${ROUTING_TAGS.join(', ')}.`);
|
|
430
|
-
}
|
|
431
|
-
if (!isRoutingBudget(budget)) {
|
|
432
|
-
throw new Error(`Unknown routing budget "${budget}". Allowed: ${ROUTING_BUDGETS.join(', ')}.`);
|
|
433
|
-
}
|
|
434
|
-
if (value.length === 0) {
|
|
435
|
-
throw new Error('Model slug must be non-empty.');
|
|
436
|
-
}
|
|
437
|
-
const { apiUrl, apiKey } = resolveAdminApi();
|
|
438
|
-
await fetchJson(`${apiUrl}/api/admin/model-routing/overrides/${tag}/${budget}`, apiKey, { method: 'PUT', body: { model: value } });
|
|
439
|
-
ctx.writeOutput({
|
|
440
|
-
command: 'config.routing.set',
|
|
441
|
-
tag,
|
|
442
|
-
budget,
|
|
443
|
-
model: value,
|
|
444
|
-
}, `routing.${tag}.${budget} = ${value}`);
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* `pugi config unset routing.<tag>.<budget>` — DELETE the override and
|
|
448
|
-
* revert the lane to its static default. Idempotent.
|
|
449
|
-
*/
|
|
450
|
-
async function runRoutingUnset(args, ctx) {
|
|
451
|
-
const lhs = (args[0] ?? '').trim();
|
|
452
|
-
const lhsParts = lhs.split('.');
|
|
453
|
-
if (lhsParts.length !== 3 || lhsParts[0] !== 'routing') {
|
|
454
|
-
throw new Error(`Expected routing.<tag>.<budget>, got "${lhs}".`);
|
|
455
|
-
}
|
|
456
|
-
const tag = lhsParts[1] ?? '';
|
|
457
|
-
const budget = lhsParts[2] ?? '';
|
|
458
|
-
if (!isRoutingTag(tag)) {
|
|
459
|
-
throw new Error(`Unknown routing tag "${tag}". Allowed: ${ROUTING_TAGS.join(', ')}.`);
|
|
460
|
-
}
|
|
461
|
-
if (!isRoutingBudget(budget)) {
|
|
462
|
-
throw new Error(`Unknown routing budget "${budget}". Allowed: ${ROUTING_BUDGETS.join(', ')}.`);
|
|
463
|
-
}
|
|
464
|
-
const { apiUrl, apiKey } = resolveAdminApi();
|
|
465
|
-
const result = await fetchJson(`${apiUrl}/api/admin/model-routing/overrides/${tag}/${budget}`, apiKey, { method: 'DELETE' });
|
|
466
|
-
ctx.writeOutput({
|
|
467
|
-
command: 'config.routing.unset',
|
|
468
|
-
tag,
|
|
469
|
-
budget,
|
|
470
|
-
removed: result.removed,
|
|
471
|
-
}, result.removed
|
|
472
|
-
? `routing.${tag}.${budget} reverted to default.`
|
|
473
|
-
: `routing.${tag}.${budget} had no override (nothing to remove).`);
|
|
474
|
-
}
|
|
475
|
-
/* ------------------------------------------------------------------ */
|
|
476
|
-
/* alpha 6.13 privacy 3-mode - config.privacy.* subcommands */
|
|
477
|
-
/* ------------------------------------------------------------------ */
|
|
478
|
-
/**
|
|
479
|
-
* Closed mirror of the server-side PRIVACY_MODES enum
|
|
480
|
-
* (apps/admin-api/src/privacy/privacy-mode.ts). Pinning the literal
|
|
481
|
-
* set here lets the CLI reject typos client-side before the round-
|
|
482
|
-
* trip to admin-api (better UX, smaller blast radius on a flaky
|
|
483
|
-
* network).
|
|
484
|
-
*/
|
|
485
|
-
const PRIVACY_MODES = ['strict', 'balanced', 'permissive'];
|
|
486
|
-
function isNewPrivacyModeValue(value) {
|
|
487
|
-
return typeof value === 'string' && PRIVACY_MODES.includes(value);
|
|
488
|
-
}
|
|
489
|
-
function isPrivacyMode(value) {
|
|
490
|
-
return PRIVACY_MODES.includes(value);
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Legacy local-config privacy values from before alpha 6.13. Kept so
|
|
494
|
-
* `pugi config set privacy=local-only` continues to write to the local
|
|
495
|
-
* config file (matching the bare-form behaviour). Triple-review P2 fix
|
|
496
|
-
* : the prior disambiguation only excluded the bare form;
|
|
497
|
-
* the `=` form routed to runPrivacySet and 4xx'd on the unknown mode.
|
|
498
|
-
*/
|
|
499
|
-
const LEGACY_LOCAL_PRIVACY_VALUES = [
|
|
500
|
-
'local-only',
|
|
501
|
-
'metadata',
|
|
502
|
-
'full',
|
|
503
|
-
];
|
|
504
|
-
function isLegacyLocalPrivacyValue(value) {
|
|
505
|
-
return (typeof value === 'string' &&
|
|
506
|
-
LEGACY_LOCAL_PRIVACY_VALUES.includes(value));
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* `pugi config get privacy` - fetch the current privacy mode snapshot
|
|
510
|
-
* from /api/admin/privacy/mode + render it in human-readable form.
|
|
511
|
-
*/
|
|
512
|
-
async function runPrivacyGet(ctx) {
|
|
513
|
-
const { apiUrl, apiKey } = resolveAdminApi();
|
|
514
|
-
const snapshot = await fetchJson(`${apiUrl}/api/admin/privacy/mode`, apiKey);
|
|
515
|
-
const lastUpdatedLine = snapshot.lastUpdated
|
|
516
|
-
? `(last set by ${snapshot.lastUpdatedBy ?? 'unknown'} on ${snapshot.lastUpdated})`
|
|
517
|
-
: '(no flips recorded; on implicit default)';
|
|
518
|
-
const text = [
|
|
519
|
-
`privacy.mode = ${snapshot.mode}`,
|
|
520
|
-
`privacy.defaultMode = ${snapshot.defaultMode}`,
|
|
521
|
-
lastUpdatedLine,
|
|
522
|
-
].join('\n');
|
|
523
|
-
ctx.writeOutput({
|
|
524
|
-
command: 'config.privacy.get',
|
|
525
|
-
apiUrl,
|
|
526
|
-
snapshot,
|
|
527
|
-
}, text);
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* `pugi config set privacy=<mode>` - PUT to /api/admin/privacy/mode.
|
|
531
|
-
* Validates the mode client-side against the closed set before
|
|
532
|
-
* round-tripping.
|
|
533
|
-
*
|
|
534
|
-
* Accepts both `privacy <mode>` and `privacy=<mode>` argument forms so
|
|
535
|
-
* the operator can type it either way.
|
|
536
|
-
*/
|
|
537
|
-
async function runPrivacySet(args, ctx) {
|
|
538
|
-
const raw = args.join(' ').trim();
|
|
539
|
-
let mode;
|
|
540
|
-
if (raw.startsWith('privacy=')) {
|
|
541
|
-
mode = raw.slice('privacy='.length).trim();
|
|
542
|
-
}
|
|
543
|
-
else if (raw === 'privacy') {
|
|
544
|
-
throw new Error('pugi config set privacy requires a mode. Try: pugi config set privacy=balanced');
|
|
545
|
-
}
|
|
546
|
-
else if (raw.startsWith('privacy ')) {
|
|
547
|
-
mode = raw.slice('privacy '.length).trim();
|
|
548
|
-
}
|
|
549
|
-
else {
|
|
550
|
-
throw new Error(`pugi config set privacy: unrecognised argument "${raw}". Try: pugi config set privacy=balanced`);
|
|
551
|
-
}
|
|
552
|
-
if (mode.length === 0) {
|
|
553
|
-
throw new Error('pugi config set privacy requires a mode. Try: pugi config set privacy=balanced');
|
|
554
|
-
}
|
|
555
|
-
if (!isPrivacyMode(mode)) {
|
|
556
|
-
throw new Error(`Unknown privacy mode "${mode}". Allowed: ${PRIVACY_MODES.join(', ')}.`);
|
|
557
|
-
}
|
|
558
|
-
const { apiUrl, apiKey } = resolveAdminApi();
|
|
559
|
-
const snapshot = await fetchJson(`${apiUrl}/api/admin/privacy/mode`, apiKey, { method: 'PUT', body: { mode } });
|
|
560
|
-
ctx.writeOutput({
|
|
561
|
-
command: 'config.privacy.set',
|
|
562
|
-
mode: snapshot.mode,
|
|
563
|
-
snapshot,
|
|
564
|
-
}, `privacy.mode = ${snapshot.mode}`);
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Thin authenticated fetch helper. Adds the bearer token + accepts JSON +
|
|
568
|
-
* surfaces structured errors. Uses undici `request` (not native `fetch`)
|
|
569
|
-
* because the test suite intercepts via `MockAgent` + `setGlobalDispatcher`
|
|
570
|
-
* — undici's `request` honours the global dispatcher reliably across the
|
|
571
|
-
* pinned undici version. Kept local (not shared with `pugi whoami`) so
|
|
572
|
-
* the routing surface is self-contained — extracting a common helper is
|
|
573
|
-
* cleanup once we see two callers.
|
|
574
|
-
*/
|
|
575
|
-
async function fetchJson(url, apiKey, options = {}) {
|
|
576
|
-
const method = options.method ?? 'GET';
|
|
577
|
-
const headers = {
|
|
578
|
-
authorization: `Bearer ${apiKey}`,
|
|
579
|
-
accept: 'application/json',
|
|
580
|
-
};
|
|
581
|
-
let body;
|
|
582
|
-
if (options.body !== undefined) {
|
|
583
|
-
body = JSON.stringify(options.body);
|
|
584
|
-
headers['content-type'] = 'application/json';
|
|
585
|
-
}
|
|
586
|
-
const res = await request(url, { method, headers, body });
|
|
587
|
-
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
588
|
-
const detail = await res.body.text().catch(() => '');
|
|
589
|
-
throw new Error(`pugi config routing: HTTP ${res.statusCode} on ${method} ${url}${detail ? ` -- ${detail.slice(0, 200)}` : ''}`);
|
|
590
|
-
}
|
|
591
|
-
// undici's `request` already returns `body` as a stream wrapped with
|
|
592
|
-
// helpers — `.json()` parses + closes for us.
|
|
593
|
-
return (await res.body.json());
|
|
594
|
-
}
|
|
595
|
-
//# sourceMappingURL=config.js.map
|