@mandujs/core 0.54.32 → 0.55.0-beta.1
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 +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,319 +1,319 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Brain — LLM Adapters (resolver + factory).
|
|
3
|
-
*
|
|
4
|
-
* `createBrainAdapter(config)` picks the right adapter based on
|
|
5
|
-
* declarative config + runtime signals. Resolution order when
|
|
6
|
-
* `adapter: "auto"` (or `brain` config omitted entirely):
|
|
7
|
-
*
|
|
8
|
-
* 1. openai-oauth — token (or ChatGPT session) present
|
|
9
|
-
* 2. anthropic-oauth — token present in the keychain
|
|
10
|
-
* 3. template — final fallback (returns NoopAdapter; Brain
|
|
11
|
-
* gracefully falls back to template analysis)
|
|
12
|
-
*
|
|
13
|
-
* The local-LLM (Ollama) tier was removed: Mandu standardised on
|
|
14
|
-
* cloud OAuth providers so every dev has the same baseline quality
|
|
15
|
-
* without managing a local daemon. CLI surfaces that *want* a brain
|
|
16
|
-
* (`mandu brain doctor`, `mandu deploy:plan --use-brain`) detect the
|
|
17
|
-
* `template` fallback and prompt the user to run
|
|
18
|
-
* `mandu brain login --provider=openai` instead of degrading silently.
|
|
19
|
-
*
|
|
20
|
-
* `telemetryOptOut: true` disables every cloud tier — the resolver
|
|
21
|
-
* skips straight to template regardless of stored tokens.
|
|
22
|
-
*
|
|
23
|
-
* Explicit `adapter: "openai"` / `"anthropic"` / `"template"` pins
|
|
24
|
-
* the choice; the resolver still degrades to the NoopAdapter when
|
|
25
|
-
* the chosen provider is unreachable, so the caller never explodes
|
|
26
|
-
* on a missing dependency.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
export * from "./base";
|
|
30
|
-
export * from "./openai-oauth";
|
|
31
|
-
export * from "./anthropic-oauth";
|
|
32
|
-
export * from "./oauth-flow";
|
|
33
|
-
export * from "./chatgpt-auth";
|
|
34
|
-
|
|
35
|
-
import { type LLMAdapter, NoopAdapter } from "./base";
|
|
36
|
-
import {
|
|
37
|
-
OpenAIOAuthAdapter,
|
|
38
|
-
createOpenAIOAuthAdapter,
|
|
39
|
-
type OpenAIOAuthAdapterOptions,
|
|
40
|
-
} from "./openai-oauth";
|
|
41
|
-
import { ChatGPTAuth } from "./chatgpt-auth";
|
|
42
|
-
import {
|
|
43
|
-
AnthropicOAuthAdapter,
|
|
44
|
-
createAnthropicOAuthAdapter,
|
|
45
|
-
type AnthropicOAuthAdapterOptions,
|
|
46
|
-
} from "./anthropic-oauth";
|
|
47
|
-
import {
|
|
48
|
-
getCredentialStore,
|
|
49
|
-
type CredentialStore,
|
|
50
|
-
type StoredToken,
|
|
51
|
-
} from "../credentials";
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Normalised Brain config shape consumed by the resolver. Mirrors
|
|
55
|
-
* `ManduConfig.brain` but with every field required-or-explicitly
|
|
56
|
-
* defaulted so downstream code does not repeat the same null checks.
|
|
57
|
-
*/
|
|
58
|
-
export interface BrainAdapterConfig {
|
|
59
|
-
adapter: "auto" | "openai" | "anthropic" | "template";
|
|
60
|
-
openai?: { model?: string };
|
|
61
|
-
anthropic?: { model?: string };
|
|
62
|
-
/**
|
|
63
|
-
* When true, cloud adapters are disabled entirely. The resolver
|
|
64
|
-
* falls to template regardless of stored tokens.
|
|
65
|
-
*/
|
|
66
|
-
telemetryOptOut?: boolean;
|
|
67
|
-
/**
|
|
68
|
-
* Project root — required for consent scoping + redaction audit log.
|
|
69
|
-
* Defaults to `process.cwd()` when omitted.
|
|
70
|
-
*/
|
|
71
|
-
projectRoot?: string;
|
|
72
|
-
/** Credential store override — tests inject an in-memory one. */
|
|
73
|
-
credentialStore?: CredentialStore;
|
|
74
|
-
/** Override OpenAI-specific adapter options (tests only). */
|
|
75
|
-
openaiOptions?: OpenAIOAuthAdapterOptions;
|
|
76
|
-
/** Override Anthropic-specific adapter options (tests only). */
|
|
77
|
-
anthropicOptions?: AnthropicOAuthAdapterOptions;
|
|
78
|
-
/**
|
|
79
|
-
* Override the keychain probe used by the auto-resolver. Returns
|
|
80
|
-
* the stored token or null. Tests inject a deterministic stub; the
|
|
81
|
-
* default consults `credentialStore.load(provider)`.
|
|
82
|
-
*/
|
|
83
|
-
probeToken?: (provider: "openai" | "anthropic") => Promise<StoredToken | null>;
|
|
84
|
-
/**
|
|
85
|
-
* Override the ChatGPT session-token probe. Default: instantiate
|
|
86
|
-
* `new ChatGPTAuth()` and check its on-disk auth.json. Tests inject
|
|
87
|
-
* a stub returning `false` so the developer's real `~/.codex/auth.json`
|
|
88
|
-
* doesn't leak into unit-test expectations.
|
|
89
|
-
*/
|
|
90
|
-
probeChatGPTAuth?: () => { authenticated: boolean; path: string | null };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Result of the resolver — returned so callers can log which tier
|
|
95
|
-
* won (surfaced in `mandu brain status`) and detect the
|
|
96
|
-
* "needs-login" state.
|
|
97
|
-
*/
|
|
98
|
-
export interface BrainAdapterResolution {
|
|
99
|
-
adapter: LLMAdapter;
|
|
100
|
-
/** Which tier the resolver picked. */
|
|
101
|
-
resolved: "openai" | "anthropic" | "template";
|
|
102
|
-
/** Which tier the caller asked for (config value). */
|
|
103
|
-
requested: BrainAdapterConfig["adapter"];
|
|
104
|
-
/** Human-readable reason — useful for `mandu brain status`. */
|
|
105
|
-
reason: string;
|
|
106
|
-
/**
|
|
107
|
-
* True when the resolver fell back to template ONLY because the
|
|
108
|
-
* user has no cloud token. Interactive CLIs should prompt
|
|
109
|
-
* `mandu brain login --provider=openai` instead of using the noop
|
|
110
|
-
* adapter. False when the user explicitly opted out (`telemetryOptOut`)
|
|
111
|
-
* or asked for `template` directly.
|
|
112
|
-
*/
|
|
113
|
-
needsLogin: boolean;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Resolve + construct a Brain adapter.
|
|
118
|
-
*
|
|
119
|
-
* Callers typically use the convenience re-export
|
|
120
|
-
* `createBrainAdapter(config)`. The full `resolveBrainAdapter()`
|
|
121
|
-
* surface returns the metadata record so CLI status commands can
|
|
122
|
-
* explain which tier won.
|
|
123
|
-
*/
|
|
124
|
-
export async function resolveBrainAdapter(
|
|
125
|
-
config: Partial<BrainAdapterConfig> = {},
|
|
126
|
-
): Promise<BrainAdapterResolution> {
|
|
127
|
-
const requested = config.adapter ?? "auto";
|
|
128
|
-
const telemetryOptOut = config.telemetryOptOut ?? false;
|
|
129
|
-
|
|
130
|
-
const store = config.credentialStore ?? getCredentialStore();
|
|
131
|
-
const projectRoot = config.projectRoot ?? process.cwd();
|
|
132
|
-
|
|
133
|
-
const probeToken =
|
|
134
|
-
config.probeToken ?? ((provider) => store.load(provider));
|
|
135
|
-
|
|
136
|
-
const probeChatGPTAuth =
|
|
137
|
-
config.probeChatGPTAuth ??
|
|
138
|
-
(() => {
|
|
139
|
-
const c = new ChatGPTAuth();
|
|
140
|
-
return { authenticated: c.isAuthenticated(), path: c.locateAuthFile() };
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Explicit template — skip every other check.
|
|
144
|
-
if (requested === "template") {
|
|
145
|
-
return {
|
|
146
|
-
adapter: new NoopAdapter(),
|
|
147
|
-
resolved: "template",
|
|
148
|
-
requested,
|
|
149
|
-
reason: "Explicit adapter: 'template' in config",
|
|
150
|
-
needsLogin: false,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Explicit openai — only honored when telemetry is allowed AND a
|
|
155
|
-
// token exists. Falls back to template otherwise so Core does not
|
|
156
|
-
// explode.
|
|
157
|
-
if (requested === "openai") {
|
|
158
|
-
if (telemetryOptOut) {
|
|
159
|
-
return {
|
|
160
|
-
adapter: new NoopAdapter(),
|
|
161
|
-
resolved: "template",
|
|
162
|
-
requested,
|
|
163
|
-
reason:
|
|
164
|
-
"adapter: 'openai' requested but telemetryOptOut=true — forcing template",
|
|
165
|
-
needsLogin: false,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
// Primary: ChatGPT session token (written by `@openai/codex login`).
|
|
169
|
-
const cg = probeChatGPTAuth();
|
|
170
|
-
const hasChatGPT = cg.authenticated;
|
|
171
|
-
const token = hasChatGPT ? null : await probeToken("openai");
|
|
172
|
-
if (!hasChatGPT && !token) {
|
|
173
|
-
return {
|
|
174
|
-
adapter: new NoopAdapter(),
|
|
175
|
-
resolved: "template",
|
|
176
|
-
requested,
|
|
177
|
-
reason:
|
|
178
|
-
"adapter: 'openai' requested but no token found — run `mandu brain login --provider=openai`",
|
|
179
|
-
needsLogin: true,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
return {
|
|
183
|
-
adapter: createOpenAIOAuthAdapter({
|
|
184
|
-
...(config.openaiOptions ?? {}),
|
|
185
|
-
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
186
|
-
credentialStore: store,
|
|
187
|
-
projectRoot,
|
|
188
|
-
}),
|
|
189
|
-
resolved: "openai",
|
|
190
|
-
requested,
|
|
191
|
-
reason: hasChatGPT
|
|
192
|
-
? "Explicit adapter: 'openai' + ChatGPT session token present"
|
|
193
|
-
: "Explicit adapter: 'openai' + keychain token present",
|
|
194
|
-
needsLogin: false,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (requested === "anthropic") {
|
|
199
|
-
if (telemetryOptOut) {
|
|
200
|
-
return {
|
|
201
|
-
adapter: new NoopAdapter(),
|
|
202
|
-
resolved: "template",
|
|
203
|
-
requested,
|
|
204
|
-
reason:
|
|
205
|
-
"adapter: 'anthropic' requested but telemetryOptOut=true — forcing template",
|
|
206
|
-
needsLogin: false,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const token = await probeToken("anthropic");
|
|
210
|
-
if (!token) {
|
|
211
|
-
return {
|
|
212
|
-
adapter: new NoopAdapter(),
|
|
213
|
-
resolved: "template",
|
|
214
|
-
requested,
|
|
215
|
-
reason:
|
|
216
|
-
"adapter: 'anthropic' requested but no token in keychain — run `mandu brain login --provider=anthropic`",
|
|
217
|
-
needsLogin: true,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
adapter: createAnthropicOAuthAdapter({
|
|
222
|
-
...(config.anthropicOptions ?? {}),
|
|
223
|
-
model: config.anthropic?.model ?? config.anthropicOptions?.model,
|
|
224
|
-
credentialStore: store,
|
|
225
|
-
projectRoot,
|
|
226
|
-
}),
|
|
227
|
-
resolved: "anthropic",
|
|
228
|
-
requested,
|
|
229
|
-
reason: "Explicit adapter: 'anthropic' + token present",
|
|
230
|
-
needsLogin: false,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Auto — try cloud providers first (when allowed), then template.
|
|
235
|
-
if (!telemetryOptOut) {
|
|
236
|
-
// Primary: ChatGPT session token (managed by `@openai/codex`).
|
|
237
|
-
const cg2 = probeChatGPTAuth();
|
|
238
|
-
if (cg2.authenticated) {
|
|
239
|
-
return {
|
|
240
|
-
adapter: createOpenAIOAuthAdapter({
|
|
241
|
-
...(config.openaiOptions ?? {}),
|
|
242
|
-
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
243
|
-
credentialStore: store,
|
|
244
|
-
projectRoot,
|
|
245
|
-
}),
|
|
246
|
-
resolved: "openai",
|
|
247
|
-
requested,
|
|
248
|
-
reason: `auto: ChatGPT session token at ${cg2.path ?? "(unknown)"}`,
|
|
249
|
-
needsLogin: false,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
const openaiToken = await probeToken("openai");
|
|
253
|
-
if (openaiToken) {
|
|
254
|
-
return {
|
|
255
|
-
adapter: createOpenAIOAuthAdapter({
|
|
256
|
-
...(config.openaiOptions ?? {}),
|
|
257
|
-
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
258
|
-
credentialStore: store,
|
|
259
|
-
projectRoot,
|
|
260
|
-
}),
|
|
261
|
-
resolved: "openai",
|
|
262
|
-
requested,
|
|
263
|
-
reason: "auto: OpenAI token found in keychain",
|
|
264
|
-
needsLogin: false,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
const anthropicToken = await probeToken("anthropic");
|
|
268
|
-
if (anthropicToken) {
|
|
269
|
-
return {
|
|
270
|
-
adapter: createAnthropicOAuthAdapter({
|
|
271
|
-
...(config.anthropicOptions ?? {}),
|
|
272
|
-
model: config.anthropic?.model ?? config.anthropicOptions?.model,
|
|
273
|
-
credentialStore: store,
|
|
274
|
-
projectRoot,
|
|
275
|
-
}),
|
|
276
|
-
resolved: "anthropic",
|
|
277
|
-
requested,
|
|
278
|
-
reason: "auto: Anthropic token found in keychain",
|
|
279
|
-
needsLogin: false,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Final fallback. `needsLogin` distinguishes "user opted out" from
|
|
285
|
-
// "user has no token" so interactive CLIs can prompt login only in
|
|
286
|
-
// the latter case.
|
|
287
|
-
return {
|
|
288
|
-
adapter: new NoopAdapter(),
|
|
289
|
-
resolved: "template",
|
|
290
|
-
requested,
|
|
291
|
-
reason: telemetryOptOut
|
|
292
|
-
? "auto: telemetryOptOut=true — using template"
|
|
293
|
-
: "auto: no cloud token — run `mandu brain login --provider=openai`",
|
|
294
|
-
needsLogin: !telemetryOptOut,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Convenience factory — returns just the adapter. Use
|
|
300
|
-
* `resolveBrainAdapter()` when you also need the resolution metadata
|
|
301
|
-
* (e.g. for `mandu brain status`).
|
|
302
|
-
*/
|
|
303
|
-
export async function createBrainAdapter(
|
|
304
|
-
config: Partial<BrainAdapterConfig> = {},
|
|
305
|
-
): Promise<LLMAdapter> {
|
|
306
|
-
const res = await resolveBrainAdapter(config);
|
|
307
|
-
return res.adapter;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Runtime guard — is the adapter a cloud connector? Used by CLI
|
|
312
|
-
* status to flag "may transmit data" lines.
|
|
313
|
-
*/
|
|
314
|
-
export function isCloudAdapter(adapter: LLMAdapter): boolean {
|
|
315
|
-
return (
|
|
316
|
-
adapter instanceof OpenAIOAuthAdapter ||
|
|
317
|
-
adapter instanceof AnthropicOAuthAdapter
|
|
318
|
-
);
|
|
319
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Brain — LLM Adapters (resolver + factory).
|
|
3
|
+
*
|
|
4
|
+
* `createBrainAdapter(config)` picks the right adapter based on
|
|
5
|
+
* declarative config + runtime signals. Resolution order when
|
|
6
|
+
* `adapter: "auto"` (or `brain` config omitted entirely):
|
|
7
|
+
*
|
|
8
|
+
* 1. openai-oauth — token (or ChatGPT session) present
|
|
9
|
+
* 2. anthropic-oauth — token present in the keychain
|
|
10
|
+
* 3. template — final fallback (returns NoopAdapter; Brain
|
|
11
|
+
* gracefully falls back to template analysis)
|
|
12
|
+
*
|
|
13
|
+
* The local-LLM (Ollama) tier was removed: Mandu standardised on
|
|
14
|
+
* cloud OAuth providers so every dev has the same baseline quality
|
|
15
|
+
* without managing a local daemon. CLI surfaces that *want* a brain
|
|
16
|
+
* (`mandu brain doctor`, `mandu deploy:plan --use-brain`) detect the
|
|
17
|
+
* `template` fallback and prompt the user to run
|
|
18
|
+
* `mandu brain login --provider=openai` instead of degrading silently.
|
|
19
|
+
*
|
|
20
|
+
* `telemetryOptOut: true` disables every cloud tier — the resolver
|
|
21
|
+
* skips straight to template regardless of stored tokens.
|
|
22
|
+
*
|
|
23
|
+
* Explicit `adapter: "openai"` / `"anthropic"` / `"template"` pins
|
|
24
|
+
* the choice; the resolver still degrades to the NoopAdapter when
|
|
25
|
+
* the chosen provider is unreachable, so the caller never explodes
|
|
26
|
+
* on a missing dependency.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export * from "./base";
|
|
30
|
+
export * from "./openai-oauth";
|
|
31
|
+
export * from "./anthropic-oauth";
|
|
32
|
+
export * from "./oauth-flow";
|
|
33
|
+
export * from "./chatgpt-auth";
|
|
34
|
+
|
|
35
|
+
import { type LLMAdapter, NoopAdapter } from "./base";
|
|
36
|
+
import {
|
|
37
|
+
OpenAIOAuthAdapter,
|
|
38
|
+
createOpenAIOAuthAdapter,
|
|
39
|
+
type OpenAIOAuthAdapterOptions,
|
|
40
|
+
} from "./openai-oauth";
|
|
41
|
+
import { ChatGPTAuth } from "./chatgpt-auth";
|
|
42
|
+
import {
|
|
43
|
+
AnthropicOAuthAdapter,
|
|
44
|
+
createAnthropicOAuthAdapter,
|
|
45
|
+
type AnthropicOAuthAdapterOptions,
|
|
46
|
+
} from "./anthropic-oauth";
|
|
47
|
+
import {
|
|
48
|
+
getCredentialStore,
|
|
49
|
+
type CredentialStore,
|
|
50
|
+
type StoredToken,
|
|
51
|
+
} from "../credentials";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalised Brain config shape consumed by the resolver. Mirrors
|
|
55
|
+
* `ManduConfig.brain` but with every field required-or-explicitly
|
|
56
|
+
* defaulted so downstream code does not repeat the same null checks.
|
|
57
|
+
*/
|
|
58
|
+
export interface BrainAdapterConfig {
|
|
59
|
+
adapter: "auto" | "openai" | "anthropic" | "template";
|
|
60
|
+
openai?: { model?: string };
|
|
61
|
+
anthropic?: { model?: string };
|
|
62
|
+
/**
|
|
63
|
+
* When true, cloud adapters are disabled entirely. The resolver
|
|
64
|
+
* falls to template regardless of stored tokens.
|
|
65
|
+
*/
|
|
66
|
+
telemetryOptOut?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Project root — required for consent scoping + redaction audit log.
|
|
69
|
+
* Defaults to `process.cwd()` when omitted.
|
|
70
|
+
*/
|
|
71
|
+
projectRoot?: string;
|
|
72
|
+
/** Credential store override — tests inject an in-memory one. */
|
|
73
|
+
credentialStore?: CredentialStore;
|
|
74
|
+
/** Override OpenAI-specific adapter options (tests only). */
|
|
75
|
+
openaiOptions?: OpenAIOAuthAdapterOptions;
|
|
76
|
+
/** Override Anthropic-specific adapter options (tests only). */
|
|
77
|
+
anthropicOptions?: AnthropicOAuthAdapterOptions;
|
|
78
|
+
/**
|
|
79
|
+
* Override the keychain probe used by the auto-resolver. Returns
|
|
80
|
+
* the stored token or null. Tests inject a deterministic stub; the
|
|
81
|
+
* default consults `credentialStore.load(provider)`.
|
|
82
|
+
*/
|
|
83
|
+
probeToken?: (provider: "openai" | "anthropic") => Promise<StoredToken | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Override the ChatGPT session-token probe. Default: instantiate
|
|
86
|
+
* `new ChatGPTAuth()` and check its on-disk auth.json. Tests inject
|
|
87
|
+
* a stub returning `false` so the developer's real `~/.codex/auth.json`
|
|
88
|
+
* doesn't leak into unit-test expectations.
|
|
89
|
+
*/
|
|
90
|
+
probeChatGPTAuth?: () => { authenticated: boolean; path: string | null };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Result of the resolver — returned so callers can log which tier
|
|
95
|
+
* won (surfaced in `mandu brain status`) and detect the
|
|
96
|
+
* "needs-login" state.
|
|
97
|
+
*/
|
|
98
|
+
export interface BrainAdapterResolution {
|
|
99
|
+
adapter: LLMAdapter;
|
|
100
|
+
/** Which tier the resolver picked. */
|
|
101
|
+
resolved: "openai" | "anthropic" | "template";
|
|
102
|
+
/** Which tier the caller asked for (config value). */
|
|
103
|
+
requested: BrainAdapterConfig["adapter"];
|
|
104
|
+
/** Human-readable reason — useful for `mandu brain status`. */
|
|
105
|
+
reason: string;
|
|
106
|
+
/**
|
|
107
|
+
* True when the resolver fell back to template ONLY because the
|
|
108
|
+
* user has no cloud token. Interactive CLIs should prompt
|
|
109
|
+
* `mandu brain login --provider=openai` instead of using the noop
|
|
110
|
+
* adapter. False when the user explicitly opted out (`telemetryOptOut`)
|
|
111
|
+
* or asked for `template` directly.
|
|
112
|
+
*/
|
|
113
|
+
needsLogin: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolve + construct a Brain adapter.
|
|
118
|
+
*
|
|
119
|
+
* Callers typically use the convenience re-export
|
|
120
|
+
* `createBrainAdapter(config)`. The full `resolveBrainAdapter()`
|
|
121
|
+
* surface returns the metadata record so CLI status commands can
|
|
122
|
+
* explain which tier won.
|
|
123
|
+
*/
|
|
124
|
+
export async function resolveBrainAdapter(
|
|
125
|
+
config: Partial<BrainAdapterConfig> = {},
|
|
126
|
+
): Promise<BrainAdapterResolution> {
|
|
127
|
+
const requested = config.adapter ?? "auto";
|
|
128
|
+
const telemetryOptOut = config.telemetryOptOut ?? false;
|
|
129
|
+
|
|
130
|
+
const store = config.credentialStore ?? getCredentialStore();
|
|
131
|
+
const projectRoot = config.projectRoot ?? process.cwd();
|
|
132
|
+
|
|
133
|
+
const probeToken =
|
|
134
|
+
config.probeToken ?? ((provider) => store.load(provider));
|
|
135
|
+
|
|
136
|
+
const probeChatGPTAuth =
|
|
137
|
+
config.probeChatGPTAuth ??
|
|
138
|
+
(() => {
|
|
139
|
+
const c = new ChatGPTAuth();
|
|
140
|
+
return { authenticated: c.isAuthenticated(), path: c.locateAuthFile() };
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Explicit template — skip every other check.
|
|
144
|
+
if (requested === "template") {
|
|
145
|
+
return {
|
|
146
|
+
adapter: new NoopAdapter(),
|
|
147
|
+
resolved: "template",
|
|
148
|
+
requested,
|
|
149
|
+
reason: "Explicit adapter: 'template' in config",
|
|
150
|
+
needsLogin: false,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Explicit openai — only honored when telemetry is allowed AND a
|
|
155
|
+
// token exists. Falls back to template otherwise so Core does not
|
|
156
|
+
// explode.
|
|
157
|
+
if (requested === "openai") {
|
|
158
|
+
if (telemetryOptOut) {
|
|
159
|
+
return {
|
|
160
|
+
adapter: new NoopAdapter(),
|
|
161
|
+
resolved: "template",
|
|
162
|
+
requested,
|
|
163
|
+
reason:
|
|
164
|
+
"adapter: 'openai' requested but telemetryOptOut=true — forcing template",
|
|
165
|
+
needsLogin: false,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Primary: ChatGPT session token (written by `@openai/codex login`).
|
|
169
|
+
const cg = probeChatGPTAuth();
|
|
170
|
+
const hasChatGPT = cg.authenticated;
|
|
171
|
+
const token = hasChatGPT ? null : await probeToken("openai");
|
|
172
|
+
if (!hasChatGPT && !token) {
|
|
173
|
+
return {
|
|
174
|
+
adapter: new NoopAdapter(),
|
|
175
|
+
resolved: "template",
|
|
176
|
+
requested,
|
|
177
|
+
reason:
|
|
178
|
+
"adapter: 'openai' requested but no token found — run `mandu brain login --provider=openai`",
|
|
179
|
+
needsLogin: true,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
adapter: createOpenAIOAuthAdapter({
|
|
184
|
+
...(config.openaiOptions ?? {}),
|
|
185
|
+
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
186
|
+
credentialStore: store,
|
|
187
|
+
projectRoot,
|
|
188
|
+
}),
|
|
189
|
+
resolved: "openai",
|
|
190
|
+
requested,
|
|
191
|
+
reason: hasChatGPT
|
|
192
|
+
? "Explicit adapter: 'openai' + ChatGPT session token present"
|
|
193
|
+
: "Explicit adapter: 'openai' + keychain token present",
|
|
194
|
+
needsLogin: false,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (requested === "anthropic") {
|
|
199
|
+
if (telemetryOptOut) {
|
|
200
|
+
return {
|
|
201
|
+
adapter: new NoopAdapter(),
|
|
202
|
+
resolved: "template",
|
|
203
|
+
requested,
|
|
204
|
+
reason:
|
|
205
|
+
"adapter: 'anthropic' requested but telemetryOptOut=true — forcing template",
|
|
206
|
+
needsLogin: false,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const token = await probeToken("anthropic");
|
|
210
|
+
if (!token) {
|
|
211
|
+
return {
|
|
212
|
+
adapter: new NoopAdapter(),
|
|
213
|
+
resolved: "template",
|
|
214
|
+
requested,
|
|
215
|
+
reason:
|
|
216
|
+
"adapter: 'anthropic' requested but no token in keychain — run `mandu brain login --provider=anthropic`",
|
|
217
|
+
needsLogin: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
adapter: createAnthropicOAuthAdapter({
|
|
222
|
+
...(config.anthropicOptions ?? {}),
|
|
223
|
+
model: config.anthropic?.model ?? config.anthropicOptions?.model,
|
|
224
|
+
credentialStore: store,
|
|
225
|
+
projectRoot,
|
|
226
|
+
}),
|
|
227
|
+
resolved: "anthropic",
|
|
228
|
+
requested,
|
|
229
|
+
reason: "Explicit adapter: 'anthropic' + token present",
|
|
230
|
+
needsLogin: false,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Auto — try cloud providers first (when allowed), then template.
|
|
235
|
+
if (!telemetryOptOut) {
|
|
236
|
+
// Primary: ChatGPT session token (managed by `@openai/codex`).
|
|
237
|
+
const cg2 = probeChatGPTAuth();
|
|
238
|
+
if (cg2.authenticated) {
|
|
239
|
+
return {
|
|
240
|
+
adapter: createOpenAIOAuthAdapter({
|
|
241
|
+
...(config.openaiOptions ?? {}),
|
|
242
|
+
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
243
|
+
credentialStore: store,
|
|
244
|
+
projectRoot,
|
|
245
|
+
}),
|
|
246
|
+
resolved: "openai",
|
|
247
|
+
requested,
|
|
248
|
+
reason: `auto: ChatGPT session token at ${cg2.path ?? "(unknown)"}`,
|
|
249
|
+
needsLogin: false,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const openaiToken = await probeToken("openai");
|
|
253
|
+
if (openaiToken) {
|
|
254
|
+
return {
|
|
255
|
+
adapter: createOpenAIOAuthAdapter({
|
|
256
|
+
...(config.openaiOptions ?? {}),
|
|
257
|
+
model: config.openai?.model ?? config.openaiOptions?.model,
|
|
258
|
+
credentialStore: store,
|
|
259
|
+
projectRoot,
|
|
260
|
+
}),
|
|
261
|
+
resolved: "openai",
|
|
262
|
+
requested,
|
|
263
|
+
reason: "auto: OpenAI token found in keychain",
|
|
264
|
+
needsLogin: false,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const anthropicToken = await probeToken("anthropic");
|
|
268
|
+
if (anthropicToken) {
|
|
269
|
+
return {
|
|
270
|
+
adapter: createAnthropicOAuthAdapter({
|
|
271
|
+
...(config.anthropicOptions ?? {}),
|
|
272
|
+
model: config.anthropic?.model ?? config.anthropicOptions?.model,
|
|
273
|
+
credentialStore: store,
|
|
274
|
+
projectRoot,
|
|
275
|
+
}),
|
|
276
|
+
resolved: "anthropic",
|
|
277
|
+
requested,
|
|
278
|
+
reason: "auto: Anthropic token found in keychain",
|
|
279
|
+
needsLogin: false,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Final fallback. `needsLogin` distinguishes "user opted out" from
|
|
285
|
+
// "user has no token" so interactive CLIs can prompt login only in
|
|
286
|
+
// the latter case.
|
|
287
|
+
return {
|
|
288
|
+
adapter: new NoopAdapter(),
|
|
289
|
+
resolved: "template",
|
|
290
|
+
requested,
|
|
291
|
+
reason: telemetryOptOut
|
|
292
|
+
? "auto: telemetryOptOut=true — using template"
|
|
293
|
+
: "auto: no cloud token — run `mandu brain login --provider=openai`",
|
|
294
|
+
needsLogin: !telemetryOptOut,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Convenience factory — returns just the adapter. Use
|
|
300
|
+
* `resolveBrainAdapter()` when you also need the resolution metadata
|
|
301
|
+
* (e.g. for `mandu brain status`).
|
|
302
|
+
*/
|
|
303
|
+
export async function createBrainAdapter(
|
|
304
|
+
config: Partial<BrainAdapterConfig> = {},
|
|
305
|
+
): Promise<LLMAdapter> {
|
|
306
|
+
const res = await resolveBrainAdapter(config);
|
|
307
|
+
return res.adapter;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Runtime guard — is the adapter a cloud connector? Used by CLI
|
|
312
|
+
* status to flag "may transmit data" lines.
|
|
313
|
+
*/
|
|
314
|
+
export function isCloudAdapter(adapter: LLMAdapter): boolean {
|
|
315
|
+
return (
|
|
316
|
+
adapter instanceof OpenAIOAuthAdapter ||
|
|
317
|
+
adapter instanceof AnthropicOAuthAdapter
|
|
318
|
+
);
|
|
319
|
+
}
|