create-walle 0.10.2 → 0.10.3
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/README.md +3 -1
- package/lib/release-schema.json +2 -2
- package/lib/runtime-closure-policy.json +8 -7
- package/package.json +1 -1
- package/template/coding-task-manager/api-prompts.js +45 -7
- package/template/coding-task-manager/bin/claude-hook.js +55 -1
- package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
- package/template/coding-task-manager/bin/land.js +2069 -0
- package/template/coding-task-manager/bin/memory-census.js +4 -2
- package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
- package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
- package/template/coding-task-manager/db.js +156 -32
- package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
- package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
- package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
- package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
- package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
- package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
- package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
- package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
- package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
- package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
- package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
- package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
- package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
- package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
- package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
- package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
- package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
- package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
- package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
- package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
- package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
- package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
- package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
- package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
- package/template/coding-task-manager/lib/agent-definitions.js +19 -0
- package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
- package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
- package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
- package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
- package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
- package/template/coding-task-manager/lib/agent-presets.js +84 -1
- package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
- package/template/coding-task-manager/lib/agent-version-service.js +74 -11
- package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
- package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
- package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
- package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
- package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
- package/template/coding-task-manager/lib/api/fleet.js +118 -0
- package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
- package/template/coding-task-manager/lib/api/ingress.js +9 -1
- package/template/coding-task-manager/lib/api/landing.js +403 -0
- package/template/coding-task-manager/lib/api/machines.js +691 -2
- package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
- package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
- package/template/coding-task-manager/lib/api/session-attach.js +623 -0
- package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
- package/template/coding-task-manager/lib/api/sessions.js +77 -51
- package/template/coding-task-manager/lib/api/setup-network.js +544 -16
- package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
- package/template/coding-task-manager/lib/api/setup.js +829 -10
- package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
- package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
- package/template/coding-task-manager/lib/auth-rules.js +292 -21
- package/template/coding-task-manager/lib/background-stop.js +369 -0
- package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
- package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
- package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
- package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
- package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
- package/template/coding-task-manager/lib/build-info.js +4 -2
- package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
- package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
- package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
- package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
- package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
- package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
- package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
- package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
- package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
- package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
- package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
- package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
- package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
- package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
- package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
- package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
- package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
- package/template/coding-task-manager/lib/conversation-sync.js +355 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
- package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
- package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
- package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
- package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
- package/template/coding-task-manager/lib/db-domains.js +1 -0
- package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
- package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
- package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
- package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
- package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
- package/template/coding-task-manager/lib/feature-manifest.js +4 -0
- package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
- package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
- package/template/coding-task-manager/lib/fleet-hub.js +762 -0
- package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
- package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
- package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
- package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
- package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
- package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
- package/template/coding-task-manager/lib/fleet-update.js +798 -0
- package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
- package/template/coding-task-manager/lib/fs-watcher.js +58 -42
- package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
- package/template/coding-task-manager/lib/headless-term-service.js +84 -8
- package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
- package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
- package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
- package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
- package/template/coding-task-manager/lib/host-handoff.js +16 -19
- package/template/coding-task-manager/lib/host-stats.js +150 -3
- package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
- package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
- package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
- package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
- package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
- package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
- package/template/coding-task-manager/lib/hub-bridge.js +398 -0
- package/template/coding-task-manager/lib/idle-notify.js +491 -0
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
- package/template/coding-task-manager/lib/instance-manifest.js +5 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
- package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
- package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
- package/template/coding-task-manager/lib/landing/config.js +536 -0
- package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
- package/template/coding-task-manager/lib/landing/gate.js +360 -0
- package/template/coding-task-manager/lib/landing/git.js +344 -0
- package/template/coding-task-manager/lib/landing/lease.js +247 -0
- package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
- package/template/coding-task-manager/lib/landing/proof.js +223 -0
- package/template/coding-task-manager/lib/landing/queue.js +998 -0
- package/template/coding-task-manager/lib/landing/radar.js +489 -0
- package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
- package/template/coding-task-manager/lib/landing/state.js +263 -0
- package/template/coding-task-manager/lib/landing/stop.js +49 -0
- package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
- package/template/coding-task-manager/lib/landing/sync.js +456 -0
- package/template/coding-task-manager/lib/landing/train.js +885 -0
- package/template/coding-task-manager/lib/landing/window.js +57 -0
- package/template/coding-task-manager/lib/launch-environment.js +121 -0
- package/template/coding-task-manager/lib/launch-model-override.js +36 -10
- package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
- package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
- package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
- package/template/coding-task-manager/lib/lead-model-options.js +160 -3
- package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
- package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
- package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
- package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
- package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
- package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
- package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
- package/template/coding-task-manager/lib/machine-registry.js +750 -5
- package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
- package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
- package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
- package/template/coding-task-manager/lib/memory-join.js +337 -0
- package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
- package/template/coding-task-manager/lib/memory-sync.js +565 -0
- package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
- package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
- package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
- package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
- package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
- package/template/coding-task-manager/lib/model-availability.js +727 -0
- package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
- package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
- package/template/coding-task-manager/lib/model-id-rule.js +19 -0
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
- package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
- package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
- package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
- package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
- package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
- package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
- package/template/coding-task-manager/lib/native-history-control.js +6 -4
- package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
- package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
- package/template/coding-task-manager/lib/opencode-cli.js +480 -0
- package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
- package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
- package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
- package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
- package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
- package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
- package/template/coding-task-manager/lib/perf-summary.js +7 -0
- package/template/coding-task-manager/lib/perf-tracker.js +63 -2
- package/template/coding-task-manager/lib/project-config.js +7 -0
- package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
- package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
- package/template/coding-task-manager/lib/read-pool-client.js +89 -2
- package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
- package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
- package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
- package/template/coding-task-manager/lib/relay-client.js +57 -3
- package/template/coding-task-manager/lib/relink-audit.js +34 -5
- package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
- package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
- package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
- package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
- package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
- package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
- package/template/coding-task-manager/lib/restart-guard.js +18 -6
- package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
- package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
- package/template/coding-task-manager/lib/resume-admission.js +17 -3
- package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
- package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
- package/template/coding-task-manager/lib/runtime-contract.js +3 -1
- package/template/coding-task-manager/lib/schema-indexes.js +1 -0
- package/template/coding-task-manager/lib/session-context-locator.js +742 -0
- package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
- package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
- package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
- package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
- package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
- package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
- package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
- package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
- package/template/coding-task-manager/lib/session-create/response.js +7 -7
- package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
- package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
- package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
- package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
- package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
- package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
- package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
- package/template/coding-task-manager/lib/session-empty.js +13 -10
- package/template/coding-task-manager/lib/session-host-manager.js +60 -0
- package/template/coding-task-manager/lib/session-jobs.js +3 -1
- package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
- package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
- package/template/coding-task-manager/lib/session-park.js +738 -0
- package/template/coding-task-manager/lib/session-path-grant.js +13 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
- package/template/coding-task-manager/lib/session-transport.js +17 -0
- package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
- package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
- package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
- package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
- package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
- package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
- package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
- package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
- package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
- package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
- package/template/coding-task-manager/lib/submission-ack.js +90 -2
- package/template/coding-task-manager/lib/systemd-scope.js +115 -0
- package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
- package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
- package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
- package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
- package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
- package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
- package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
- package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
- package/template/coding-task-manager/lib/transcript-source.js +36 -4
- package/template/coding-task-manager/lib/transcript-store.js +9 -0
- package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
- package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
- package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
- package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
- package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
- package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
- package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
- package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
- package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
- package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
- package/template/coding-task-manager/lib/walle/turn.js +1 -1
- package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
- package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
- package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
- package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
- package/template/coding-task-manager/lib/walle-row-history.js +495 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
- package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
- package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
- package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
- package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
- package/template/coding-task-manager/lib/ws/misc.js +1 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
- package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
- package/template/coding-task-manager/oauth-proxy.js +112 -115
- package/template/coding-task-manager/package-lock.json +10 -94
- package/template/coding-task-manager/package.json +4 -3
- package/template/coding-task-manager/protocol/message-table.json +10 -0
- package/template/coding-task-manager/protocol/messages.js +10 -0
- package/template/coding-task-manager/protocol/ws-schema.json +20 -0
- package/template/coding-task-manager/protocol/ws-schema.md +2 -0
- package/template/coding-task-manager/public/css/app.css +170 -0
- package/template/coding-task-manager/public/css/walle-session.css +4 -0
- package/template/coding-task-manager/public/index.html +473 -243
- package/template/coding-task-manager/public/js/agent-command.js +86 -0
- package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
- package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
- package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
- package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
- package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
- package/template/coding-task-manager/public/js/background-work.js +196 -27
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
- package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
- package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
- package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
- package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
- package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
- package/template/coding-task-manager/public/js/document-review-links.js +9 -1
- package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
- package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
- package/template/coding-task-manager/public/js/message-renderer.js +4 -3
- package/template/coding-task-manager/public/js/model-picker.js +3 -1
- package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
- package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
- package/template/coding-task-manager/public/js/pasted-content.js +236 -0
- package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
- package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
- package/template/coding-task-manager/public/js/prompts.js +11 -10
- package/template/coding-task-manager/public/js/protocol.js +10 -0
- package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
- package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
- package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
- package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
- package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
- package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
- package/template/coding-task-manager/public/js/stream-view.js +6 -5
- package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
- package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
- package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
- package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
- package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
- package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
- package/template/coding-task-manager/public/js/walle.js +6 -0
- package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
- package/template/coding-task-manager/public/m/app.css +140 -0
- package/template/coding-task-manager/public/m/app.js +507 -68
- package/template/coding-task-manager/public/m/index.html +17 -0
- package/template/coding-task-manager/public/m/machines.js +838 -4
- package/template/coding-task-manager/public/m/private-api.js +31 -1
- package/template/coding-task-manager/public/usage.html +1 -0
- package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
- package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
- package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
- package/template/coding-task-manager/server.js +1950 -11679
- package/template/coding-task-manager/session-integrity.js +64 -4
- package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
- package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
- package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
- package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
- package/template/coding-task-manager/workers/session-host-process.js +5 -5
- package/template/coding-task-manager/workers/watch-worker.js +105 -0
- package/template/coding-task-manager/workers/worktree-worker.js +168 -33
- package/template/ctm-relay/lib/protocol.js +14 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/shared/backup-retention.js +82 -0
- package/template/shared/conversation-admission.js +2 -0
- package/template/shared/conversation-provenance.js +3 -7
- package/template/shared/conversation-storage-v2-contract.js +5 -0
- package/template/shared/linked-worktree.js +51 -0
- package/template/shared/macos-fda-target.js +57 -0
- package/template/shared/orphan-parents.js +55 -0
- package/template/shared/release-schema.json +2 -2
- package/template/shared/storage/contract.js +3 -0
- package/template/wall-e/adapters/imessage.js +12 -0
- package/template/wall-e/agent.js +71 -4
- package/template/wall-e/api-walle.js +21 -1
- package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
- package/template/wall-e/brain/brain-join.js +859 -0
- package/template/wall-e/brain/brain-sync-membership.js +20 -0
- package/template/wall-e/brain/brain-sync-store.js +131 -77
- package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
- package/template/wall-e/brain/brain-sync-v2.js +578 -0
- package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
- package/template/wall-e/brain/deterministic-id.js +51 -0
- package/template/wall-e/brain/memories.js +2 -2
- package/template/wall-e/brain/migrations.js +40 -6
- package/template/wall-e/brain/model-identity-migration.js +1 -1
- package/template/wall-e/brain/owner-call-whitelist.js +23 -7
- package/template/wall-e/brain/schema.js +45 -85
- package/template/wall-e/brain.js +171 -35
- package/template/wall-e/chat.js +3 -3
- package/template/wall-e/coding/agent-shell-env.js +7 -2
- package/template/wall-e/coding/prompt-bundle.js +66 -21
- package/template/wall-e/config/main-loop-exemptions.json +2 -2
- package/template/wall-e/http/admin-api.js +37 -0
- package/template/wall-e/http/gws-api.js +4 -0
- package/template/wall-e/http/helper-rules-api.js +7 -1
- package/template/wall-e/http/model-admin.js +8 -1
- package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
- package/template/wall-e/http/model-proxy-providers.js +174 -1
- package/template/wall-e/http/model-route-health.js +157 -0
- package/template/wall-e/http/sync-api.js +278 -464
- package/template/wall-e/jobs/job-runner.js +28 -1
- package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
- package/template/wall-e/lib/ctm-data-dir.js +26 -0
- package/template/wall-e/lib/derivation-home.js +147 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
- package/template/wall-e/lib/helper-rules/grade.js +51 -8
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
- package/template/wall-e/lib/helper-rules/store.js +4 -2
- package/template/wall-e/lib/landing-mcp-tools.js +170 -0
- package/template/wall-e/lib/mcp-client-config.js +133 -14
- package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
- package/template/wall-e/lib/mcp-integration.js +58 -13
- package/template/wall-e/lib/parent-death.js +7 -1
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
- package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
- package/template/wall-e/llm/delegate-vendor-default.js +12 -5
- package/template/wall-e/llm/model-roles.js +1 -1
- package/template/wall-e/llm/native-delegate-selection.js +56 -8
- package/template/wall-e/llm/worker-delegate-args.js +72 -7
- package/template/wall-e/llm/worker-mcp-routing.js +66 -17
- package/template/wall-e/loops/brain-custom-writes.js +19 -0
- package/template/wall-e/loops/ingest.js +17 -1
- package/template/wall-e/loops/think.js +19 -4
- package/template/wall-e/mcp-contract.js +222 -6
- package/template/wall-e/mcp-protocol-contract.js +40 -5
- package/template/wall-e/mcp-server.js +128 -29
- package/template/wall-e/mcp-worker-schemas.js +5 -5
- package/template/wall-e/memory/ctm-context-client.js +85 -0
- package/template/wall-e/memory/ctm-session-context.js +9 -3
- package/template/wall-e/memory/hub-bridge-client.js +388 -0
- package/template/wall-e/memory/replica-origin.js +56 -0
- package/template/wall-e/memory/session-pool.js +135 -0
- package/template/wall-e/runtime/live-capabilities.js +8 -3
- package/template/wall-e/server.js +4 -2
- package/template/wall-e/shared/release-schema.json +2 -2
- package/template/wall-e/shared/sqlite-file-set.js +39 -0
- package/template/wall-e/shared/storage/contract.js +3 -0
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
- package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
- package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
- package/template/wall-e/tools/chrome-automation.js +6 -0
- package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
- package/template/wall-e/workers/brain-owner-worker.js +18 -4
- package/template/wall-e/workers/mcp-gateway-process.js +5 -5
|
@@ -18,6 +18,31 @@
|
|
|
18
18
|
// all three move here VERBATIM (misc-ops.js's workspace handlers are the
|
|
19
19
|
// precedent for a verbatim body living alongside dispatch-only wiring in the
|
|
20
20
|
// same module).
|
|
21
|
+
//
|
|
22
|
+
// Landing-throughput WP-5 shard S2 (design doc §5 D-10 "move-only", §6 "Shard
|
|
23
|
+
// module seam", §8 WP-5, §9 S-21..S-27): three more setup handlers move out of
|
|
24
|
+
// server.js VERBATIM -- apiSetupTestKey (GET /api/setup/test-key),
|
|
25
|
+
// apiSetupDetectKey (GET /api/setup/detect-key) and apiSetupSave
|
|
26
|
+
// (POST /api/setup/save). Each body is identical to server.js's declaration
|
|
27
|
+
// except for the `_deps.` prefix on every identifier that used to resolve to a
|
|
28
|
+
// server.js top-level binding (D-10 rule 1), proven by
|
|
29
|
+
// `node bin/move-only-check.js`. Injected deps, per function:
|
|
30
|
+
// apiSetupTestKey: CONFIG_DIR, MODEL_PROBE_ORDER, codexPaths, fs,
|
|
31
|
+
// getWalleStatusData, hostIdentityAccessAllowed, missingBinaryChild,
|
|
32
|
+
// oauthProxySupervisor, probeModel, resolveAgentCliCommand,
|
|
33
|
+
// sanitizeSetupProviderType, serverConfig, walleClient
|
|
34
|
+
// apiSetupDetectKey: CONFIG_DIR, codexPaths, execFileSync, fs,
|
|
35
|
+
// hostIdentityAccessAllowed, path, probeModel, readUserShellEnv,
|
|
36
|
+
// resolveAgentCliCommand, sanitizeSetupProviderType, serverConfig
|
|
37
|
+
// apiSetupSave: CONFIG_DIR, SETUP_PROVIDER_TYPES, __dirname,
|
|
38
|
+
// atomicWriteFileSync, fs, path, resolveEnvFilePath,
|
|
39
|
+
// sanitizeSetupProviderType, serverConfig, setup, setupProviderTypeList,
|
|
40
|
+
// telemetry, walleClient, walleSupervisor
|
|
41
|
+
// `__dirname` is injected although it is NOT a server.js top-level binding: it
|
|
42
|
+
// is the CommonJS wrapper parameter, so left bare in this file it would resolve
|
|
43
|
+
// to lib/api/ instead of server.js's own directory and silently change
|
|
44
|
+
// resolveEnvFilePath's repoRoot (D-10 rule 9, "behaviour: none changes"). It is
|
|
45
|
+
// passed by value, same as every immutable binding.
|
|
21
46
|
|
|
22
47
|
const { registerApiRoutes } = require('../api-router');
|
|
23
48
|
|
|
@@ -25,15 +50,12 @@ let _deps = null;
|
|
|
25
50
|
|
|
26
51
|
function configure({
|
|
27
52
|
apiSetupStatus,
|
|
28
|
-
apiSetupTestKey,
|
|
29
53
|
apiSetupDetectCodexCli,
|
|
30
|
-
apiSetupDetectKey,
|
|
31
54
|
apiSetupDetectGeminiCli,
|
|
32
55
|
apiSetupDetectClaudeCli,
|
|
33
56
|
apiSetupOauthProxyStart,
|
|
34
57
|
apiSetupOauthProxyStop,
|
|
35
58
|
apiSetupOauthProxyStatus,
|
|
36
|
-
apiSetupSave,
|
|
37
59
|
apiSetupProviders,
|
|
38
60
|
apiSetupSaveProvider,
|
|
39
61
|
apiSetupTestProvider,
|
|
@@ -49,18 +71,40 @@ function configure({
|
|
|
49
71
|
listWalleModelsViaOwner,
|
|
50
72
|
walleOwnerWrite,
|
|
51
73
|
serverConfig,
|
|
74
|
+
// WP-5 shard S2 move-only deps (see this file's header): the top-level
|
|
75
|
+
// server.js bindings apiSetupTestKey/apiSetupDetectKey/apiSetupSave used to
|
|
76
|
+
// close over. Immutable bindings, passed by value; `__dirname` is
|
|
77
|
+
// server.js's own module-wrapper directory (see header).
|
|
78
|
+
CONFIG_DIR,
|
|
79
|
+
MODEL_PROBE_ORDER,
|
|
80
|
+
codexPaths,
|
|
81
|
+
execFileSync,
|
|
82
|
+
fs,
|
|
83
|
+
path,
|
|
84
|
+
getWalleStatusData,
|
|
85
|
+
hostIdentityAccessAllowed,
|
|
86
|
+
missingBinaryChild,
|
|
87
|
+
oauthProxySupervisor,
|
|
88
|
+
probeModel,
|
|
89
|
+
readUserShellEnv,
|
|
90
|
+
resolveAgentCliCommand,
|
|
91
|
+
atomicWriteFileSync,
|
|
92
|
+
resolveEnvFilePath,
|
|
93
|
+
setup,
|
|
94
|
+
setupProviderTypeList,
|
|
95
|
+
telemetry,
|
|
96
|
+
walleClient,
|
|
97
|
+
walleSupervisor,
|
|
98
|
+
__dirname,
|
|
52
99
|
} = {}) {
|
|
53
100
|
_deps = {
|
|
54
101
|
apiSetupStatus,
|
|
55
|
-
apiSetupTestKey,
|
|
56
102
|
apiSetupDetectCodexCli,
|
|
57
|
-
apiSetupDetectKey,
|
|
58
103
|
apiSetupDetectGeminiCli,
|
|
59
104
|
apiSetupDetectClaudeCli,
|
|
60
105
|
apiSetupOauthProxyStart,
|
|
61
106
|
apiSetupOauthProxyStop,
|
|
62
107
|
apiSetupOauthProxyStatus,
|
|
63
|
-
apiSetupSave,
|
|
64
108
|
apiSetupProviders,
|
|
65
109
|
apiSetupSaveProvider,
|
|
66
110
|
apiSetupTestProvider,
|
|
@@ -76,6 +120,27 @@ function configure({
|
|
|
76
120
|
listWalleModelsViaOwner,
|
|
77
121
|
walleOwnerWrite,
|
|
78
122
|
serverConfig,
|
|
123
|
+
CONFIG_DIR,
|
|
124
|
+
MODEL_PROBE_ORDER,
|
|
125
|
+
codexPaths,
|
|
126
|
+
execFileSync,
|
|
127
|
+
fs,
|
|
128
|
+
path,
|
|
129
|
+
getWalleStatusData,
|
|
130
|
+
hostIdentityAccessAllowed,
|
|
131
|
+
missingBinaryChild,
|
|
132
|
+
oauthProxySupervisor,
|
|
133
|
+
probeModel,
|
|
134
|
+
readUserShellEnv,
|
|
135
|
+
resolveAgentCliCommand,
|
|
136
|
+
atomicWriteFileSync,
|
|
137
|
+
resolveEnvFilePath,
|
|
138
|
+
setup,
|
|
139
|
+
setupProviderTypeList,
|
|
140
|
+
telemetry,
|
|
141
|
+
walleClient,
|
|
142
|
+
walleSupervisor,
|
|
143
|
+
__dirname,
|
|
79
144
|
};
|
|
80
145
|
}
|
|
81
146
|
|
|
@@ -226,15 +291,15 @@ async function handleSetupModelsByType(req, res, type) {
|
|
|
226
291
|
function register() {
|
|
227
292
|
registerApiRoutes('setup', [
|
|
228
293
|
{ method: 'GET', path: '/api/setup/status', handler: (req, res, url) => _deps.apiSetupStatus(req, res, url) },
|
|
229
|
-
{ method: 'GET', path: '/api/setup/test-key', handler: (req, res, url) =>
|
|
294
|
+
{ method: 'GET', path: '/api/setup/test-key', handler: (req, res, url) => apiSetupTestKey(req, res, url) },
|
|
230
295
|
{ method: 'GET', path: '/api/setup/detect-codex-cli', handler: (req, res, url) => _deps.apiSetupDetectCodexCli(req, res, url) },
|
|
231
|
-
{ method: 'GET', path: '/api/setup/detect-key', handler: (req, res, url) =>
|
|
296
|
+
{ method: 'GET', path: '/api/setup/detect-key', handler: (req, res, url) => apiSetupDetectKey(req, res, url) },
|
|
232
297
|
{ method: 'GET', path: '/api/setup/detect-gemini-cli', handler: (req, res, url) => _deps.apiSetupDetectGeminiCli(req, res, url) },
|
|
233
298
|
{ method: 'GET', path: '/api/setup/detect-claude-cli', handler: (req, res, url) => _deps.apiSetupDetectClaudeCli(req, res, url) },
|
|
234
299
|
{ method: 'POST', path: '/api/setup/oauth-proxy/start', handler: (req, res, url) => _deps.apiSetupOauthProxyStart(req, res, url) },
|
|
235
300
|
{ method: 'POST', path: '/api/setup/oauth-proxy/stop', handler: (req, res, url) => _deps.apiSetupOauthProxyStop(req, res, url) },
|
|
236
301
|
{ method: 'GET', path: '/api/setup/oauth-proxy/status', handler: (req, res, url) => _deps.apiSetupOauthProxyStatus(req, res, url) },
|
|
237
|
-
{ method: 'POST', path: '/api/setup/save', handler: (req, res, url) =>
|
|
302
|
+
{ method: 'POST', path: '/api/setup/save', handler: (req, res, url) => apiSetupSave(req, res, url) },
|
|
238
303
|
{ method: 'GET', path: '/api/setup/providers', handler: (req, res, url) => _deps.apiSetupProviders(req, res, url) },
|
|
239
304
|
{ method: 'POST', path: '/api/setup/save-provider', handler: (req, res, url) => _deps.apiSetupSaveProvider(req, res, url) },
|
|
240
305
|
{ method: 'POST', path: '/api/setup/test-provider', handler: (req, res, url) => _deps.apiSetupTestProvider(req, res, url) },
|
|
@@ -267,4 +332,758 @@ function register() {
|
|
|
267
332
|
]);
|
|
268
333
|
}
|
|
269
334
|
|
|
270
|
-
|
|
335
|
+
async function apiSetupTestKey(req, res, url) {
|
|
336
|
+
const providerParam = _deps.sanitizeSetupProviderType(url.searchParams.get('provider') || '');
|
|
337
|
+
const authMethodParam = url.searchParams.get('auth_method') || '';
|
|
338
|
+
const walleStatus = await _deps.getWalleStatusData().catch(() => ({}));
|
|
339
|
+
const walleProvider = walleStatus.walle_provider || 'anthropic';
|
|
340
|
+
const walleModel = walleStatus.walle_model || '';
|
|
341
|
+
|
|
342
|
+
// ── auth_method=claude_cli: shell out to `claude auth status` (JSON
|
|
343
|
+
// output, no API call, no tokens burned). Tells us whether the CLI
|
|
344
|
+
// is installed AND authenticated, plus the user's email + plan.
|
|
345
|
+
if (providerParam === 'anthropic' && authMethodParam === 'claude_cli') {
|
|
346
|
+
const respond = (data) => {
|
|
347
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
348
|
+
res.end(JSON.stringify(data));
|
|
349
|
+
};
|
|
350
|
+
const { spawn } = require('child_process');
|
|
351
|
+
// Scrub ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN so the CLI reports the
|
|
352
|
+
// subscription auth (claude.ai login) we actually want to validate
|
|
353
|
+
// here — otherwise it just echoes back "apiKeySource": "ANTHROPIC_API_KEY"
|
|
354
|
+
// and we'd be testing the wrong path.
|
|
355
|
+
const childEnv = { ...process.env };
|
|
356
|
+
delete childEnv.ANTHROPIC_API_KEY;
|
|
357
|
+
delete childEnv.ANTHROPIC_AUTH_TOKEN;
|
|
358
|
+
let proc;
|
|
359
|
+
try {
|
|
360
|
+
proc = spawn(_deps.resolveAgentCliCommand('claude'), ['auth', 'status'], {
|
|
361
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
362
|
+
timeout: 15000,
|
|
363
|
+
env: childEnv,
|
|
364
|
+
});
|
|
365
|
+
} catch (err) {
|
|
366
|
+
return respond({ ok: false, error: 'Could not spawn claude: ' + err.message });
|
|
367
|
+
}
|
|
368
|
+
let stdout = '';
|
|
369
|
+
let stderr = '';
|
|
370
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
371
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
372
|
+
proc.on('error', (err) => {
|
|
373
|
+
respond({
|
|
374
|
+
ok: false,
|
|
375
|
+
error: err.code === 'ENOENT'
|
|
376
|
+
? 'claude CLI not on PATH. Install Claude Code from claude.com/claude-code.'
|
|
377
|
+
: 'spawn failed: ' + err.message,
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
proc.on('close', (code) => {
|
|
381
|
+
if (code !== 0) {
|
|
382
|
+
return respond({ ok: false, error: 'claude auth status exited ' + code + ': ' + (stderr || stdout).slice(0, 300) });
|
|
383
|
+
}
|
|
384
|
+
let parsed;
|
|
385
|
+
try {
|
|
386
|
+
parsed = JSON.parse(stdout);
|
|
387
|
+
} catch (parseErr) {
|
|
388
|
+
return respond({ ok: false, error: 'claude auth status stdout not JSON: ' + stdout.slice(0, 200) });
|
|
389
|
+
}
|
|
390
|
+
if (!parsed.loggedIn) {
|
|
391
|
+
return respond({ ok: false, error: 'CLI installed but not signed in. Run `claude auth login`.' });
|
|
392
|
+
}
|
|
393
|
+
// Display "email · subscription" e.g. "user@example.com · pro"
|
|
394
|
+
const label = [parsed.email, parsed.subscriptionType].filter(Boolean).join(' · ');
|
|
395
|
+
respond({ ok: true, model: label || 'claude.ai', reply: parsed.authMethod || 'subscription' });
|
|
396
|
+
});
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ── auth_method=oauth_proxy: probe the running proxy on its loopback
|
|
401
|
+
// port, then send a minimal /v1/messages request through it. Flips
|
|
402
|
+
// `running=false` to a clear error so the user knows to enable it.
|
|
403
|
+
if (providerParam === 'anthropic' && authMethodParam === 'oauth_proxy') {
|
|
404
|
+
const respond = (data) => {
|
|
405
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
406
|
+
res.end(JSON.stringify(data));
|
|
407
|
+
};
|
|
408
|
+
const proxyStatus = await _deps.oauthProxySupervisor.status();
|
|
409
|
+
if (!proxyStatus.running) {
|
|
410
|
+
return respond({ ok: false, error: 'OAuth proxy is not running yet. Save the Anthropic card to start it, then test again.', proxy_status: proxyStatus, remediation: 'save_provider' });
|
|
411
|
+
}
|
|
412
|
+
const port = proxyStatus.port;
|
|
413
|
+
try {
|
|
414
|
+
const r = await fetch(`http://127.0.0.1:${port}/v1/messages`, {
|
|
415
|
+
method: 'POST',
|
|
416
|
+
headers: {
|
|
417
|
+
'Content-Type': 'application/json',
|
|
418
|
+
'anthropic-version': '2023-06-01',
|
|
419
|
+
},
|
|
420
|
+
body: JSON.stringify({
|
|
421
|
+
model: 'claude-haiku-4-5-20251001',
|
|
422
|
+
max_tokens: 20,
|
|
423
|
+
messages: [{ role: 'user', content: 'reply with the literal string OK and nothing else' }],
|
|
424
|
+
}),
|
|
425
|
+
signal: AbortSignal.timeout(20000),
|
|
426
|
+
});
|
|
427
|
+
if (!r.ok) {
|
|
428
|
+
const txt = await r.text().catch(() => '');
|
|
429
|
+
return respond({ ok: false, error: `Proxy returned ${r.status}: ${txt.slice(0, 250)}` });
|
|
430
|
+
}
|
|
431
|
+
const d = await r.json();
|
|
432
|
+
const reply = (d.content || []).filter((b) => b.type === 'text').map((b) => b.text).join('').trim();
|
|
433
|
+
return respond({
|
|
434
|
+
ok: reply.length > 0,
|
|
435
|
+
model: d.model || 'claude-haiku-4-5',
|
|
436
|
+
reply: reply.slice(0, 50),
|
|
437
|
+
});
|
|
438
|
+
} catch (err) {
|
|
439
|
+
return respond({ ok: false, error: 'Proxy probe failed: ' + err.message });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const _testStoredProvider = async (type, fallbackModel, extraBody = {}) => {
|
|
444
|
+
try {
|
|
445
|
+
const providersResp = await _deps.walleClient.requestJson('/api/models/providers');
|
|
446
|
+
const providers = Array.isArray(providersResp.json?.providers) ? providersResp.json.providers : [];
|
|
447
|
+
const keylessProvider = type === 'ollama' || type === 'mlx';
|
|
448
|
+
const provider = providers.find((item) => item.type === type && item.enabled !== 0 && (keylessProvider || item.has_key));
|
|
449
|
+
if (!provider) return null;
|
|
450
|
+
const testResp = await _deps.walleClient.requestJson('/api/models/test-connection', {
|
|
451
|
+
method: 'POST',
|
|
452
|
+
body: { type, instance_id: provider.id, ...extraBody },
|
|
453
|
+
});
|
|
454
|
+
if (testResp.status >= 200 && testResp.status < 300 && testResp.json?.ok) {
|
|
455
|
+
const firstModel = Array.isArray(testResp.json.models) && testResp.json.models[0]?.id;
|
|
456
|
+
return { ok: true, model: testResp.json.probe?.model || firstModel || fallbackModel, probe: testResp.json.probe || null };
|
|
457
|
+
}
|
|
458
|
+
return { ok: false, error: testResp.json?.error || 'Provider not available', probe: testResp.json?.probe || null };
|
|
459
|
+
} catch {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// ── Ollama test (local runtime, no API key) ──
|
|
465
|
+
if (providerParam === 'ollama') {
|
|
466
|
+
const ollamaModel = (walleProvider === 'ollama' && walleModel) ? walleModel : (url.searchParams.get('model') || process.env.OLLAMA_MODEL || 'gemma4:e4b');
|
|
467
|
+
const stored = await _testStoredProvider('ollama', ollamaModel, { model: ollamaModel });
|
|
468
|
+
if (stored) {
|
|
469
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
470
|
+
res.end(JSON.stringify(stored));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
const testResp = await _deps.walleClient.requestJson('/api/models/test-connection', {
|
|
475
|
+
method: 'POST',
|
|
476
|
+
body: {
|
|
477
|
+
type: 'ollama',
|
|
478
|
+
model: ollamaModel,
|
|
479
|
+
baseUrl: process.env.OLLAMA_BASE_URL || 'http://127.0.0.1:11434',
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
483
|
+
if (testResp.status >= 200 && testResp.status < 300 && testResp.json?.ok) {
|
|
484
|
+
res.end(JSON.stringify({
|
|
485
|
+
ok: true,
|
|
486
|
+
model: testResp.json.probe?.model || ollamaModel,
|
|
487
|
+
probe: testResp.json.probe || null,
|
|
488
|
+
}));
|
|
489
|
+
} else {
|
|
490
|
+
res.end(JSON.stringify({
|
|
491
|
+
ok: false,
|
|
492
|
+
error: testResp.json?.error || `Ollama probe failed with HTTP ${testResp.status}`,
|
|
493
|
+
probe: testResp.json?.probe || null,
|
|
494
|
+
}));
|
|
495
|
+
}
|
|
496
|
+
} catch (e) {
|
|
497
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
498
|
+
res.end(JSON.stringify({ ok: false, error: e.message }));
|
|
499
|
+
}
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ── Google key test ──
|
|
504
|
+
if (providerParam === 'google') {
|
|
505
|
+
const googleKey = _deps.serverConfig.GOOGLE_API_KEY || '';
|
|
506
|
+
if (!googleKey) {
|
|
507
|
+
const stored = await _testStoredProvider('google', 'gemini-2.5-flash');
|
|
508
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
509
|
+
res.end(JSON.stringify(stored || { ok: false, error: 'No Google API key configured.' }));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Detect stale ya29 access tokens — these are short-lived and can't be refreshed without GOOGLE_AUTH_MODE=oauth
|
|
513
|
+
if (googleKey.startsWith('ya29.') && _deps.serverConfig.GOOGLE_AUTH_MODE !== 'oauth') {
|
|
514
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
515
|
+
res.end(JSON.stringify({ ok: false, error: 'Stored key is a stale Gemini CLI OAuth token (ya29.*). Get an API key from aistudio.google.com/apikey or use "Use Gemini CLI Credentials" on the setup page.' }));
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const isOAuth = _deps.serverConfig.GOOGLE_AUTH_MODE === 'oauth';
|
|
519
|
+
const testUrl = isOAuth
|
|
520
|
+
? 'https://generativelanguage.googleapis.com/v1beta/models?pageSize=1'
|
|
521
|
+
: `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(googleKey)}&pageSize=1`;
|
|
522
|
+
const headers = isOAuth ? { 'Authorization': `Bearer ${googleKey}` } : {};
|
|
523
|
+
const googleModel = (walleProvider === 'google' && walleModel && walleModel.startsWith('gemini')) ? walleModel : 'gemini-2.5-flash';
|
|
524
|
+
fetch(testUrl, { headers }).then(async (resp) => {
|
|
525
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
526
|
+
if (resp.ok) { res.end(JSON.stringify({ ok: true, model: googleModel })); }
|
|
527
|
+
else { const body = await resp.json().catch(() => ({})); res.end(JSON.stringify({ ok: false, error: body?.error?.message || `HTTP ${resp.status}` })); }
|
|
528
|
+
}).catch(e => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: e.message })); });
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// ── OpenAI codex_cli test: shell out to `codex login status` (free, no
|
|
533
|
+
// API call, no tokens). Mirrors anthropic's claude_cli test pattern.
|
|
534
|
+
if (providerParam === 'openai' && authMethodParam === 'codex_cli') {
|
|
535
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
536
|
+
const spawn = _deps.hostIdentityAccessAllowed({ configDir: _deps.CONFIG_DIR }) ? require('child_process').spawn : _deps.missingBinaryChild;
|
|
537
|
+
const childEnv = { ...process.env };
|
|
538
|
+
delete childEnv.OPENAI_API_KEY; // force ChatGPT subscription path
|
|
539
|
+
let proc;
|
|
540
|
+
try {
|
|
541
|
+
proc = spawn(_deps.resolveAgentCliCommand('codex'), ['login', 'status'], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 10000, env: childEnv });
|
|
542
|
+
} catch (err) {
|
|
543
|
+
return respond({ ok: false, error: 'Could not spawn codex: ' + err.message });
|
|
544
|
+
}
|
|
545
|
+
let stdout = '';
|
|
546
|
+
let stderr = '';
|
|
547
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
548
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
549
|
+
proc.on('error', (err) => {
|
|
550
|
+
respond({
|
|
551
|
+
ok: false,
|
|
552
|
+
error: err.code === 'ENOENT'
|
|
553
|
+
? 'codex CLI not on PATH. Install Codex from chatgpt.com/codex.'
|
|
554
|
+
: 'spawn failed: ' + err.message,
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
proc.on('close', (code) => {
|
|
558
|
+
if (code !== 0) {
|
|
559
|
+
return respond({ ok: false, error: 'codex login status exited ' + code + ': ' + (stderr || stdout).slice(0, 300) });
|
|
560
|
+
}
|
|
561
|
+
// codex login status prints to stderr; combine streams for the match.
|
|
562
|
+
const combined = stdout + '\n' + stderr;
|
|
563
|
+
const loggedIn = /logged in/i.test(combined);
|
|
564
|
+
if (!loggedIn) {
|
|
565
|
+
return respond({ ok: false, error: 'Codex installed but not logged in. Run `codex login`.' });
|
|
566
|
+
}
|
|
567
|
+
// Parse plan from auth.json JWT (best-effort, same as detect)
|
|
568
|
+
let plan = null;
|
|
569
|
+
try {
|
|
570
|
+
const auth = JSON.parse(_deps.fs.readFileSync(_deps.codexPaths.codexAuthPath(), 'utf8'));
|
|
571
|
+
const accessToken = auth?.tokens?.access_token;
|
|
572
|
+
if (accessToken) {
|
|
573
|
+
const parts = accessToken.split('.');
|
|
574
|
+
if (parts.length >= 2) {
|
|
575
|
+
const pad = '='.repeat((-parts[1].length) & 3);
|
|
576
|
+
const payload = JSON.parse(Buffer.from(parts[1] + pad, 'base64url').toString('utf8'));
|
|
577
|
+
plan = payload?.['https://api.openai.com/auth']?.chatgpt_plan_type || null;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
} catch { /* best-effort */ }
|
|
581
|
+
const emailMatch = combined.match(/[\w.+-]+@[\w-]+\.[\w.-]+/);
|
|
582
|
+
const label = [emailMatch ? emailMatch[0] : null, plan].filter(Boolean).join(' · ');
|
|
583
|
+
respond({ ok: true, model: label || 'ChatGPT subscription', reply: 'codex' });
|
|
584
|
+
});
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ── DeepSeek key test (api_key — direct API, OpenAI-compatible) ──
|
|
589
|
+
if (providerParam === 'deepseek') {
|
|
590
|
+
const deepseekKey = _deps.serverConfig.DEEPSEEK_API_KEY || '';
|
|
591
|
+
if (!deepseekKey) {
|
|
592
|
+
const stored = await _testStoredProvider('deepseek', 'deepseek-v4-flash');
|
|
593
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
594
|
+
res.end(JSON.stringify(stored || { ok: false, error: 'No DeepSeek API key configured.' }));
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const dsModel = (walleProvider === 'deepseek' && walleModel && /^deepseek-/.test(walleModel)) ? walleModel : 'deepseek-v4-flash';
|
|
598
|
+
// /models is OpenAI-compatible at api.deepseek.com — single GET, no token burn.
|
|
599
|
+
fetch('https://api.deepseek.com/v1/models', { headers: { 'Authorization': `Bearer ${deepseekKey}` } }).then(async (resp) => {
|
|
600
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
601
|
+
if (resp.ok) { res.end(JSON.stringify({ ok: true, model: dsModel })); }
|
|
602
|
+
else { const body = await resp.json().catch(() => ({})); res.end(JSON.stringify({ ok: false, error: body?.error?.message || `HTTP ${resp.status}` })); }
|
|
603
|
+
}).catch(e => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: e.message })); });
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ── Moonshot / Kimi key test (api_key — direct API, OpenAI-compatible) ──
|
|
608
|
+
if (providerParam === 'moonshot') {
|
|
609
|
+
const moonshotKey = _deps.serverConfig.MOONSHOT_API_KEY || '';
|
|
610
|
+
if (!moonshotKey) {
|
|
611
|
+
const stored = await _testStoredProvider('moonshot', 'kimi-k2.6');
|
|
612
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
613
|
+
res.end(JSON.stringify(stored || { ok: false, error: 'No Moonshot API key configured.' }));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const moonshotModel = (walleProvider === 'moonshot' && walleModel && /^(kimi-|moonshot-)/.test(walleModel)) ? walleModel : 'kimi-k2.6';
|
|
617
|
+
const base = process.env.MOONSHOT_BASE_URL || 'https://api.moonshot.ai/v1';
|
|
618
|
+
fetch(base.replace(/\/+$/, '') + '/models', { headers: { 'Authorization': `Bearer ${moonshotKey}` } }).then(async (resp) => {
|
|
619
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
620
|
+
if (resp.ok) { res.end(JSON.stringify({ ok: true, model: moonshotModel })); }
|
|
621
|
+
else { const body = await resp.json().catch(() => ({})); res.end(JSON.stringify({ ok: false, error: body?.error?.message || `HTTP ${resp.status}` })); }
|
|
622
|
+
}).catch(e => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: e.message })); });
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ── Z.AI key test (api_key — direct API, OpenAI-compatible) ──
|
|
627
|
+
if (providerParam === 'zai') {
|
|
628
|
+
const zaiKey = _deps.serverConfig.ZAI_API_KEY || '';
|
|
629
|
+
if (!zaiKey) {
|
|
630
|
+
const stored = await _testStoredProvider('zai', 'glm-5.2');
|
|
631
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
632
|
+
res.end(JSON.stringify(stored || { ok: false, error: 'No Z.AI API key configured.' }));
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const zaiModel = (walleProvider === 'zai' && walleModel && /^glm-/.test(walleModel)) ? walleModel : 'glm-5.2';
|
|
636
|
+
const base = process.env.ZAI_BASE_URL || 'https://api.z.ai/api/paas/v4/';
|
|
637
|
+
fetch(base.replace(/\/+$/, '') + '/models', { headers: { 'Authorization': `Bearer ${zaiKey}` } }).then(async (resp) => {
|
|
638
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
639
|
+
if (resp.ok) { res.end(JSON.stringify({ ok: true, model: zaiModel })); }
|
|
640
|
+
else { const body = await resp.json().catch(() => ({})); res.end(JSON.stringify({ ok: false, error: body?.error?.message || `HTTP ${resp.status}` })); }
|
|
641
|
+
}).catch(e => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: e.message })); });
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// ── OpenAI key test (api_key — direct API) ──
|
|
646
|
+
if (providerParam === 'openai') {
|
|
647
|
+
const openaiKey = _deps.serverConfig.OPENAI_API_KEY || '';
|
|
648
|
+
if (!openaiKey) {
|
|
649
|
+
const stored = await _testStoredProvider('openai', 'gpt-4o-mini');
|
|
650
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
651
|
+
res.end(JSON.stringify(stored || { ok: false, error: 'No OpenAI API key configured.' }));
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const openaiModel = (walleProvider === 'openai' && walleModel && /^(gpt|o[1-9])/.test(walleModel)) ? walleModel : 'gpt-4o-mini';
|
|
655
|
+
fetch('https://api.openai.com/v1/models?limit=1', { headers: { 'Authorization': `Bearer ${openaiKey}` } }).then(async (resp) => {
|
|
656
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
657
|
+
if (resp.ok) { res.end(JSON.stringify({ ok: true, model: openaiModel })); }
|
|
658
|
+
else { const body = await resp.json().catch(() => ({})); res.end(JSON.stringify({ ok: false, error: body?.error?.message || `HTTP ${resp.status}` })); }
|
|
659
|
+
}).catch(e => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: e.message })); });
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ── Anthropic key test (default) ──
|
|
664
|
+
const apiKey = _deps.serverConfig.ANTHROPIC_API_KEY || _deps.serverConfig.ANTHROPIC_AUTH_TOKEN || '';
|
|
665
|
+
const baseUrl = _deps.serverConfig.ANTHROPIC_BASE_URL || 'https://api.anthropic.com';
|
|
666
|
+
const customHeadersB64 = _deps.serverConfig.ANTHROPIC_CUSTOM_HEADERS_B64 || '';
|
|
667
|
+
const configuredModel = _deps.MODEL_PROBE_ORDER[0];
|
|
668
|
+
if (!apiKey) {
|
|
669
|
+
const stored = await _testStoredProvider('anthropic', configuredModel);
|
|
670
|
+
if (stored) {
|
|
671
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
672
|
+
res.end(JSON.stringify(stored));
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
676
|
+
res.end(JSON.stringify({ ok: false, error: 'No API key configured.' }));
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
// Try the configured model first; only probe for an alternative if it fails.
|
|
680
|
+
// Build probe order with configured model at the front (deduplicated)
|
|
681
|
+
const probeOrder = [configuredModel, ..._deps.MODEL_PROBE_ORDER.filter(m => m !== configuredModel)];
|
|
682
|
+
_deps.probeModel(apiKey, baseUrl, customHeadersB64, probeOrder).then(({ model, authError }) => {
|
|
683
|
+
if (model) {
|
|
684
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
685
|
+
res.end(JSON.stringify({ ok: true, model }));
|
|
686
|
+
} else if (authError) {
|
|
687
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
688
|
+
res.end(JSON.stringify({ ok: false, error: 'Invalid API key — double-check the key and try again.' }));
|
|
689
|
+
} else {
|
|
690
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
691
|
+
res.end(JSON.stringify({ ok: false, error: 'No accessible model found for this key. Check that your key has inference permissions.' }));
|
|
692
|
+
}
|
|
693
|
+
}).catch(e => {
|
|
694
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
695
|
+
res.end(JSON.stringify({ ok: false, error: e.message }));
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function apiSetupDetectKey(req, res, url) {
|
|
701
|
+
// Detect API credentials from external sources (NOT .env — that's what we're trying to populate).
|
|
702
|
+
// Provider-aware: ?provider=openai|google|anthropic. Defaults to anthropic for back-compat.
|
|
703
|
+
const detectProvider = _deps.sanitizeSetupProviderType(url.searchParams.get('provider') || 'anthropic') || 'anthropic';
|
|
704
|
+
|
|
705
|
+
// Helper: read a shell env var via the user's login shell so we get values
|
|
706
|
+
// sourced from .zshrc / .bashrc / etc., not just our parent process.
|
|
707
|
+
const readShellEnv = (varName) => {
|
|
708
|
+
return _deps.readUserShellEnv(varName, 6);
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
// ── OpenAI branch: shell env OPENAI_API_KEY → fallback to codex CLI suggestion
|
|
712
|
+
if (detectProvider === 'openai') {
|
|
713
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
714
|
+
const openaiKey = readShellEnv('OPENAI_API_KEY');
|
|
715
|
+
if (openaiKey && openaiKey.startsWith('sk-')) {
|
|
716
|
+
return respond({ found: true, key: openaiKey, source: 'shell environment (OPENAI_API_KEY)', provider: 'openai' });
|
|
717
|
+
}
|
|
718
|
+
// No API key — probe codex CLI as a Strategy-2 (subscription) suggestion.
|
|
719
|
+
try {
|
|
720
|
+
if (!_deps.hostIdentityAccessAllowed({ configDir: _deps.CONFIG_DIR })) throw new Error('host identity access not allowed');
|
|
721
|
+
const proc = require('child_process').spawnSync(_deps.resolveAgentCliCommand('codex'), ['login', 'status'], { encoding: 'utf8', timeout: 8000 });
|
|
722
|
+
if (proc.status === 0) {
|
|
723
|
+
const stdout = (proc.stdout || '').trim();
|
|
724
|
+
// Output is freeform like "Logged in using ChatGPT". Surface presence + try to parse account email.
|
|
725
|
+
const emailMatch = stdout.match(/[\w.+-]+@[\w-]+\.[\w.-]+/);
|
|
726
|
+
let plan = null;
|
|
727
|
+
try {
|
|
728
|
+
const auth = JSON.parse(require('fs').readFileSync(_deps.codexPaths.codexAuthPath(), 'utf8'));
|
|
729
|
+
const accessToken = auth?.tokens?.access_token;
|
|
730
|
+
if (accessToken) {
|
|
731
|
+
const parts = accessToken.split('.');
|
|
732
|
+
if (parts.length >= 2) {
|
|
733
|
+
const pad = '='.repeat((-parts[1].length) & 3);
|
|
734
|
+
const payload = JSON.parse(Buffer.from(parts[1] + pad, 'base64url').toString('utf8'));
|
|
735
|
+
plan = payload?.['https://api.openai.com/auth']?.chatgpt_plan_type || null;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
} catch { /* best-effort */ }
|
|
739
|
+
return respond({
|
|
740
|
+
found: false,
|
|
741
|
+
provider: 'openai',
|
|
742
|
+
codex_available: true,
|
|
743
|
+
codex_status: stdout,
|
|
744
|
+
codex_email: emailMatch ? emailMatch[0] : null,
|
|
745
|
+
codex_plan: plan,
|
|
746
|
+
hint: 'No OPENAI_API_KEY in shell env. Codex CLI is logged in — use it for subscription billing instead.',
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
} catch { /* codex not on PATH — fine */ }
|
|
750
|
+
return respond({ found: false, provider: 'openai', hint: 'No OPENAI_API_KEY in shell env. Get a key at platform.openai.com OR install Codex CLI from chatgpt.com/codex' });
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// ── DeepSeek branch: shell env DEEPSEEK_API_KEY only (no CLI subscription path).
|
|
754
|
+
if (detectProvider === 'deepseek') {
|
|
755
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
756
|
+
const dsKey = readShellEnv('DEEPSEEK_API_KEY');
|
|
757
|
+
if (dsKey && dsKey.startsWith('sk-')) {
|
|
758
|
+
return respond({ found: true, key: dsKey, source: 'shell environment (DEEPSEEK_API_KEY)', provider: 'deepseek' });
|
|
759
|
+
}
|
|
760
|
+
return respond({ found: false, provider: 'deepseek', hint: 'No DEEPSEEK_API_KEY in shell env. Get a key at platform.deepseek.com (prepaid — top up first).' });
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ── Moonshot / Kimi branch: shell env MOONSHOT_API_KEY only.
|
|
764
|
+
if (detectProvider === 'moonshot') {
|
|
765
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
766
|
+
const moonshotKey = readShellEnv('MOONSHOT_API_KEY');
|
|
767
|
+
if (moonshotKey) {
|
|
768
|
+
return respond({ found: true, key: moonshotKey, source: 'shell environment (MOONSHOT_API_KEY)', provider: 'moonshot' });
|
|
769
|
+
}
|
|
770
|
+
return respond({ found: false, provider: 'moonshot', hint: 'No MOONSHOT_API_KEY in shell env. Get a key from the Kimi Open Platform console.' });
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ── Z.AI branch: shell env ZAI_API_KEY only.
|
|
774
|
+
if (detectProvider === 'zai') {
|
|
775
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
776
|
+
const zaiKey = readShellEnv('ZAI_API_KEY');
|
|
777
|
+
if (zaiKey) {
|
|
778
|
+
return respond({ found: true, key: zaiKey, source: 'shell environment (ZAI_API_KEY)', provider: 'zai' });
|
|
779
|
+
}
|
|
780
|
+
return respond({ found: false, provider: 'zai', hint: 'No ZAI_API_KEY in shell env. Get a key from the Z.AI Open Platform.' });
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ── Google branch: shell env GOOGLE_API_KEY / GEMINI_API_KEY → fallback to Gemini CLI OAuth
|
|
784
|
+
if (detectProvider === 'google') {
|
|
785
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
786
|
+
const googleKey = readShellEnv('GOOGLE_API_KEY') || readShellEnv('GEMINI_API_KEY');
|
|
787
|
+
if (googleKey && !googleKey.startsWith('ya29.')) {
|
|
788
|
+
const which = readShellEnv('GOOGLE_API_KEY') ? 'GOOGLE_API_KEY' : 'GEMINI_API_KEY';
|
|
789
|
+
return respond({ found: true, key: googleKey, source: `shell environment (${which})`, provider: 'google' });
|
|
790
|
+
}
|
|
791
|
+
// Fallback to Gemini CLI's OAuth file (existing /api/setup/detect-gemini-cli handles this richly).
|
|
792
|
+
return respond({ found: false, provider: 'google', hint: 'No GOOGLE_API_KEY/GEMINI_API_KEY in shell env. Use the Gemini CLI auto-detection if installed, or get a key at aistudio.google.com/apikey' });
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// ── Anthropic branch: existing logic (devbox / Keychain / shell env). Falls through.
|
|
796
|
+
let key = '';
|
|
797
|
+
let source = '';
|
|
798
|
+
let gateway = null;
|
|
799
|
+
|
|
800
|
+
// 1. Devbox corporate gateway (reads auth headers file directly)
|
|
801
|
+
if (process.platform === 'darwin') {
|
|
802
|
+
try {
|
|
803
|
+
const authFile = _deps.path.join(_deps.serverConfig.HOME, '.devbox', 'secrets', 'claude', 'auth_headers');
|
|
804
|
+
const headers = _deps.fs.readFileSync(authFile, 'utf8').trim();
|
|
805
|
+
if (headers) {
|
|
806
|
+
let baseUrl = '';
|
|
807
|
+
try {
|
|
808
|
+
const claudeScript = _deps.fs.readFileSync(_deps.path.join(_deps.serverConfig.HOME, '.devbox', 'ai', 'claude', 'claude'), 'utf8');
|
|
809
|
+
const m = claudeScript.match(/VPN_CHECK_URL="(https?:\/\/[^"]+)"/);
|
|
810
|
+
if (m) baseUrl = m[1] + '/v1';
|
|
811
|
+
} catch {}
|
|
812
|
+
if (baseUrl) {
|
|
813
|
+
gateway = {
|
|
814
|
+
base_url: baseUrl,
|
|
815
|
+
auth_token: 'sk-ant-api03-unused',
|
|
816
|
+
custom_headers_b64: Buffer.from(headers).toString('base64'),
|
|
817
|
+
};
|
|
818
|
+
source = 'Claude Code devbox (' + baseUrl.replace(/https?:\/\//, '').split('/')[0] + ')';
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
} catch {}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// 2. Claude Code OAuth token from macOS Keychain (checked before shell env — always freshest)
|
|
825
|
+
if (!gateway && !key && process.platform === 'darwin' && _deps.hostIdentityAccessAllowed({ configDir: _deps.CONFIG_DIR })) {
|
|
826
|
+
try {
|
|
827
|
+
const credJson = _deps.execFileSync('security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], { encoding: 'utf8', timeout: 3000 }).trim();
|
|
828
|
+
if (credJson) {
|
|
829
|
+
const cred = JSON.parse(credJson);
|
|
830
|
+
const oauth = cred.claudeAiOauth;
|
|
831
|
+
if (oauth && oauth.accessToken) {
|
|
832
|
+
const expired = oauth.expiresAt && oauth.expiresAt < Date.now();
|
|
833
|
+
if (!expired) {
|
|
834
|
+
key = oauth.accessToken;
|
|
835
|
+
source = 'Claude Code (OAuth)';
|
|
836
|
+
} else {
|
|
837
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
838
|
+
res.end(JSON.stringify({ found: false, claude_code_expired: true, hint: 'Found Claude Code, but the OAuth token has expired. Run "claude" in your terminal once to refresh it, then click Detect again.' }));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
} catch {}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// 3. Shell environment (fallback — may be stale if sourced from a previous .env)
|
|
847
|
+
if (!gateway && !key) {
|
|
848
|
+
try {
|
|
849
|
+
const result = _deps.readUserShellEnv('ANTHROPIC_API_KEY', 6);
|
|
850
|
+
if (result && result.length > 5 && result !== 'sk-ant-api03-unused') {
|
|
851
|
+
key = result;
|
|
852
|
+
source = 'shell environment';
|
|
853
|
+
}
|
|
854
|
+
} catch {}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (!gateway && !key) {
|
|
858
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
859
|
+
res.end(JSON.stringify({ found: false, hint: 'No API key found. Checked: Claude Code keychain, shell environment, devbox auth. Enter your key manually or get one at console.anthropic.com' }));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Probe API to discover the best model accessible with this key
|
|
864
|
+
const probeKey = key || (gateway ? gateway.auth_token : '');
|
|
865
|
+
const probeBase = gateway ? gateway.base_url : 'https://api.anthropic.com';
|
|
866
|
+
const probeHeaders = gateway ? gateway.custom_headers_b64 : null;
|
|
867
|
+
_deps.probeModel(probeKey, probeBase, probeHeaders).then(({ model: detectedModel }) => {
|
|
868
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
869
|
+
if (gateway) {
|
|
870
|
+
res.end(JSON.stringify({ found: true, gateway, source, ...(detectedModel ? { model: detectedModel } : {}) }));
|
|
871
|
+
} else {
|
|
872
|
+
res.end(JSON.stringify({ found: true, key, source, ...(detectedModel ? { model: detectedModel } : {}) }));
|
|
873
|
+
}
|
|
874
|
+
}).catch(() => {
|
|
875
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
876
|
+
if (gateway) {
|
|
877
|
+
res.end(JSON.stringify({ found: true, gateway, source }));
|
|
878
|
+
} else {
|
|
879
|
+
res.end(JSON.stringify({ found: true, key, source }));
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
async function apiSetupSave(req, res, url) {
|
|
886
|
+
let body = '';
|
|
887
|
+
let bodyLen = 0;
|
|
888
|
+
req.on('data', c => {
|
|
889
|
+
bodyLen += c.length;
|
|
890
|
+
if (bodyLen > 8192) { req.destroy(); return; } // 8KB limit
|
|
891
|
+
body += c;
|
|
892
|
+
});
|
|
893
|
+
req.on('end', async () => {
|
|
894
|
+
try {
|
|
895
|
+
const data = JSON.parse(body);
|
|
896
|
+
// Validate and sanitize inputs
|
|
897
|
+
const ownerName = typeof data.owner_name === 'string'
|
|
898
|
+
? data.owner_name.replace(/[\r\n=]/g, '').trim().slice(0, 200)
|
|
899
|
+
: '';
|
|
900
|
+
const apiKey = typeof data.api_key === 'string'
|
|
901
|
+
? data.api_key.replace(/[\r\n\s]/g, '').slice(0, 200)
|
|
902
|
+
: '';
|
|
903
|
+
// Gateway config (corporate Portkey/cybertron setups) — sanitize values
|
|
904
|
+
let gw = null;
|
|
905
|
+
if (data.gateway && data.gateway.base_url) {
|
|
906
|
+
gw = {
|
|
907
|
+
base_url: String(data.gateway.base_url).replace(/[\r\n]/g, '').slice(0, 500),
|
|
908
|
+
auth_token: String(data.gateway.auth_token || 'sk-ant-api03-unused').replace(/[\r\n]/g, '').slice(0, 500),
|
|
909
|
+
custom_headers_b64: String(data.gateway.custom_headers_b64 || '').replace(/[\r\n\s]/g, '').slice(0, 2000),
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
const walleModel = typeof data.model === 'string'
|
|
913
|
+
? data.model.replace(/[\r\n\s]/g, '').slice(0, 100)
|
|
914
|
+
: '';
|
|
915
|
+
const walleProvider = _deps.sanitizeSetupProviderType(data.provider);
|
|
916
|
+
if (walleProvider && !_deps.SETUP_PROVIDER_TYPES.includes(walleProvider)) {
|
|
917
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
918
|
+
res.end(JSON.stringify({ error: 'Invalid provider type. Must be one of: ' + _deps.setupProviderTypeList() }));
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const providerForApiKey = walleProvider || 'anthropic';
|
|
922
|
+
// Anthropic auth_method picker: 'api_key' (default), 'claude_cli', or
|
|
923
|
+
// 'oauth_proxy'. Anything else gets normalized to '' (= cleared).
|
|
924
|
+
// OpenAI auth_method picker: 'api_key' (default) or 'codex_cli'.
|
|
925
|
+
const anthropicAuthMethods = new Set(['api_key', 'claude_cli', 'oauth_proxy']);
|
|
926
|
+
const openaiAuthMethods = new Set(['api_key', 'codex_cli']);
|
|
927
|
+
const validForProvider = walleProvider === 'anthropic'
|
|
928
|
+
? anthropicAuthMethods
|
|
929
|
+
: walleProvider === 'openai'
|
|
930
|
+
? openaiAuthMethods
|
|
931
|
+
: new Set();
|
|
932
|
+
const authMethod = validForProvider.has(data.auth_method) ? data.auth_method : '';
|
|
933
|
+
// coding_via_cli: route the coding agent through `claude -p` (subscription
|
|
934
|
+
// billing) even when the primary auth is api_key. Stored as
|
|
935
|
+
// WALLE_CODING_USE_CLI=true|false to match what coding-orchestrator reads.
|
|
936
|
+
const codingViaCli = walleProvider === 'anthropic' && data.coding_via_cli === true;
|
|
937
|
+
// Accept any non-empty key (Anthropic, Portkey, or other providers)
|
|
938
|
+
const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
|
|
939
|
+
const lines = [];
|
|
940
|
+
// Build set of keys to strip
|
|
941
|
+
const keysToReplace = new Set();
|
|
942
|
+
if (ownerName) keysToReplace.add('WALLE_OWNER_NAME');
|
|
943
|
+
// WALLE_AUTH_METHOD applies to anthropic (api_key/claude_cli/oauth_proxy)
|
|
944
|
+
// AND openai (api_key/codex_cli). WALLE_CODING_USE_CLI is anthropic-only.
|
|
945
|
+
if (walleProvider === 'anthropic' || walleProvider === 'openai') {
|
|
946
|
+
keysToReplace.add('WALLE_AUTH_METHOD');
|
|
947
|
+
}
|
|
948
|
+
if (walleProvider === 'anthropic') {
|
|
949
|
+
keysToReplace.add('WALLE_CODING_USE_CLI');
|
|
950
|
+
}
|
|
951
|
+
if (gw) {
|
|
952
|
+
// Gateway: strip Anthropic direct key vars
|
|
953
|
+
keysToReplace.add('ANTHROPIC_API_KEY');
|
|
954
|
+
keysToReplace.add('ANTHROPIC_BASE_URL');
|
|
955
|
+
keysToReplace.add('ANTHROPIC_AUTH_TOKEN');
|
|
956
|
+
keysToReplace.add('ANTHROPIC_CUSTOM_HEADERS_B64');
|
|
957
|
+
} else if (apiKey) {
|
|
958
|
+
// Only strip the key for the provider being configured — preserve others
|
|
959
|
+
if (providerForApiKey === 'openai') { keysToReplace.add('OPENAI_API_KEY'); }
|
|
960
|
+
else if (providerForApiKey === 'google') { keysToReplace.add('GOOGLE_API_KEY'); keysToReplace.add('GEMINI_API_KEY'); keysToReplace.add('GOOGLE_AUTH_MODE'); keysToReplace.add('GOOGLE_REFRESH_TOKEN'); }
|
|
961
|
+
else if (providerForApiKey === 'deepseek') { keysToReplace.add('DEEPSEEK_API_KEY'); }
|
|
962
|
+
else if (providerForApiKey === 'moonshot') { keysToReplace.add('MOONSHOT_API_KEY'); }
|
|
963
|
+
else if (providerForApiKey === 'zai') { keysToReplace.add('ZAI_API_KEY'); }
|
|
964
|
+
else if (providerForApiKey === 'anthropic') { keysToReplace.add('ANTHROPIC_API_KEY'); keysToReplace.add('ANTHROPIC_BASE_URL'); keysToReplace.add('ANTHROPIC_AUTH_TOKEN'); keysToReplace.add('ANTHROPIC_CUSTOM_HEADERS_B64'); }
|
|
965
|
+
}
|
|
966
|
+
// Read existing .env, keep lines that aren't being replaced
|
|
967
|
+
try {
|
|
968
|
+
const existing = _deps.fs.readFileSync(envPath, 'utf8');
|
|
969
|
+
for (const line of existing.split('\n')) {
|
|
970
|
+
const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
|
|
971
|
+
if (m && keysToReplace.has(m[1])) continue; // skip — will re-add below
|
|
972
|
+
lines.push(line);
|
|
973
|
+
}
|
|
974
|
+
} catch { lines.push('# Wall-E configuration'); lines.push(''); }
|
|
975
|
+
// Strip trailing blank lines
|
|
976
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
|
|
977
|
+
lines.push('');
|
|
978
|
+
// Add new values
|
|
979
|
+
if (ownerName) {
|
|
980
|
+
lines.push(`WALLE_OWNER_NAME=${ownerName}`);
|
|
981
|
+
_deps.serverConfig.override("WALLE_OWNER_NAME", ownerName, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
982
|
+
}
|
|
983
|
+
// WALLE_AUTH_METHOD persistence — applies to anthropic AND openai.
|
|
984
|
+
if (walleProvider === 'anthropic' || walleProvider === 'openai') {
|
|
985
|
+
if (authMethod) {
|
|
986
|
+
lines.push(`WALLE_AUTH_METHOD=${authMethod}`);
|
|
987
|
+
_deps.serverConfig.override("WALLE_AUTH_METHOD", authMethod, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
988
|
+
} else {
|
|
989
|
+
delete _deps.serverConfig.WALLE_AUTH_METHOD;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
// WALLE_CODING_USE_CLI is anthropic-only (claude -p override for the
|
|
993
|
+
// coding agent). Codex's equivalent path is auth_method=codex_cli itself.
|
|
994
|
+
if (walleProvider === 'anthropic') {
|
|
995
|
+
if (codingViaCli) {
|
|
996
|
+
lines.push(`WALLE_CODING_USE_CLI=true`);
|
|
997
|
+
_deps.serverConfig.override("WALLE_CODING_USE_CLI", 'true', 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
998
|
+
} else {
|
|
999
|
+
delete _deps.serverConfig.WALLE_CODING_USE_CLI;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (gw) {
|
|
1003
|
+
// Gateway setup: save gateway vars, clear direct API key
|
|
1004
|
+
lines.push(`ANTHROPIC_BASE_URL=${gw.base_url}`);
|
|
1005
|
+
lines.push(`ANTHROPIC_AUTH_TOKEN=${gw.auth_token}`);
|
|
1006
|
+
lines.push(`ANTHROPIC_CUSTOM_HEADERS_B64=${gw.custom_headers_b64}`);
|
|
1007
|
+
_deps.serverConfig.override("ANTHROPIC_BASE_URL", gw.base_url, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1008
|
+
_deps.serverConfig.override("ANTHROPIC_AUTH_TOKEN", gw.auth_token, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1009
|
+
_deps.serverConfig.override("ANTHROPIC_CUSTOM_HEADERS_B64", gw.custom_headers_b64, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1010
|
+
delete _deps.serverConfig.ANTHROPIC_API_KEY;
|
|
1011
|
+
} else if (apiKey) {
|
|
1012
|
+
// Save key under the correct provider env var
|
|
1013
|
+
if (providerForApiKey === 'openai') {
|
|
1014
|
+
lines.push(`OPENAI_API_KEY=${apiKey}`);
|
|
1015
|
+
_deps.serverConfig.override("OPENAI_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1016
|
+
} else if (providerForApiKey === 'google') {
|
|
1017
|
+
const googleAuthMode = typeof data.google_auth_mode === 'string' ? data.google_auth_mode.replace(/[^a-z]/g, '') : '';
|
|
1018
|
+
const googleRefreshToken = typeof data.google_refresh_token === 'string' ? data.google_refresh_token.replace(/[\r\n]/g, '').slice(0, 2000) : '';
|
|
1019
|
+
lines.push(`GOOGLE_API_KEY=${apiKey}`);
|
|
1020
|
+
_deps.serverConfig.override("GOOGLE_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1021
|
+
if (googleAuthMode === 'oauth' && googleRefreshToken) {
|
|
1022
|
+
lines.push(`GOOGLE_AUTH_MODE=oauth`);
|
|
1023
|
+
lines.push(`GOOGLE_REFRESH_TOKEN=${googleRefreshToken}`);
|
|
1024
|
+
_deps.serverConfig.override("GOOGLE_AUTH_MODE", 'oauth', 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1025
|
+
_deps.serverConfig.override("GOOGLE_REFRESH_TOKEN", googleRefreshToken, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1026
|
+
} else {
|
|
1027
|
+
delete _deps.serverConfig.GOOGLE_AUTH_MODE;
|
|
1028
|
+
delete _deps.serverConfig.GOOGLE_REFRESH_TOKEN;
|
|
1029
|
+
}
|
|
1030
|
+
} else if (providerForApiKey === 'deepseek') {
|
|
1031
|
+
lines.push(`DEEPSEEK_API_KEY=${apiKey}`);
|
|
1032
|
+
_deps.serverConfig.override("DEEPSEEK_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1033
|
+
} else if (providerForApiKey === 'moonshot') {
|
|
1034
|
+
lines.push(`MOONSHOT_API_KEY=${apiKey}`);
|
|
1035
|
+
_deps.serverConfig.override("MOONSHOT_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1036
|
+
} else if (providerForApiKey === 'zai') {
|
|
1037
|
+
lines.push(`ZAI_API_KEY=${apiKey}`);
|
|
1038
|
+
_deps.serverConfig.override("ZAI_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1039
|
+
} else if (providerForApiKey === 'anthropic') {
|
|
1040
|
+
// Default: Anthropic
|
|
1041
|
+
lines.push(`ANTHROPIC_API_KEY=${apiKey}`);
|
|
1042
|
+
_deps.serverConfig.override("ANTHROPIC_API_KEY", apiKey, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
1043
|
+
delete _deps.serverConfig.ANTHROPIC_BASE_URL;
|
|
1044
|
+
delete _deps.serverConfig.ANTHROPIC_AUTH_TOKEN;
|
|
1045
|
+
delete _deps.serverConfig.ANTHROPIC_CUSTOM_HEADERS_B64;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
_deps.atomicWriteFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
|
|
1049
|
+
_deps.setup.clearSetupCache(); // so next / request goes to dashboard
|
|
1050
|
+
_deps.telemetry.track('ctm_setup_save', { has_key: !!(apiKey || gw), has_model: !!walleModel });
|
|
1051
|
+
|
|
1052
|
+
// Persist provider config through Wall-E, which owns its brain DB.
|
|
1053
|
+
try {
|
|
1054
|
+
await _deps.walleClient.requestJson('/api/wall-e/setup/config', {
|
|
1055
|
+
method: 'POST',
|
|
1056
|
+
body: {
|
|
1057
|
+
provider: walleProvider,
|
|
1058
|
+
model: walleModel,
|
|
1059
|
+
ownerName,
|
|
1060
|
+
providerConfig: walleProvider ? {
|
|
1061
|
+
id: `${walleProvider}-default`,
|
|
1062
|
+
baseUrl: gw ? gw.base_url : null,
|
|
1063
|
+
apiKeyEncrypted: gw ? gw.auth_token : (apiKey || null),
|
|
1064
|
+
customHeaders: gw ? gw.custom_headers_b64 : null,
|
|
1065
|
+
} : null,
|
|
1066
|
+
},
|
|
1067
|
+
});
|
|
1068
|
+
} catch (dbErr) {
|
|
1069
|
+
console.error('[setup] Failed to persist Wall-E config:', dbErr.message);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Restart Wall-E so it picks up new config
|
|
1073
|
+
if (apiKey || gw || walleModel || walleProvider) {
|
|
1074
|
+
_deps.walleSupervisor.restartQuiet();
|
|
1075
|
+
}
|
|
1076
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1077
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1080
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// WP-5 shard S2: the three verbatim bodies above are appended here by
|
|
1087
|
+
// bin/move-only-check.js's proof (identical to server.js's declarations up to
|
|
1088
|
+
// the `_deps.` prefix).
|
|
1089
|
+
module.exports = { configure, register, apiSetupTestKey, apiSetupDetectKey, apiSetupSave };
|