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
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Landing-throughput WP-6 shard S15 (design doc §5 D-10 "move-only", §6 "Shard module
|
|
4
|
+
// seam"): the 17 setup-wizard HTTP handlers move out of server.js VERBATIM into this module
|
|
5
|
+
// behind the same configure(deps) seam as lib/api/setup.js (S2), lib/api/session-attach.js
|
|
6
|
+
// (S6), lib/walle-history-serve.js (S8), lib/codex-rollout-history.js (S9),
|
|
7
|
+
// lib/terminal-input-prepared.js (S10), lib/idle-notify.js (S11), lib/session-park.js (S12),
|
|
8
|
+
// lib/terminal-draft-operation.js (S13) and lib/conversation-freshness-diagnostics.js (S14).
|
|
9
|
+
// Bodies are identical to the server.js declarations except for the `_deps.` prefix on every
|
|
10
|
+
// identifier that used to resolve to a server.js top-level binding (D-10 rule 1), proven by
|
|
11
|
+
// `node bin/move-only-check.js`. Four inline requires were rebased to this module's own
|
|
12
|
+
// directory (S8 precedent: one move-only DIFF per rebased require -- the remainder is proven
|
|
13
|
+
// by re-running against a copy with the inverse sed applied).
|
|
14
|
+
//
|
|
15
|
+
// The module lives under lib/api/ (and NOT appended to lib/api/setup.js, which is already
|
|
16
|
+
// 1,089 lines: itself plus these 807 would cross the 1,500-line new-file ceiling). It
|
|
17
|
+
// registers no routes of its own: lib/api/setup.js keeps the route table and receives these
|
|
18
|
+
// 17 functions through its own configure() call in server.js.
|
|
19
|
+
//
|
|
20
|
+
// Injected deps, per function:
|
|
21
|
+
// apiSetupStatus: CONFIG_DIR, HOSTNAME, __dirname, _buildHostedRuntimeContext, _controlSnapshot, _effectiveStorageBackends, _runCtmDbWorkerOp, fs, getActiveWalleProviderState, os, path, resolveEnvFilePath, serverConfig, setup, walleClient
|
|
22
|
+
// apiSetupDetectCodexCli: codingAgentAuth, readUserShellEnv
|
|
23
|
+
// apiSetupDetectGeminiCli: CONFIG_DIR, fs, hostIdentityAccessAllowed, path, serverConfig
|
|
24
|
+
// apiSetupDetectClaudeCli: codingAgentAuth, resolveAgentCliCommand
|
|
25
|
+
// apiSetupOauthProxyStart: oauthProxySupervisor
|
|
26
|
+
// apiSetupOauthProxyStop: oauthProxySupervisor
|
|
27
|
+
// apiSetupOauthProxyStatus: oauthProxySupervisor
|
|
28
|
+
// apiSetupProviders: SETUP_PROVIDER_ENV_KEYS, SETUP_PROVIDER_NAMES, collapseSetupProviderInstances, getWalleModelProviders, getWalleStatusData, loggedSetupProviderDuplicateSignatures, normalizeModelOverviewProviderCredentialState, providerRuntimeStateFromRow, sanitizeSetupProviderType, telemetry
|
|
29
|
+
// apiSetupSaveProvider: CONFIG_DIR, SETUP_PROVIDER_ENV_KEYS, SETUP_PROVIDER_NAMES, SETUP_PROVIDER_TYPES, __dirname, atomicWriteFileSync, fs, oauthProxySupervisor, path, resolveEnvFilePath, sanitizeSetupProviderType, serverConfig, setup, setupProviderAnswers, setupProviderTypeList, walleOwnerWrite, walleSupervisor
|
|
30
|
+
// apiSetupTestProvider: claudeCliAccountError, codingAgentAuth, firstModelLabel, getProviderRuntimeStateFromWalle, isSetupProviderType, listWalleModelsViaOwner, probeAnthropicOauthProxy, sanitizeSetupProviderType, setupProviderAnswers, shouldRouteOllamaTestViaOwner
|
|
31
|
+
// apiSetupSetDefault: getWalleDefaultModelSelection, resolveSetupDefaultSelection, sanitizeSetupProviderType, setup, walleOwnerWrite
|
|
32
|
+
// apiSetupCheckDir: fs, path
|
|
33
|
+
// apiSetupEmbeddings: walleClient
|
|
34
|
+
// apiSetupEmbeddingsSwitch: readBody, walleClient
|
|
35
|
+
// apiSetupEmbeddingsPolicy: readBody, walleClient
|
|
36
|
+
// apiSetupEmbeddingsDelete: readBody, walleClient
|
|
37
|
+
// apiSetupSaveDirs: CONFIG_DIR, __dirname, _closeDbForProcessExit, _ctmDbClosingForProcessExit, _dbCloseOutcome, _drainCtmDbWorkers, _exitForLaunchdRestart, atomicWriteFileSync, createWalleBrainBackupForCopy, dbModule, fs, os, path, resolveEnvFilePath, serverConfig
|
|
38
|
+
//
|
|
39
|
+
// Mutable module state (D-10 rule 2 / S9 lesson 7): _ctmDbClosingForProcessExit is a server.js
|
|
40
|
+
// `let` that a moved body WRITES (apiSetupSaveDirs sets it before the launchd restart), so it is
|
|
41
|
+
// injected as a get/set PAIR -- never copied. Every other dep is a `const` binding, a function
|
|
42
|
+
// declaration or the CommonJS wrapper parameter, all passed by value.
|
|
43
|
+
// `__dirname` is injected although it is NOT a server.js top-level binding: it is the CommonJS
|
|
44
|
+
// wrapper parameter, so left bare in this file it would resolve to lib/api/ instead of server.js's
|
|
45
|
+
// own directory and silently change resolveEnvFilePath's repoRoot (D-10 rule 9, "behaviour: none
|
|
46
|
+
// changes"). It is passed by value, same as every immutable binding.
|
|
47
|
+
// Calls between the 17 moved functions stay direct (no `_deps.`); none references another.
|
|
48
|
+
|
|
49
|
+
let _deps = null;
|
|
50
|
+
|
|
51
|
+
function configure(deps = {}) {
|
|
52
|
+
_deps = deps;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function apiSetupStatus(req, res, url) {
|
|
56
|
+
const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
|
|
57
|
+
let hasApiKey = !!(_deps.serverConfig.ANTHROPIC_API_KEY || _deps.serverConfig.ANTHROPIC_BASE_URL);
|
|
58
|
+
let ownerName = _deps.serverConfig.WALLE_OWNER_NAME || '';
|
|
59
|
+
if (!hasApiKey) {
|
|
60
|
+
try {
|
|
61
|
+
const envContent = await _deps.fs.promises.readFile(envPath, 'utf8');
|
|
62
|
+
hasApiKey = /^ANTHROPIC_API_KEY=\S+/m.test(envContent);
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
let slackConnected = false;
|
|
66
|
+
let slackTeam = '';
|
|
67
|
+
try {
|
|
68
|
+
const tokPath = _deps.path.join(_deps.serverConfig.HOME, '.claude', 'wall-e-slack-token.json');
|
|
69
|
+
const tok = JSON.parse(await _deps.fs.promises.readFile(tokPath, 'utf8'));
|
|
70
|
+
// Check token exists, is non-empty, AND hasn't expired
|
|
71
|
+
if (tok.access_token && tok.access_token.length > 10) {
|
|
72
|
+
const expiresAt = tok.expiresAt || (tok.obtained_at ? tok.obtained_at + (tok.expires_in || 43200) * 1000 : 0);
|
|
73
|
+
slackConnected = !expiresAt || expiresAt > Date.now();
|
|
74
|
+
}
|
|
75
|
+
slackTeam = tok.team_name || '';
|
|
76
|
+
} catch {}
|
|
77
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
78
|
+
let version = '';
|
|
79
|
+
try { version = require('../../package.json').version; } catch {}
|
|
80
|
+
const ctmDataDir = require('../data-dir').getDataDir();
|
|
81
|
+
const walleDataDir = _deps.serverConfig.WALL_E_DATA_DIR || _deps.path.join(_deps.os.homedir(), '.walle', 'data');
|
|
82
|
+
let walleProvider = 'anthropic';
|
|
83
|
+
let walleModel = '';
|
|
84
|
+
let serviceAlerts = [];
|
|
85
|
+
let serviceAlertSummary = null;
|
|
86
|
+
try {
|
|
87
|
+
const upstream = await _deps.walleClient.requestJson('/api/wall-e/status');
|
|
88
|
+
const data = upstream.json?.data || {};
|
|
89
|
+
if (data.walle_provider) walleProvider = data.walle_provider;
|
|
90
|
+
if (data.walle_model) walleModel = data.walle_model;
|
|
91
|
+
if (data.owner && !ownerName) ownerName = data.owner;
|
|
92
|
+
if (typeof data.has_api_key === 'boolean') hasApiKey = hasApiKey || data.has_api_key;
|
|
93
|
+
serviceAlerts = Array.isArray(data.service_alerts) ? data.service_alerts : [];
|
|
94
|
+
if (data.service_alert_summary && typeof data.service_alert_summary === 'object') serviceAlertSummary = data.service_alert_summary;
|
|
95
|
+
} catch {}
|
|
96
|
+
// Also check env-based provider keys
|
|
97
|
+
if (!hasApiKey && walleProvider === 'openai' && _deps.serverConfig.OPENAI_API_KEY) hasApiKey = true;
|
|
98
|
+
if (!hasApiKey && walleProvider === 'google' && (_deps.serverConfig.GOOGLE_API_KEY || _deps.serverConfig.GEMINI_API_KEY)) hasApiKey = true;
|
|
99
|
+
if (!hasApiKey && walleProvider === 'deepseek' && _deps.serverConfig.DEEPSEEK_API_KEY) hasApiKey = true;
|
|
100
|
+
if (!hasApiKey && walleProvider === 'moonshot' && _deps.serverConfig.MOONSHOT_API_KEY) hasApiKey = true;
|
|
101
|
+
if (!hasApiKey && walleProvider === 'zai' && _deps.serverConfig.ZAI_API_KEY) hasApiKey = true;
|
|
102
|
+
if (!hasApiKey && (walleProvider === 'ollama' || walleProvider === 'mlx')) hasApiKey = true;
|
|
103
|
+
const providerState = _deps.getActiveWalleProviderState(walleProvider);
|
|
104
|
+
if (!hasApiKey && providerState.hasRuntimeAccess) hasApiKey = true;
|
|
105
|
+
const authMethod = providerState.authMethod || '';
|
|
106
|
+
const codingViaCli = _deps.serverConfig.WALLE_CODING_USE_CLI === 'true';
|
|
107
|
+
const deviceLabel = _deps._controlSnapshot.getSetting('ui_setup_device_label', 'Owner iPhone');
|
|
108
|
+
const {
|
|
109
|
+
instance,
|
|
110
|
+
sync,
|
|
111
|
+
workspaceBindings,
|
|
112
|
+
localEdge,
|
|
113
|
+
security,
|
|
114
|
+
hostedReadiness,
|
|
115
|
+
} = await _deps._buildHostedRuntimeContext();
|
|
116
|
+
const transcriptRetention = await require('../transcript-retention-check').collectTranscriptRetentionCheck({ runOwnerOp: _deps._runCtmDbWorkerOp });
|
|
117
|
+
res.end(JSON.stringify({ owner_name: ownerName, has_api_key: hasApiKey, slack_connected: slackConnected, slack_team: slackTeam, needs_setup: !hasApiKey && _deps.setup.needsSetup(), version, ctm_data_dir: ctmDataDir, walle_data_dir: walleDataDir, hostname: _deps.HOSTNAME, walle_model: walleModel, walle_provider: walleProvider, auth_method: authMethod, coding_via_cli: codingViaCli, service_alerts: serviceAlerts, service_alert_summary: serviceAlertSummary, device_label: deviceLabel, instance, storage_backend: { ..._deps._effectiveStorageBackends }, sync, workspaceBindings, localEdge, security, hostedReadiness, transcript_retention: transcriptRetention }));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function apiSetupDetectCodexCli(req, res, url) {
|
|
122
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
123
|
+
// The probe itself (which spawns `codex login status`, matches "logged in" on
|
|
124
|
+
// either stream, and reads plan/email from ~/.codex/auth.json) lives in
|
|
125
|
+
// lib/coding-agent-auth.js (WP-0).
|
|
126
|
+
const account = await _deps.codingAgentAuth.probeAccount('codex');
|
|
127
|
+
const loggedIn = account.signedIn === true;
|
|
128
|
+
// Also surface whether the user has a separate OPENAI_API_KEY (mode-mismatch nudge)
|
|
129
|
+
let alsoHasApiKey = false;
|
|
130
|
+
try {
|
|
131
|
+
const out = _deps.readUserShellEnv('OPENAI_API_KEY', 4);
|
|
132
|
+
alsoHasApiKey = !!(out && out.startsWith('sk-'));
|
|
133
|
+
} catch {}
|
|
134
|
+
respond({
|
|
135
|
+
found: account.reason !== 'cli_missing',
|
|
136
|
+
logged_in: loggedIn,
|
|
137
|
+
version: null, // `codex login status` prints no version; agent-versions owns that
|
|
138
|
+
email: account.email || null,
|
|
139
|
+
plan: account.plan || null,
|
|
140
|
+
also_has_api_key: alsoHasApiKey,
|
|
141
|
+
hint: loggedIn ? null
|
|
142
|
+
: (account.reason === 'cli_missing'
|
|
143
|
+
? 'codex CLI not on PATH. Install Codex from https://chatgpt.com/codex'
|
|
144
|
+
: (account.reason === 'probe_unavailable'
|
|
145
|
+
? 'codex login state is only probed by the primary instance'
|
|
146
|
+
: 'codex installed but not logged in — run `codex login`')),
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function apiSetupDetectGeminiCli(req, res, url) {
|
|
152
|
+
const GEMINI_CLI_CREDS = _deps.path.join(_deps.serverConfig.HOME, '.gemini', 'oauth_creds.json');
|
|
153
|
+
const GEMINI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com';
|
|
154
|
+
const GEMINI_CLIENT_SECRET = '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';
|
|
155
|
+
const respond = (data) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); };
|
|
156
|
+
|
|
157
|
+
// 1. Read Gemini CLI creds file
|
|
158
|
+
let creds;
|
|
159
|
+
try {
|
|
160
|
+
if (!_deps.hostIdentityAccessAllowed({ configDir: _deps.CONFIG_DIR })) throw new Error('host identity access not allowed');
|
|
161
|
+
creds = JSON.parse(_deps.fs.readFileSync(GEMINI_CLI_CREDS, 'utf8'));
|
|
162
|
+
} catch {
|
|
163
|
+
respond({ found: false, hint: 'Gemini CLI not installed. Get an API key from aistudio.google.com/apikey' });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!creds.refresh_token) {
|
|
167
|
+
respond({ found: false, hint: 'Gemini CLI creds found but no refresh token. Re-run "gemini auth login"' });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
(async () => {
|
|
172
|
+
// 2. Refresh the OAuth access token
|
|
173
|
+
let accessToken;
|
|
174
|
+
try {
|
|
175
|
+
const tokenResp = await fetch('https://oauth2.googleapis.com/token', {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
178
|
+
body: new URLSearchParams({
|
|
179
|
+
client_id: GEMINI_CLIENT_ID,
|
|
180
|
+
client_secret: GEMINI_CLIENT_SECRET,
|
|
181
|
+
refresh_token: creds.refresh_token,
|
|
182
|
+
grant_type: 'refresh_token',
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
const tokenData = await tokenResp.json();
|
|
186
|
+
if (!tokenData.access_token) throw new Error(tokenData.error_description || 'Token refresh failed');
|
|
187
|
+
accessToken = tokenData.access_token;
|
|
188
|
+
} catch (e) {
|
|
189
|
+
return respond({ found: true, error: 'Token refresh failed: ' + e.message });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 3. Find a GCP project and enable required APIs
|
|
193
|
+
const authHeaders = { 'Authorization': `Bearer ${accessToken}` };
|
|
194
|
+
let projectId;
|
|
195
|
+
try {
|
|
196
|
+
const projResp = await fetch('https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState%3AACTIVE&pageSize=10', {
|
|
197
|
+
headers: authHeaders,
|
|
198
|
+
});
|
|
199
|
+
const projData = await projResp.json();
|
|
200
|
+
projectId = projData.projects?.[0]?.projectId;
|
|
201
|
+
} catch {}
|
|
202
|
+
if (!projectId) {
|
|
203
|
+
return respond({ found: true, error: 'No active GCP project found. Get an API key from aistudio.google.com/apikey instead.' });
|
|
204
|
+
}
|
|
205
|
+
const projHeaders = { ...authHeaders, 'x-goog-user-project': projectId };
|
|
206
|
+
|
|
207
|
+
// Enable Generative Language API + API Keys API on the project
|
|
208
|
+
for (const svc of ['generativelanguage.googleapis.com', 'apikeys.googleapis.com']) {
|
|
209
|
+
try {
|
|
210
|
+
await fetch(`https://serviceusage.googleapis.com/v1/projects/${projectId}/services/${svc}:enable`, {
|
|
211
|
+
method: 'POST', headers: authHeaders,
|
|
212
|
+
});
|
|
213
|
+
} catch {}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 4. Create a Gemini API key (permanent — no refresh needed)
|
|
217
|
+
let apiKey;
|
|
218
|
+
try {
|
|
219
|
+
// Check for existing Wall-E key first
|
|
220
|
+
const listResp = await fetch(`https://apikeys.googleapis.com/v2/projects/${projectId}/locations/global/keys`, {
|
|
221
|
+
headers: projHeaders,
|
|
222
|
+
});
|
|
223
|
+
const listData = await listResp.json();
|
|
224
|
+
const existing = (listData.keys || []).find(k => k.displayName === 'Wall-E Gemini Key (auto-created)');
|
|
225
|
+
if (existing) {
|
|
226
|
+
// Re-fetch with keyString
|
|
227
|
+
const getResp = await fetch(`https://apikeys.googleapis.com/v2/${existing.name}/keyString`, {
|
|
228
|
+
headers: projHeaders,
|
|
229
|
+
});
|
|
230
|
+
const getData = await getResp.json();
|
|
231
|
+
apiKey = getData.keyString;
|
|
232
|
+
}
|
|
233
|
+
if (!apiKey) {
|
|
234
|
+
// Create new key restricted to Gemini API
|
|
235
|
+
const createResp = await fetch(`https://apikeys.googleapis.com/v2/projects/${projectId}/locations/global/keys`, {
|
|
236
|
+
method: 'POST', headers: { ...projHeaders, 'Content-Type': 'application/json' },
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
displayName: 'Wall-E Gemini Key (auto-created)',
|
|
239
|
+
restrictions: { apiTargets: [{ service: 'generativelanguage.googleapis.com' }] },
|
|
240
|
+
}),
|
|
241
|
+
});
|
|
242
|
+
const createData = await createResp.json();
|
|
243
|
+
if (createData.error) throw new Error(createData.error.message);
|
|
244
|
+
// Poll for completion (usually instant)
|
|
245
|
+
const opName = createData.name;
|
|
246
|
+
for (let i = 0; i < 10; i++) {
|
|
247
|
+
const opResp = await fetch(`https://apikeys.googleapis.com/v2/${opName}`, { headers: projHeaders });
|
|
248
|
+
const opData = await opResp.json();
|
|
249
|
+
if (opData.done) {
|
|
250
|
+
// GCP may redact keyString in the LRO response — fetch it via GetKeyString endpoint
|
|
251
|
+
if (opData.response?.keyString) {
|
|
252
|
+
apiKey = opData.response.keyString;
|
|
253
|
+
} else if (opData.response?.name) {
|
|
254
|
+
const ksResp = await fetch(`https://apikeys.googleapis.com/v2/${opData.response.name}/keyString`, { headers: projHeaders });
|
|
255
|
+
const ksData = await ksResp.json();
|
|
256
|
+
apiKey = ksData.keyString;
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
if (opData.error) throw new Error(opData.error.message);
|
|
261
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch (e) {
|
|
265
|
+
return respond({ found: true, error: 'Failed to create API key: ' + e.message + '. Get one manually from aistudio.google.com/apikey' });
|
|
266
|
+
}
|
|
267
|
+
if (!apiKey) {
|
|
268
|
+
return respond({ found: true, error: 'API key creation timed out. Try again or get a key from aistudio.google.com/apikey' });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 5. Test the API key against Gemini API
|
|
272
|
+
try {
|
|
273
|
+
const testResp = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}&pageSize=1`);
|
|
274
|
+
if (testResp.ok) {
|
|
275
|
+
return respond({
|
|
276
|
+
found: true, source: 'Gemini CLI (auto-created API key)',
|
|
277
|
+
api_key: apiKey,
|
|
278
|
+
model: 'gemini-2.5-flash',
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
const errBody = await testResp.json().catch(() => ({}));
|
|
282
|
+
const errMsg = errBody?.error?.message || `HTTP ${testResp.status}`;
|
|
283
|
+
return respond({ found: true, error: errMsg });
|
|
284
|
+
} catch (e) {
|
|
285
|
+
return respond({ found: true, error: e.message });
|
|
286
|
+
}
|
|
287
|
+
})().catch(e => respond({ found: false, error: e.message }));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function apiSetupDetectClaudeCli(req, res, url) {
|
|
292
|
+
const respond = (data) => {
|
|
293
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
294
|
+
res.end(JSON.stringify(data));
|
|
295
|
+
};
|
|
296
|
+
const { spawn } = require('child_process');
|
|
297
|
+
let proc;
|
|
298
|
+
try {
|
|
299
|
+
// spawn (not exec) to avoid shell injection. Looks up `claude` on PATH;
|
|
300
|
+
// returns ENOENT if not installed.
|
|
301
|
+
proc = spawn(_deps.resolveAgentCliCommand('claude'), ['--version'], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 });
|
|
302
|
+
} catch (err) {
|
|
303
|
+
return respond({ found: false, hint: 'Could not spawn claude: ' + err.message });
|
|
304
|
+
}
|
|
305
|
+
let stdout = '';
|
|
306
|
+
let stderr = '';
|
|
307
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
308
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
309
|
+
proc.on('error', (err) => {
|
|
310
|
+
respond({
|
|
311
|
+
found: false,
|
|
312
|
+
hint: err.code === 'ENOENT'
|
|
313
|
+
? 'claude CLI not on PATH. Install Claude Code from https://claude.com/claude-code, then run `claude login`.'
|
|
314
|
+
: 'claude failed: ' + err.message,
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
proc.on('close', (code) => {
|
|
318
|
+
if (code !== 0) {
|
|
319
|
+
return respond({
|
|
320
|
+
found: false,
|
|
321
|
+
hint: 'claude --version exited ' + code + (stderr ? ': ' + stderr.slice(0, 200) : ''),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// claude --version prints something like "2.1.119 (Claude Code)" — pull the first version-shaped token.
|
|
325
|
+
const versionMatch = stdout.match(/\d+\.\d+\.\d+/);
|
|
326
|
+
// WP-0: the login facts come from `claude auth status` (lib/coding-agent-auth).
|
|
327
|
+
_deps.codingAgentAuth.probeAccount('claude-code').then((account) => {
|
|
328
|
+
respond({
|
|
329
|
+
found: true,
|
|
330
|
+
version: versionMatch ? versionMatch[0] : stdout.trim().slice(0, 40),
|
|
331
|
+
logged_in: account.signedIn,
|
|
332
|
+
email: account.email || null,
|
|
333
|
+
plan: account.plan || null,
|
|
334
|
+
});
|
|
335
|
+
}, (error) => {
|
|
336
|
+
// HANDLE: probeAccount resolves rather than rejects; this keeps a thrown promise from hanging the response.
|
|
337
|
+
respond({
|
|
338
|
+
found: true,
|
|
339
|
+
version: versionMatch ? versionMatch[0] : stdout.trim().slice(0, 40),
|
|
340
|
+
logged_in: null,
|
|
341
|
+
email: null,
|
|
342
|
+
plan: null,
|
|
343
|
+
hint: 'claude login probe failed: ' + ((error && error.message) || 'unknown error'),
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function apiSetupOauthProxyStart(req, res, url) {
|
|
351
|
+
const out = _deps.oauthProxySupervisor.start();
|
|
352
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
353
|
+
res.end(JSON.stringify(out));
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function apiSetupOauthProxyStop(req, res, url) {
|
|
358
|
+
const out = _deps.oauthProxySupervisor.stop();
|
|
359
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
360
|
+
res.end(JSON.stringify(out));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function apiSetupOauthProxyStatus(req, res, url) {
|
|
365
|
+
const out = await _deps.oauthProxySupervisor.status();
|
|
366
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
367
|
+
res.end(JSON.stringify(out));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function apiSetupProviders(req, res, url) {
|
|
372
|
+
try {
|
|
373
|
+
const [providersSettled, statusSettled] = await Promise.allSettled([
|
|
374
|
+
_deps.getWalleModelProviders(2500),
|
|
375
|
+
_deps.getWalleStatusData(2500),
|
|
376
|
+
]);
|
|
377
|
+
const rows = providersSettled.status === 'fulfilled' ? providersSettled.value : [];
|
|
378
|
+
const status = statusSettled.status === 'fulfilled' ? statusSettled.value : {};
|
|
379
|
+
const defaultProvider = _deps.sanitizeSetupProviderType(status.walle_provider || 'anthropic') || 'anthropic';
|
|
380
|
+
const defaultModel = status.walle_model || '';
|
|
381
|
+
const providerInstances = rows.map(row => {
|
|
382
|
+
const envKey = _deps.SETUP_PROVIDER_ENV_KEYS[row.type];
|
|
383
|
+
const runtime = _deps.providerRuntimeStateFromRow(row.type, row);
|
|
384
|
+
const authMethod = runtime.authMethod || 'api_key';
|
|
385
|
+
const hasStoredKey = runtime.hasStoredKey;
|
|
386
|
+
const hasKey = runtime.hasRuntimeAccess || (envKey ? !!process.env[envKey] : false);
|
|
387
|
+
const credentialState = _deps.normalizeModelOverviewProviderCredentialState({
|
|
388
|
+
...row,
|
|
389
|
+
auth_method: authMethod,
|
|
390
|
+
has_key: hasStoredKey,
|
|
391
|
+
has_stored_key: hasStoredKey,
|
|
392
|
+
});
|
|
393
|
+
let status = 'unknown', lastTested = null, error = null;
|
|
394
|
+
return {
|
|
395
|
+
id: row.id, type: row.type, name: _deps.SETUP_PROVIDER_NAMES[row.type] || row.name,
|
|
396
|
+
enabled: !!row.enabled, is_default: row.type === defaultProvider,
|
|
397
|
+
has_key: hasKey, model: row.type === defaultProvider ? (defaultModel || null) : null,
|
|
398
|
+
has_stored_key: hasStoredKey,
|
|
399
|
+
has_api_key: hasKey,
|
|
400
|
+
has_runtime_access: hasKey,
|
|
401
|
+
credential_set: hasKey,
|
|
402
|
+
credential_status: hasKey ? credentialState.credential_status : 'missing',
|
|
403
|
+
credential_source: credentialState.credential_source || (hasKey ? 'runtime' : ''),
|
|
404
|
+
base_url: row.base_url || null,
|
|
405
|
+
auth_method: authMethod,
|
|
406
|
+
status, last_tested: lastTested, error,
|
|
407
|
+
created_at: row.created_at,
|
|
408
|
+
updated_at: row.updated_at,
|
|
409
|
+
};
|
|
410
|
+
});
|
|
411
|
+
const { providers, duplicateDiagnostics, invalidProviderCount } = _deps.collapseSetupProviderInstances(providerInstances, { defaultProvider });
|
|
412
|
+
if (duplicateDiagnostics.length || invalidProviderCount) {
|
|
413
|
+
const signature = JSON.stringify({ duplicateDiagnostics, invalidProviderCount });
|
|
414
|
+
if (!_deps.loggedSetupProviderDuplicateSignatures.has(signature)) {
|
|
415
|
+
_deps.loggedSetupProviderDuplicateSignatures.add(signature);
|
|
416
|
+
console.warn('[setup] Collapsed duplicate provider instances for setup health:', {
|
|
417
|
+
duplicateDiagnostics,
|
|
418
|
+
invalidProviderCount,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
_deps.telemetry.track('setup_provider_instances_collapsed', {
|
|
423
|
+
duplicate_types: duplicateDiagnostics.map((item) => item.type).join(','),
|
|
424
|
+
duplicate_groups: duplicateDiagnostics.length,
|
|
425
|
+
invalid_provider_count: invalidProviderCount,
|
|
426
|
+
});
|
|
427
|
+
} catch {}
|
|
428
|
+
}
|
|
429
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
430
|
+
res.end(JSON.stringify({ providers, default_provider: defaultProvider, duplicate_providers: duplicateDiagnostics }));
|
|
431
|
+
} catch (e) {
|
|
432
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
433
|
+
res.end(JSON.stringify({ providers: [], default_provider: null, error: e.message }));
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async function apiSetupSaveProvider(req, res, url) {
|
|
439
|
+
let body = '';
|
|
440
|
+
let bodyLen = 0;
|
|
441
|
+
req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
|
|
442
|
+
req.on('end', async () => {
|
|
443
|
+
try {
|
|
444
|
+
const data = JSON.parse(body);
|
|
445
|
+
const type = _deps.sanitizeSetupProviderType(data.type);
|
|
446
|
+
if (!_deps.SETUP_PROVIDER_TYPES.includes(type)) {
|
|
447
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
448
|
+
res.end(JSON.stringify({ error: 'Invalid provider type. Must be one of: ' + _deps.setupProviderTypeList() }));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const apiKey = typeof data.api_key === 'string' ? data.api_key.replace(/[\r\n\s]/g, '').slice(0, 200) : '';
|
|
452
|
+
const hasBaseUrl = Object.prototype.hasOwnProperty.call(data, 'base_url') || Object.prototype.hasOwnProperty.call(data, 'baseUrl');
|
|
453
|
+
const rawBaseUrl = typeof data.base_url === 'string' ? data.base_url : (typeof data.baseUrl === 'string' ? data.baseUrl : '');
|
|
454
|
+
const baseUrl = hasBaseUrl ? rawBaseUrl.replace(/[\r\n]/g, '').trim().slice(0, 500) : undefined;
|
|
455
|
+
const model = typeof data.model === 'string' ? data.model.replace(/[\r\n\s]/g, '').slice(0, 100) : '';
|
|
456
|
+
const enabled = data.enabled !== false;
|
|
457
|
+
const setDefault = !!data.set_default;
|
|
458
|
+
const VALID_AUTH_METHODS = new Set(['api_key', 'claude_cli', 'oauth_proxy', 'codex_cli', 'gemini_cli', 'devbox']);
|
|
459
|
+
const authMethod = typeof data.auth_method === 'string' && VALID_AUTH_METHODS.has(data.auth_method) ? data.auth_method : null;
|
|
460
|
+
|
|
461
|
+
const ownerBody = {
|
|
462
|
+
type,
|
|
463
|
+
name: _deps.SETUP_PROVIDER_NAMES[type] || type,
|
|
464
|
+
api_key: apiKey,
|
|
465
|
+
model,
|
|
466
|
+
enabled,
|
|
467
|
+
set_default: setDefault,
|
|
468
|
+
auth_method: authMethod,
|
|
469
|
+
};
|
|
470
|
+
if (hasBaseUrl) ownerBody.base_url = baseUrl || null;
|
|
471
|
+
if (typeof data.caCert === 'string' && data.caCert.trim()) ownerBody.caCert = data.caCert.trim();
|
|
472
|
+
|
|
473
|
+
const ownerWrite = await _deps.walleOwnerWrite('/api/wall-e/setup/provider', {
|
|
474
|
+
method: 'POST',
|
|
475
|
+
body: ownerBody,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Persist .env updates: API key (if provided), WALLE_AUTH_METHOD (if
|
|
479
|
+
// set for an auth-capable provider), and (set_default is Wall-E-owned, never written here). Note we run this branch even when no apiKey is
|
|
480
|
+
// present — auth_method=oauth_proxy (and similar CLI methods) do
|
|
481
|
+
// not require an API key but still need WALLE_AUTH_METHOD wired
|
|
482
|
+
// into the daemon's env so its provider factory routes correctly.
|
|
483
|
+
const writesEnv = !!(apiKey && _deps.SETUP_PROVIDER_ENV_KEYS[type])
|
|
484
|
+
|| (authMethod && (type === 'anthropic' || type === 'openai'));
|
|
485
|
+
if (writesEnv) {
|
|
486
|
+
const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
|
|
487
|
+
const envVar = _deps.SETUP_PROVIDER_ENV_KEYS[type];
|
|
488
|
+
const lines = [];
|
|
489
|
+
const keysToReplace = new Set();
|
|
490
|
+
if (apiKey && envVar) {
|
|
491
|
+
keysToReplace.add(envVar);
|
|
492
|
+
if (type === 'google') keysToReplace.add('GEMINI_API_KEY');
|
|
493
|
+
}
|
|
494
|
+
if (authMethod && (type === 'anthropic' || type === 'openai')) {
|
|
495
|
+
keysToReplace.add('WALLE_AUTH_METHOD');
|
|
496
|
+
}
|
|
497
|
+
// Auth methods that use keychain/CLI/proxy must NOT leave the old
|
|
498
|
+
// ANTHROPIC_API_KEY / OPENAI_API_KEY in .env — those env vars
|
|
499
|
+
// become a conflict source for child processes (e.g. `claude` CLI
|
|
500
|
+
// emits "Auth conflict: Both a token (claude.ai) and an API key
|
|
501
|
+
// (ANTHROPIC_API_KEY) are set" because both are present after
|
|
502
|
+
// we switch to oauth_proxy or claude_cli). Strip the auth env
|
|
503
|
+
// entirely on those methods so newly-spawned PTY sessions don't
|
|
504
|
+
// inherit them via process.env. Also clears the legacy gateway
|
|
505
|
+
// variables that piggyback on ANTHROPIC_*.
|
|
506
|
+
const KEYCHAIN_AUTH_METHODS = new Set(['oauth_proxy', 'claude_cli']);
|
|
507
|
+
const stripAnthropicEnv = type === 'anthropic' && authMethod && KEYCHAIN_AUTH_METHODS.has(authMethod);
|
|
508
|
+
if (stripAnthropicEnv) {
|
|
509
|
+
keysToReplace.add('ANTHROPIC_API_KEY');
|
|
510
|
+
keysToReplace.add('ANTHROPIC_AUTH_TOKEN');
|
|
511
|
+
keysToReplace.add('ANTHROPIC_BASE_URL');
|
|
512
|
+
keysToReplace.add('ANTHROPIC_CUSTOM_HEADERS_B64');
|
|
513
|
+
}
|
|
514
|
+
const stripOpenaiEnv = type === 'openai' && authMethod === 'codex_cli';
|
|
515
|
+
if (stripOpenaiEnv) {
|
|
516
|
+
keysToReplace.add('OPENAI_API_KEY');
|
|
517
|
+
keysToReplace.add('OPENAI_BASE_URL');
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
const existing = _deps.fs.readFileSync(envPath, 'utf8');
|
|
521
|
+
for (const line of existing.split('\n')) {
|
|
522
|
+
const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
|
|
523
|
+
if (m && keysToReplace.has(m[1])) continue;
|
|
524
|
+
lines.push(line);
|
|
525
|
+
}
|
|
526
|
+
} catch { lines.push('# Wall-E configuration'); lines.push(''); }
|
|
527
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
|
|
528
|
+
lines.push('');
|
|
529
|
+
if (apiKey && envVar) {
|
|
530
|
+
lines.push(`${envVar}=${apiKey}`);
|
|
531
|
+
_deps.serverConfig.override(envVar, apiKey, 'provider-api-key-save'); // WP-4: seam, never process.env directly
|
|
532
|
+
}
|
|
533
|
+
if (authMethod && (type === 'anthropic' || type === 'openai')) {
|
|
534
|
+
lines.push(`WALLE_AUTH_METHOD=${authMethod}`);
|
|
535
|
+
_deps.serverConfig.override("WALLE_AUTH_METHOD", authMethod, 'ctm-config-seam: migrated write site (WP-4 AC-4c)');
|
|
536
|
+
}
|
|
537
|
+
// Mirror the strip into process.env so newly-spawned PTY children
|
|
538
|
+
// don't inherit the conflicting vars. (The .env edit alone only
|
|
539
|
+
// affects FUTURE CTM boots; live process.env still has them.)
|
|
540
|
+
if (stripAnthropicEnv) {
|
|
541
|
+
delete _deps.serverConfig.ANTHROPIC_API_KEY;
|
|
542
|
+
delete _deps.serverConfig.ANTHROPIC_AUTH_TOKEN;
|
|
543
|
+
delete _deps.serverConfig.ANTHROPIC_BASE_URL;
|
|
544
|
+
delete _deps.serverConfig.ANTHROPIC_CUSTOM_HEADERS_B64;
|
|
545
|
+
}
|
|
546
|
+
if (stripOpenaiEnv) {
|
|
547
|
+
delete _deps.serverConfig.OPENAI_API_KEY;
|
|
548
|
+
delete process.env.OPENAI_BASE_URL;
|
|
549
|
+
}
|
|
550
|
+
_deps.atomicWriteFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// OAuth proxy lifecycle: when the user picks auth_method=oauth_proxy
|
|
554
|
+
// for Anthropic, start the local proxy so the daemon can route
|
|
555
|
+
// through it on its next restart. When they pick anything else AND
|
|
556
|
+
// we previously had it set, stop the proxy. Idempotent on both
|
|
557
|
+
// sides — supervisor returns alreadyRunning/alreadyStopped.
|
|
558
|
+
if (type === 'anthropic') {
|
|
559
|
+
try {
|
|
560
|
+
if (authMethod === 'oauth_proxy') {
|
|
561
|
+
_deps.oauthProxySupervisor.start();
|
|
562
|
+
} else if (authMethod && authMethod !== 'oauth_proxy') {
|
|
563
|
+
if (_deps.oauthProxySupervisor.isRunning()) _deps.oauthProxySupervisor.stop();
|
|
564
|
+
}
|
|
565
|
+
} catch (_) { /* supervisor errors don't block save */ }
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// WP-2 (delegated-implementation-workers-2026-09-24.html §5 D-4, §9 S-5): re-push
|
|
569
|
+
// the model-proxy route table -- best-effort, never blocks the save response.
|
|
570
|
+
_deps.oauthProxySupervisor.pushRoutesNow().catch(() => { /* expected: best-effort route re-push -- a proxy that is not running yet must never block a provider save */ });
|
|
571
|
+
|
|
572
|
+
// Sync model registry and restart
|
|
573
|
+
_deps.setup.clearSetupCache();
|
|
574
|
+
_deps.walleSupervisor.restartQuiet();
|
|
575
|
+
|
|
576
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
577
|
+
res.end(JSON.stringify(_deps.setupProviderAnswers.providerSaveBody(ownerWrite)));
|
|
578
|
+
} catch (e) {
|
|
579
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
580
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async function apiSetupTestProvider(req, res, url) {
|
|
587
|
+
let body = '';
|
|
588
|
+
let bodyLen = 0;
|
|
589
|
+
req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
|
|
590
|
+
req.on('end', async () => {
|
|
591
|
+
try {
|
|
592
|
+
const data = JSON.parse(body);
|
|
593
|
+
const type = _deps.sanitizeSetupProviderType(data.type);
|
|
594
|
+
const startMs = Date.now();
|
|
595
|
+
const rawBodyApiKey = typeof data.api_key === 'string' ? data.api_key : (typeof data.apiKey === 'string' ? data.apiKey : '');
|
|
596
|
+
const bodyApiKey = rawBodyApiKey
|
|
597
|
+
? rawBodyApiKey.replace(/[\r\n\s]/g, '').slice(0, 500)
|
|
598
|
+
: '';
|
|
599
|
+
const rawBaseUrl = typeof data.base_url === 'string' ? data.base_url : (typeof data.baseUrl === 'string' ? data.baseUrl : '');
|
|
600
|
+
const bodyBaseUrl = rawBaseUrl.replace(/[\r\n]/g, '').trim().slice(0, 500);
|
|
601
|
+
const VALID_AUTH_METHODS = new Set(['api_key', 'claude_cli', 'oauth_proxy', 'codex_cli', 'gemini_cli', 'devbox']);
|
|
602
|
+
const bodyAuthMethod = typeof data.auth_method === 'string' && VALID_AUTH_METHODS.has(data.auth_method)
|
|
603
|
+
? data.auth_method
|
|
604
|
+
: null;
|
|
605
|
+
const bodyCaCert = typeof data.caCert === 'string' ? data.caCert.trim() : ''; // R-2: Test threads caCert like Save does
|
|
606
|
+
const respondOwnerProviderTest = async (providerType) => {
|
|
607
|
+
try {
|
|
608
|
+
const models = await _deps.listWalleModelsViaOwner({ provider: providerType, apiKey: bodyApiKey, baseUrl: bodyBaseUrl, authMethod: bodyAuthMethod, caCert: bodyCaCert || undefined });
|
|
609
|
+
const latency = Date.now() - startMs;
|
|
610
|
+
if (models.length) {
|
|
611
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
612
|
+
res.end(JSON.stringify({ ok: true, model: _deps.firstModelLabel(models, providerType), latency_ms: latency }));
|
|
613
|
+
} else {
|
|
614
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
615
|
+
res.end(JSON.stringify({ ok: false, error: 'No accessible model found' }));
|
|
616
|
+
}
|
|
617
|
+
} catch (e) {
|
|
618
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
619
|
+
res.end(JSON.stringify(_deps.setupProviderAnswers.providerTestFailureBody(e)));
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
if (type === 'anthropic') {
|
|
624
|
+
const runtime = await _deps.getProviderRuntimeStateFromWalle('anthropic');
|
|
625
|
+
const authMethod = bodyAuthMethod
|
|
626
|
+
? bodyAuthMethod
|
|
627
|
+
: runtime.authMethod || 'api_key';
|
|
628
|
+
if (authMethod === 'claude_cli') {
|
|
629
|
+
const account = await _deps.codingAgentAuth.probeAccount('claude-code');
|
|
630
|
+
const result = account.signedIn === true
|
|
631
|
+
? {
|
|
632
|
+
ok: true,
|
|
633
|
+
model: [account.email, account.plan].filter(Boolean).join(' · ') || 'claude.ai',
|
|
634
|
+
reply: account.authMethod || 'subscription',
|
|
635
|
+
}
|
|
636
|
+
: { ok: false, error: _deps.claudeCliAccountError(account) };
|
|
637
|
+
const latency = Date.now() - startMs;
|
|
638
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
639
|
+
res.end(JSON.stringify({ ...result, latency_ms: latency, auth_method: authMethod }));
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (authMethod === 'oauth_proxy') {
|
|
643
|
+
const result = await _deps.probeAnthropicOauthProxy();
|
|
644
|
+
const latency = Date.now() - startMs;
|
|
645
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
646
|
+
res.end(JSON.stringify({ ...result, latency_ms: latency, auth_method: authMethod }));
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
await respondOwnerProviderTest('anthropic');
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (type === 'ollama') {
|
|
654
|
+
// R-2: a remote/CA'd row routes via the owner-test path (honors baseUrl+caCert); default local keeps the raw probe below
|
|
655
|
+
if (_deps.shouldRouteOllamaTestViaOwner({ baseUrl: bodyBaseUrl, caCert: bodyCaCert })) {
|
|
656
|
+
await respondOwnerProviderTest('ollama');
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
const resp = await fetch('http://localhost:11434/api/tags', { signal: AbortSignal.timeout(5000) });
|
|
661
|
+
const latency = Date.now() - startMs;
|
|
662
|
+
if (resp.ok) {
|
|
663
|
+
const result = await resp.json();
|
|
664
|
+
const modelCount = result.models ? result.models.length : 0;
|
|
665
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
666
|
+
res.end(JSON.stringify({ ok: true, model: `${modelCount} model(s) available`, latency_ms: latency }));
|
|
667
|
+
} else {
|
|
668
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
669
|
+
res.end(JSON.stringify({ ok: false, error: `Ollama returned HTTP ${resp.status}` }));
|
|
670
|
+
}
|
|
671
|
+
} catch (e) {
|
|
672
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
673
|
+
res.end(JSON.stringify({ ok: false, error: 'Ollama not reachable at localhost:11434', diagnosis: 'Make sure Ollama is running (ollama serve)' }));
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (_deps.isSetupProviderType(type)) {
|
|
679
|
+
await respondOwnerProviderTest(type);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
684
|
+
res.end(JSON.stringify({ error: 'Unknown provider type: ' + type }));
|
|
685
|
+
} catch (e) {
|
|
686
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
687
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async function apiSetupSetDefault(req, res, url) {
|
|
694
|
+
let body = '';
|
|
695
|
+
let bodyLen = 0;
|
|
696
|
+
req.on('data', c => { bodyLen += c.length; if (bodyLen > 8192) { req.destroy(); return; } body += c; });
|
|
697
|
+
req.on('end', async () => {
|
|
698
|
+
try {
|
|
699
|
+
const data = JSON.parse(body);
|
|
700
|
+
const type = _deps.sanitizeSetupProviderType(data.type);
|
|
701
|
+
const currentDefault = await _deps.getWalleDefaultModelSelection();
|
|
702
|
+
const storedModel = currentDefault.model_provider === type ? currentDefault.model_id : '';
|
|
703
|
+
const selection = _deps.resolveSetupDefaultSelection({ type, model: data.model, storedModel });
|
|
704
|
+
if (!selection.ok) {
|
|
705
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
706
|
+
res.end(JSON.stringify({ error: selection.error }));
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const { requestedModel, targetModel } = selection;
|
|
711
|
+
const ownerWrite = await _deps.walleOwnerWrite('/api/wall-e/setup/default-provider', {
|
|
712
|
+
method: 'POST',
|
|
713
|
+
body: { type, requested_model: requestedModel, target_model: targetModel || '' },
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
_deps.setup.clearSetupCache();
|
|
717
|
+
const liveUpdated = ownerWrite.owner_write === 'wall-e-api';
|
|
718
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
719
|
+
res.end(JSON.stringify({ ok: true, live_updated: liveUpdated, restart_scheduled: false, owner_write: ownerWrite.owner_write }));
|
|
720
|
+
} catch (e) {
|
|
721
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
722
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async function apiSetupCheckDir(req, res, url) {
|
|
729
|
+
let body = '', bodyLen = 0;
|
|
730
|
+
req.on('data', c => { bodyLen += c.length; if (bodyLen > 4096) { req.destroy(); return; } body += c; });
|
|
731
|
+
req.on('end', () => {
|
|
732
|
+
try {
|
|
733
|
+
const data = JSON.parse(body);
|
|
734
|
+
const dir = (data.dir || '').replace(/[\r\n]/g, '').trim();
|
|
735
|
+
if (!dir) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'No directory' })); return; }
|
|
736
|
+
const exists = _deps.fs.existsSync(dir);
|
|
737
|
+
const hasCtmDb = _deps.fs.existsSync(_deps.path.join(dir, 'task-manager.db'));
|
|
738
|
+
const hasBrainDb = _deps.fs.existsSync(_deps.path.join(dir, 'wall-e-brain.db'));
|
|
739
|
+
const hasBackups = _deps.fs.existsSync(_deps.path.join(dir, 'backups'));
|
|
740
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
741
|
+
res.end(JSON.stringify({ exists, has_ctm_db: hasCtmDb, has_brain_db: hasBrainDb, has_backups: hasBackups }));
|
|
742
|
+
} catch (e) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
743
|
+
});
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async function apiSetupEmbeddings(req, res, url) {
|
|
748
|
+
try {
|
|
749
|
+
const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings');
|
|
750
|
+
res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
|
|
751
|
+
res.end(upstream.body || JSON.stringify({ providers: [], stats: {}, totalMemories: 0 }));
|
|
752
|
+
} catch (e) {
|
|
753
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
754
|
+
res.end(JSON.stringify({ providers: [], stats: {}, totalMemories: 0, error: e.message }));
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
async function apiSetupEmbeddingsSwitch(req, res, url) {
|
|
760
|
+
_deps.readBody(req).then(async (body) => {
|
|
761
|
+
const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/switch', { method: 'POST', body });
|
|
762
|
+
res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
|
|
763
|
+
res.end(upstream.body || JSON.stringify({ ok: false }));
|
|
764
|
+
}).catch((e) => {
|
|
765
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
766
|
+
res.end(JSON.stringify({ ok: false, message: e.message }));
|
|
767
|
+
});
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
async function apiSetupEmbeddingsPolicy(req, res, url) {
|
|
772
|
+
_deps.readBody(req).then(async (body) => {
|
|
773
|
+
const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/policy', { method: 'POST', body });
|
|
774
|
+
res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
|
|
775
|
+
res.end(upstream.body || JSON.stringify({ ok: false }));
|
|
776
|
+
}).catch((e) => {
|
|
777
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
778
|
+
res.end(JSON.stringify({ ok: false, message: e.message }));
|
|
779
|
+
});
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async function apiSetupEmbeddingsDelete(req, res, url) {
|
|
784
|
+
_deps.readBody(req).then(async (body) => {
|
|
785
|
+
const upstream = await _deps.walleClient.requestJson('/api/wall-e/setup/embeddings/delete', { method: 'POST', body });
|
|
786
|
+
res.writeHead(upstream.status || 200, { 'Content-Type': 'application/json' });
|
|
787
|
+
res.end(upstream.body || JSON.stringify({ ok: false }));
|
|
788
|
+
}).catch((e) => {
|
|
789
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
790
|
+
res.end(JSON.stringify({ ok: false, error: e.message }));
|
|
791
|
+
});
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async function apiSetupSaveDirs(req, res, url) {
|
|
796
|
+
let body = '', bodyLen = 0;
|
|
797
|
+
req.on('data', c => { bodyLen += c.length; if (bodyLen > 4096) { req.destroy(); return; } body += c; });
|
|
798
|
+
req.on('end', async () => {
|
|
799
|
+
try {
|
|
800
|
+
const data = JSON.parse(body);
|
|
801
|
+
const ctmDir = (data.ctm_data_dir || '').replace(/[\r\n]/g, '').trim();
|
|
802
|
+
const walleDir = (data.walle_data_dir || '').replace(/[\r\n]/g, '').trim();
|
|
803
|
+
const copyData = !!data.copy_data;
|
|
804
|
+
const envPath = _deps.resolveEnvFilePath({ repoRoot: _deps.path.resolve(_deps.__dirname, '..'), dataDir: _deps.CONFIG_DIR });
|
|
805
|
+
// Create directories
|
|
806
|
+
if (ctmDir) try { _deps.fs.mkdirSync(ctmDir, { recursive: true }); } catch {}
|
|
807
|
+
if (walleDir) try { _deps.fs.mkdirSync(walleDir, { recursive: true }); } catch {}
|
|
808
|
+
// Copy current databases if requested
|
|
809
|
+
if (copyData) {
|
|
810
|
+
const curCtmDir = require('../data-dir').getDataDir();
|
|
811
|
+
const curWalleDir = _deps.serverConfig.WALL_E_DATA_DIR || _deps.path.join(_deps.os.homedir(), '.walle', 'data');
|
|
812
|
+
if (ctmDir && ctmDir !== curCtmDir) {
|
|
813
|
+
const dst = _deps.path.join(ctmDir, 'task-manager.db');
|
|
814
|
+
if (!_deps.fs.existsSync(dst)) {
|
|
815
|
+
await _deps._drainCtmDbWorkers('setup-save-dirs-copy', 5000);
|
|
816
|
+
await _deps.dbModule.copyLiveDatabaseTo(dst);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (walleDir && walleDir !== curWalleDir) {
|
|
820
|
+
const dst = _deps.path.join(walleDir, 'wall-e-brain.db');
|
|
821
|
+
if (!_deps.fs.existsSync(dst)) {
|
|
822
|
+
const backup = await _deps.createWalleBrainBackupForCopy();
|
|
823
|
+
_deps.fs.copyFileSync(backup.backupPath, dst);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const lines = [];
|
|
828
|
+
try {
|
|
829
|
+
const existing = _deps.fs.readFileSync(envPath, 'utf8');
|
|
830
|
+
for (const line of existing.split('\n')) {
|
|
831
|
+
const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
|
|
832
|
+
if (m && (m[1] === 'CTM_DATA_DIR' || m[1] === 'WALL_E_DATA_DIR')) continue;
|
|
833
|
+
lines.push(line);
|
|
834
|
+
}
|
|
835
|
+
} catch { lines.push('# Wall-E configuration'); lines.push(''); }
|
|
836
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
|
|
837
|
+
lines.push('');
|
|
838
|
+
if (ctmDir) lines.push('CTM_DATA_DIR=' + ctmDir);
|
|
839
|
+
if (walleDir) lines.push('WALL_E_DATA_DIR=' + walleDir);
|
|
840
|
+
_deps.atomicWriteFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
|
|
841
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
842
|
+
res.end(JSON.stringify({ ok: true }));
|
|
843
|
+
// Restart everything after a brief delay
|
|
844
|
+
setTimeout(async () => {
|
|
845
|
+
// Restart CTM itself. Exit with code 42 — launchd KeepAlive restarts
|
|
846
|
+
// on non-zero exit. No bash watcher needed (EDR flags detached kill-0
|
|
847
|
+
// loops as malware).
|
|
848
|
+
try {
|
|
849
|
+
_deps._ctmDbClosingForProcessExit = true; // nothing on the serving path may write past the door
|
|
850
|
+
const dbExit = await _deps._closeDbForProcessExit('data-dir-change'); _deps._exitForLaunchdRestart('data-dir-change', null, { dbClose: _deps._dbCloseOutcome(dbExit) }); // D-7/D-3
|
|
851
|
+
} catch (e) { // D-3/D-7: as in _scheduleTransportRestart -- a throwing close exits through the door with `post_failed`, never through the fatal/unhandledRejection path
|
|
852
|
+
console.error('[restart] data-dir-change db close threw: ' + e.message); _deps._exitForLaunchdRestart('data-dir-change', null, { dbClose: 'post_failed' });
|
|
853
|
+
}
|
|
854
|
+
}, 1000);
|
|
855
|
+
} catch (e) {
|
|
856
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
857
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
// WP-6 shard S15: the 17 verbatim bodies above are appended here by
|
|
863
|
+
// bin/move-only-check.js's proof (identical to server.js's declarations up to the `_deps.`
|
|
864
|
+
// prefix and the four rebased requires).
|
|
865
|
+
module.exports = {
|
|
866
|
+
configure,
|
|
867
|
+
apiSetupStatus,
|
|
868
|
+
apiSetupDetectCodexCli,
|
|
869
|
+
apiSetupDetectGeminiCli,
|
|
870
|
+
apiSetupDetectClaudeCli,
|
|
871
|
+
apiSetupOauthProxyStart,
|
|
872
|
+
apiSetupOauthProxyStop,
|
|
873
|
+
apiSetupOauthProxyStatus,
|
|
874
|
+
apiSetupProviders,
|
|
875
|
+
apiSetupSaveProvider,
|
|
876
|
+
apiSetupTestProvider,
|
|
877
|
+
apiSetupSetDefault,
|
|
878
|
+
apiSetupCheckDir,
|
|
879
|
+
apiSetupEmbeddings,
|
|
880
|
+
apiSetupEmbeddingsSwitch,
|
|
881
|
+
apiSetupEmbeddingsPolicy,
|
|
882
|
+
apiSetupEmbeddingsDelete,
|
|
883
|
+
apiSetupSaveDirs,
|
|
884
|
+
};
|