@xopcai/xopc 0.0.87 → 0.0.89
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 +8 -1
- package/README.zh-CN.md +8 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-B6PJB07W.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-BSHqqCF1.js → channels-status-swr-DaHGkRF1.js} +1 -1
- package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +1 -0
- package/dist/gateway/static/root/assets/{dist-Cmjp2APP.js → dist-6LecgDx5.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CFa9z_1N.js → extension-debug-page-CtuKJ9tE.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BI8eaTPq.js → extension-page-ykzjOkR5.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-Ce2qrdpO.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-DRqwef_Q.js → fetch-C9FFJjuH.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-BiNHBo2Y.js → field-primitives-BFcrNeTU.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-ZRb8qhuz.js → heartbeat-config-api-CEg4Vr9R.js} +1 -1
- package/dist/gateway/static/root/assets/{index-Cu7bKuUi.js → index-CZfy9oxs.js} +85 -85
- package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DiqqVs6m.js → settings-form-section-BqdzA28u.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-n1Gprylk.js → share-preview-page-Di5Bzh4g.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-CZOh1nT3.js → theme-store-CNqbmTNV.js} +1 -1
- package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +7 -0
- package/dist/gateway/static/root/assets/{utils-CkWBfxs4.js → utils-BWm2tG2w.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +27 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +2 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/child-agent-factory.d.ts +15 -0
- package/dist/src/agent/child-agent-factory.js +35 -2
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/client-error-format.d.ts +20 -0
- package/dist/src/agent/client-error-format.js +97 -0
- package/dist/src/agent/client-error-format.js.map +1 -0
- package/dist/src/agent/embedded/run-turn.js +23 -4
- package/dist/src/agent/embedded/run-turn.js.map +1 -1
- package/dist/src/agent/goals/goal-locale.d.ts +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
- package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
- package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +19 -3
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/webchat-tts.d.ts +1 -2
- package/dist/src/agent/service/webchat-tts.js +1 -1
- package/dist/src/agent/service/webchat-tts.js.map +1 -1
- package/dist/src/agent/service.js +2 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +3 -1
- package/dist/src/agent/tools/cronjob-tool.js +2 -1
- package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -0
- package/dist/src/agent/tools/factory.js +2 -23
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.d.ts +6 -28
- package/dist/src/agent/tools/workflow-tool.js +61 -213
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
- package/dist/src/agent/workflow/agent-progress.js +65 -0
- package/dist/src/agent/workflow/agent-progress.js.map +1 -0
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
- package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
- package/dist/src/agent/workflow/builtins/index.js +11 -1
- package/dist/src/agent/workflow/builtins/index.js.map +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/release-check.js +165 -0
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/research.js +14 -0
- package/dist/src/agent/workflow/builtins/research.js.map +1 -1
- package/dist/src/agent/workflow/index.d.ts +2 -1
- package/dist/src/agent/workflow/index.js +3 -2
- package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
- package/dist/src/agent/workflow/meta-locale.js +62 -0
- package/dist/src/agent/workflow/meta-locale.js.map +1 -0
- package/dist/src/agent/workflow/parser.js +3 -0
- package/dist/src/agent/workflow/parser.js.map +1 -1
- package/dist/src/agent/workflow/runtime.d.ts +2 -2
- package/dist/src/agent/workflow/runtime.js +21 -14
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/snapshot.js +2 -12
- package/dist/src/agent/workflow/snapshot.js.map +1 -1
- package/dist/src/agent/workflow/step-labels.d.ts +8 -0
- package/dist/src/agent/workflow/step-labels.js +48 -0
- package/dist/src/agent/workflow/step-labels.js.map +1 -0
- package/dist/src/agent/workflow/subagent-runner.js +46 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +74 -1
- package/dist/src/agent/workflow/workflow-child-tools.d.ts +4 -0
- package/dist/src/agent/workflow/workflow-child-tools.js +21 -0
- package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -0
- package/dist/src/auth/credentials.d.ts +19 -2
- package/dist/src/auth/credentials.js +47 -13
- package/dist/src/auth/credentials.js.map +1 -1
- package/dist/src/auth/oauth/types.d.ts +16 -0
- package/dist/src/cli/commands/auth.js +6 -0
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +6 -0
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/config/agent-typed-models.d.ts +18 -0
- package/dist/src/config/agent-typed-models.js +53 -0
- package/dist/src/config/agent-typed-models.js.map +1 -0
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +52 -0
- package/dist/src/config/schema.js +39 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/voice.d.ts +3 -28
- package/dist/src/config/voice.js +27 -261
- package/dist/src/config/voice.js.map +1 -1
- package/dist/src/cron/executor.d.ts +2 -0
- package/dist/src/cron/executor.js +59 -5
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/job-content.js +2 -1
- package/dist/src/cron/job-content.js.map +1 -1
- package/dist/src/cron/types.d.ts +21 -1
- package/dist/src/cron/validation.d.ts +76 -0
- package/dist/src/cron/validation.js +26 -1
- package/dist/src/cron/validation.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +9 -0
- package/dist/src/gateway/agents-admin.js +16 -0
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/config-tools-web.js +3 -2
- package/dist/src/gateway/config-tools-web.js.map +1 -1
- package/dist/src/gateway/gateway-workflow-host.types.d.ts +17 -0
- package/dist/src/gateway/gateway-workflow-host.types.js +1 -0
- package/dist/src/gateway/hono/lib/agent-model.d.ts +7 -0
- package/dist/src/gateway/hono/lib/agent-model.js +36 -1
- package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +28 -5
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
- package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
- package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
- package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
- package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
- package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
- package/dist/src/gateway/hono/lib/safe-voice-config.js +2 -1
- package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
- package/dist/src/gateway/hono/oauth-async.js +40 -15
- package/dist/src/gateway/hono/oauth-async.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +31 -6
- package/dist/src/gateway/hono/oauth.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +8 -2
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
- package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +7 -2
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +59 -0
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +84 -15
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +75 -0
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
- package/dist/src/gateway/hono/routes/workflows.js +226 -0
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +2 -20
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +28 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/providers/index.d.ts +8 -0
- package/dist/src/providers/index.js +51 -12
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/share/site-share-config.d.ts +3 -2
- package/dist/src/share/site-share-config.js.map +1 -1
- package/dist/src/tui/tui-agent-events.js +2 -1
- package/dist/src/tui/tui-agent-events.js.map +1 -1
- package/dist/src/voice/metadata/builtin.d.ts +2 -0
- package/dist/src/voice/metadata/builtin.js +420 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -0
- package/dist/src/voice/metadata/index.d.ts +4 -0
- package/dist/src/voice/metadata/index.js +3 -0
- package/dist/src/voice/metadata/registry.d.ts +5 -0
- package/dist/src/voice/metadata/registry.js +34 -0
- package/dist/src/voice/metadata/registry.js.map +1 -0
- package/dist/src/voice/metadata/types.d.ts +41 -0
- package/dist/src/voice/metadata/types.js +1 -0
- package/dist/src/voice/stt/list-providers.d.ts +3 -3
- package/dist/src/voice/stt/list-providers.js +41 -6
- package/dist/src/voice/stt/list-providers.js.map +1 -1
- package/dist/src/voice/tts/list-providers.d.ts +3 -3
- package/dist/src/voice/tts/list-providers.js +41 -6
- package/dist/src/voice/tts/list-providers.js.map +1 -1
- package/dist/src/workflows/domain/command.d.ts +19 -0
- package/dist/src/workflows/domain/command.js +1 -0
- package/dist/src/workflows/domain/definition-utils.d.ts +14 -0
- package/dist/src/workflows/domain/definition-utils.js +50 -0
- package/dist/src/workflows/domain/definition-utils.js.map +1 -0
- package/dist/src/workflows/domain/definition.d.ts +62 -0
- package/dist/src/workflows/domain/definition.js +1 -0
- package/dist/src/workflows/domain/event.d.ts +67 -0
- package/dist/src/workflows/domain/event.js +1 -0
- package/dist/src/workflows/domain/index.d.ts +7 -0
- package/dist/src/workflows/domain/index.js +4 -0
- package/dist/src/workflows/domain/result.d.ts +65 -0
- package/dist/src/workflows/domain/result.js +1 -0
- package/dist/src/workflows/domain/run.d.ts +177 -0
- package/dist/src/workflows/domain/run.js +14 -0
- package/dist/src/workflows/domain/run.js.map +1 -0
- package/dist/src/workflows/domain/validation.d.ts +19 -0
- package/dist/src/workflows/domain/validation.js +66 -0
- package/dist/src/workflows/domain/validation.js.map +1 -0
- package/dist/src/workflows/engine/index.d.ts +2 -0
- package/dist/src/workflows/engine/index.js +3 -0
- package/dist/src/workflows/engine/projector.d.ts +3 -0
- package/dist/src/workflows/engine/projector.js +205 -0
- package/dist/src/workflows/engine/projector.js.map +1 -0
- package/dist/src/workflows/engine/workflow-engine.d.ts +32 -0
- package/dist/src/workflows/engine/workflow-engine.js +189 -0
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
- package/dist/src/workflows/index.d.ts +10 -0
- package/dist/src/workflows/index.js +18 -0
- package/dist/src/workflows/runtime/index.d.ts +1 -0
- package/dist/src/workflows/runtime/index.js +4 -0
- package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
- package/dist/src/workflows/runtime/script-runtime.js +3 -0
- package/dist/src/workflows/service/run-view-to-snapshot.d.ts +4 -0
- package/dist/src/workflows/service/run-view-to-snapshot.js +61 -0
- package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -0
- package/dist/src/workflows/service/workflow-run-service.d.ts +36 -0
- package/dist/src/workflows/service/workflow-run-service.js +279 -0
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -0
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +47 -0
- package/dist/src/workflows/service/workflow-run-service.types.js +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +29 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +177 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -0
- package/dist/src/workflows/service/workflow-session-key.d.ts +3 -0
- package/dist/src/workflows/service/workflow-session-key.js +21 -0
- package/dist/src/workflows/service/workflow-session-key.js.map +1 -0
- package/dist/src/workflows/store/event-store.d.ts +17 -0
- package/dist/src/workflows/store/event-store.js +83 -0
- package/dist/src/workflows/store/event-store.js.map +1 -0
- package/dist/src/workflows/store/paths.d.ts +7 -0
- package/dist/src/workflows/store/paths.js +26 -0
- package/dist/src/workflows/store/paths.js.map +1 -0
- package/dist/src/workflows/store/run-store.d.ts +13 -0
- package/dist/src/workflows/store/run-store.js +69 -0
- package/dist/src/workflows/store/run-store.js.map +1 -0
- package/package.json +5 -5
- package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-Dg8R-Szf.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-yohw9YSu.js +0 -1
- package/dist/gateway/static/root/assets/cron-api-0h_QT8U3.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-BkfKFfFk.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +0 -1
- package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BFZ8GgCv.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CD7AfB-2.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-CcN_gj--.js +0 -2
- package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +0 -3
- package/dist/gateway/static/root/assets/voice-api-key-field-O6awz9hi.js +0 -1
|
@@ -1,4 +1,39 @@
|
|
|
1
1
|
//#region src/gateway/hono/lib/agent-model.ts
|
|
2
|
+
const TYPED_MODEL_ID_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
3
|
+
function isValidProviderModelRef(ref) {
|
|
4
|
+
const trimmed = ref.trim();
|
|
5
|
+
const idx = trimmed.indexOf("/");
|
|
6
|
+
return idx > 0 && idx < trimmed.length - 1;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Coerce PATCH body `models[]` into validated typed model entries.
|
|
10
|
+
* Returns `null` to clear, `undefined` when input should be skipped, or cleaned array.
|
|
11
|
+
* Empty array after filtering → `null` (same as clear).
|
|
12
|
+
*/
|
|
13
|
+
function normalizePatchTypedModels(v) {
|
|
14
|
+
if (v === void 0) return void 0;
|
|
15
|
+
if (v === null) return null;
|
|
16
|
+
if (!Array.isArray(v)) return void 0;
|
|
17
|
+
const byId = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const raw of v) {
|
|
19
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
20
|
+
const o = raw;
|
|
21
|
+
const id = typeof o.id === "string" ? o.id.trim() : "";
|
|
22
|
+
const model = typeof o.model === "string" ? o.model.trim() : "";
|
|
23
|
+
if (!id || !TYPED_MODEL_ID_RE.test(id) || !model || !isValidProviderModelRef(model)) continue;
|
|
24
|
+
const description = typeof o.description === "string" && o.description.trim() ? o.description.trim().slice(0, 500) : void 0;
|
|
25
|
+
byId.set(id, description ? {
|
|
26
|
+
id,
|
|
27
|
+
description,
|
|
28
|
+
model
|
|
29
|
+
} : {
|
|
30
|
+
id,
|
|
31
|
+
model
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (byId.size === 0) return null;
|
|
35
|
+
return [...byId.values()];
|
|
36
|
+
}
|
|
2
37
|
/** Read `primary` from an `AgentModelConfig` object. */
|
|
3
38
|
function agentModelRefToString(ref) {
|
|
4
39
|
if (!ref || typeof ref !== "object" || Array.isArray(ref)) return void 0;
|
|
@@ -57,6 +92,6 @@ function normalizePatchAgentImageGenerationModel(v) {
|
|
|
57
92
|
return out;
|
|
58
93
|
}
|
|
59
94
|
//#endregion
|
|
60
|
-
export { agentImageGenerationModelAutoProviderFallback, agentImageGenerationModelTimeoutMs, agentModelFallbacksToArray, agentModelRefToString, normalizePatchAgentImageGenerationModel, normalizePatchAgentModel };
|
|
95
|
+
export { agentImageGenerationModelAutoProviderFallback, agentImageGenerationModelTimeoutMs, agentModelFallbacksToArray, agentModelRefToString, normalizePatchAgentImageGenerationModel, normalizePatchAgentModel, normalizePatchTypedModels };
|
|
61
96
|
|
|
62
97
|
//# sourceMappingURL=agent-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-model.js","names":[],"sources":["../../../../../src/gateway/hono/lib/agent-model.ts"],"sourcesContent":["/** Read `primary` from an `AgentModelConfig` object. */\nexport function agentModelRefToString(ref: unknown): string | undefined {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return undefined;\n const p = (ref as { primary?: string }).primary;\n return typeof p === 'string' && p.trim() ? p : undefined;\n}\n\nexport function agentModelFallbacksToArray(ref: unknown): string[] {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return [];\n const f = (ref as { fallbacks?: unknown }).fallbacks;\n if (!Array.isArray(f)) return [];\n return f.filter((x): x is string => typeof x === 'string' && x.trim().length > 0);\n}\n\n/**\n * Coerce a PATCH body `model` value into the canonical `{ primary, fallbacks? }`\n * object form. Returns `undefined` when the input has no usable primary so the\n * caller can skip the assignment.\n */\nexport function normalizePatchAgentModel(v: unknown): { primary: string; fallbacks?: string[] } | undefined {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n const o = v as Record<string, unknown>;\n const primary = typeof o.primary === 'string' ? o.primary.trim() : '';\n if (!primary) return undefined;\n const fallbacks = Array.isArray(o.fallbacks)\n ? o.fallbacks.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n : [];\n return fallbacks.length > 0 ? { primary, fallbacks } : { primary };\n}\n\n/** Read `timeoutMs` from an image-generation model ref (only set when ref is an object). */\nexport function agentImageGenerationModelTimeoutMs(ref: unknown): number | null {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return null;\n const v = (ref as { timeoutMs?: unknown }).timeoutMs;\n return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : null;\n}\n\n/** Read `autoProviderFallback` from an image-generation model ref. Defaults to false. */\nexport function agentImageGenerationModelAutoProviderFallback(ref: unknown): boolean {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return false;\n return (ref as { autoProviderFallback?: unknown }).autoProviderFallback === true;\n}\n\n/**\n * PATCH body normalizer for `agents.defaults.imageGenerationModel`. Always\n * emits `{ primary, fallbacks?, timeoutMs?, autoProviderFallback? }`. Returns\n * `undefined` when the input has no usable primary so the caller can skip the\n * assignment.\n */\nexport function normalizePatchAgentImageGenerationModel(\n v: unknown,\n): { primary: string; fallbacks?: string[]; timeoutMs?: number; autoProviderFallback?: true } | undefined {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n const o = v as Record<string, unknown>;\n const primary = typeof o.primary === 'string' ? o.primary.trim() : '';\n if (!primary) return undefined;\n const out: { primary: string; fallbacks?: string[]; timeoutMs?: number; autoProviderFallback?: true } = { primary };\n const fallbacks = Array.isArray(o.fallbacks)\n ? o.fallbacks\n .filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n .map((x) => x.trim())\n : [];\n if (fallbacks.length > 0) out.fallbacks = fallbacks;\n if (typeof o.timeoutMs === 'number' && Number.isFinite(o.timeoutMs) && o.timeoutMs > 0) {\n out.timeoutMs = Math.floor(o.timeoutMs);\n }\n if (o.autoProviderFallback === true) out.autoProviderFallback = true;\n return out;\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"agent-model.js","names":[],"sources":["../../../../../src/gateway/hono/lib/agent-model.ts"],"sourcesContent":["import type { AgentTypedModel } from '../../../config/schema.js';\n\nconst TYPED_MODEL_ID_RE = /^[a-z][a-z0-9_-]{0,63}$/;\n\nfunction isValidProviderModelRef(ref: string): boolean {\n const trimmed = ref.trim();\n const idx = trimmed.indexOf('/');\n return idx > 0 && idx < trimmed.length - 1;\n}\n\n/**\n * Coerce PATCH body `models[]` into validated typed model entries.\n * Returns `null` to clear, `undefined` when input should be skipped, or cleaned array.\n * Empty array after filtering → `null` (same as clear).\n */\nexport function normalizePatchTypedModels(v: unknown): AgentTypedModel[] | null | undefined {\n if (v === undefined) return undefined;\n if (v === null) return null;\n if (!Array.isArray(v)) return undefined;\n\n const byId = new Map<string, AgentTypedModel>();\n for (const raw of v) {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue;\n const o = raw as Record<string, unknown>;\n const id = typeof o.id === 'string' ? o.id.trim() : '';\n const model = typeof o.model === 'string' ? o.model.trim() : '';\n if (!id || !TYPED_MODEL_ID_RE.test(id) || !model || !isValidProviderModelRef(model)) continue;\n const description =\n typeof o.description === 'string' && o.description.trim()\n ? o.description.trim().slice(0, 500)\n : undefined;\n byId.set(id, description ? { id, description, model } : { id, model });\n }\n\n if (byId.size === 0) return null;\n return [...byId.values()];\n}\n\n/** Read `primary` from an `AgentModelConfig` object. */\nexport function agentModelRefToString(ref: unknown): string | undefined {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return undefined;\n const p = (ref as { primary?: string }).primary;\n return typeof p === 'string' && p.trim() ? p : undefined;\n}\n\nexport function agentModelFallbacksToArray(ref: unknown): string[] {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return [];\n const f = (ref as { fallbacks?: unknown }).fallbacks;\n if (!Array.isArray(f)) return [];\n return f.filter((x): x is string => typeof x === 'string' && x.trim().length > 0);\n}\n\n/**\n * Coerce a PATCH body `model` value into the canonical `{ primary, fallbacks? }`\n * object form. Returns `undefined` when the input has no usable primary so the\n * caller can skip the assignment.\n */\nexport function normalizePatchAgentModel(v: unknown): { primary: string; fallbacks?: string[] } | undefined {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n const o = v as Record<string, unknown>;\n const primary = typeof o.primary === 'string' ? o.primary.trim() : '';\n if (!primary) return undefined;\n const fallbacks = Array.isArray(o.fallbacks)\n ? o.fallbacks.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n : [];\n return fallbacks.length > 0 ? { primary, fallbacks } : { primary };\n}\n\n/** Read `timeoutMs` from an image-generation model ref (only set when ref is an object). */\nexport function agentImageGenerationModelTimeoutMs(ref: unknown): number | null {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return null;\n const v = (ref as { timeoutMs?: unknown }).timeoutMs;\n return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : null;\n}\n\n/** Read `autoProviderFallback` from an image-generation model ref. Defaults to false. */\nexport function agentImageGenerationModelAutoProviderFallback(ref: unknown): boolean {\n if (!ref || typeof ref !== 'object' || Array.isArray(ref)) return false;\n return (ref as { autoProviderFallback?: unknown }).autoProviderFallback === true;\n}\n\n/**\n * PATCH body normalizer for `agents.defaults.imageGenerationModel`. Always\n * emits `{ primary, fallbacks?, timeoutMs?, autoProviderFallback? }`. Returns\n * `undefined` when the input has no usable primary so the caller can skip the\n * assignment.\n */\nexport function normalizePatchAgentImageGenerationModel(\n v: unknown,\n): { primary: string; fallbacks?: string[]; timeoutMs?: number; autoProviderFallback?: true } | undefined {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n const o = v as Record<string, unknown>;\n const primary = typeof o.primary === 'string' ? o.primary.trim() : '';\n if (!primary) return undefined;\n const out: { primary: string; fallbacks?: string[]; timeoutMs?: number; autoProviderFallback?: true } = { primary };\n const fallbacks = Array.isArray(o.fallbacks)\n ? o.fallbacks\n .filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n .map((x) => x.trim())\n : [];\n if (fallbacks.length > 0) out.fallbacks = fallbacks;\n if (typeof o.timeoutMs === 'number' && Number.isFinite(o.timeoutMs) && o.timeoutMs > 0) {\n out.timeoutMs = Math.floor(o.timeoutMs);\n }\n if (o.autoProviderFallback === true) out.autoProviderFallback = true;\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
|
|
2
|
+
import { resolveModelsJsonPath } from "../../../config/paths.js";
|
|
3
|
+
import { init_models_json, loadModelsJson } from "../../../config/models-json.js";
|
|
4
|
+
import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
|
|
5
|
+
import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
|
|
2
6
|
import { getAllProviders, init_providers, isProviderConfigured } from "../../../providers/index.js";
|
|
3
7
|
import { resolveShareConfig } from "../../../share/share-config.js";
|
|
4
8
|
import { normalizeConfiguredMcpServers } from "../../../config/mcp-config-normalize.js";
|
|
5
9
|
import { bundledChannelPlugins } from "../../../generated/bundled-channel-plugins.js";
|
|
6
10
|
import { listChannelPlugins, syncChannelPluginsFromManager } from "../../../channels/plugins/registry.js";
|
|
7
11
|
import { resolveCronConfigForWeb, resolveGoalsConfigForWeb, resolveSessionConfigForWeb, resolveUpdateConfigForWeb } from "../../../config/web-patch.js";
|
|
12
|
+
import { agentImageGenerationModelAutoProviderFallback, agentImageGenerationModelTimeoutMs, agentModelFallbacksToArray, agentModelRefToString } from "./agent-model.js";
|
|
13
|
+
import { GENERIC_MASKED_SECRET, maskSecretLength } from "./mask-secret-length.js";
|
|
8
14
|
import { safeToolsWebForGet } from "../../config-tools-web.js";
|
|
9
15
|
import { maskTunnelSecretForWeb } from "../../../tunnel/env.js";
|
|
10
|
-
import { agentImageGenerationModelAutoProviderFallback, agentImageGenerationModelTimeoutMs, agentModelFallbacksToArray, agentModelRefToString } from "./agent-model.js";
|
|
11
16
|
import { buildSafeProvidersConfigForWeb } from "./safe-providers-config.js";
|
|
12
17
|
import { maskSttConfigForWeb, maskTtsConfigForWeb } from "./safe-voice-config.js";
|
|
13
18
|
//#region src/gateway/hono/lib/config-payload.ts
|
|
14
19
|
init_agent_scope();
|
|
20
|
+
init_credentials();
|
|
21
|
+
init_models_json();
|
|
15
22
|
init_providers();
|
|
23
|
+
init_plugin_registry();
|
|
24
|
+
function readModelsJsonProviderApiKey(providerId) {
|
|
25
|
+
const { config } = loadModelsJson(resolveModelsJsonPath());
|
|
26
|
+
const key = (config.providers?.[providerId])?.apiKey;
|
|
27
|
+
return typeof key === "string" && key.trim() ? key.trim() : void 0;
|
|
28
|
+
}
|
|
29
|
+
/** Length-preserving mask for LLM provider keys in GET `/api/config`. */
|
|
30
|
+
async function maskLlmProviderApiKeyForWeb(provider) {
|
|
31
|
+
const stored = await new CredentialResolver().revealGatewayStoredApiKey(provider);
|
|
32
|
+
if (stored) return maskSecretLength(stored);
|
|
33
|
+
const fromModelsJson = readModelsJsonProviderApiKey(provider);
|
|
34
|
+
if (fromModelsJson) return maskSecretLength(fromModelsJson);
|
|
35
|
+
if (getProviderRegistry().has(provider)) return "";
|
|
36
|
+
if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
16
39
|
/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */
|
|
17
40
|
function buildSafeMcpConfigForWeb(config) {
|
|
18
41
|
const mcp = config.mcp;
|
|
@@ -26,7 +49,7 @@ function maskBrowserCloudConfigForWeb(cloud) {
|
|
|
26
49
|
if (!cloud || typeof cloud !== "object" || Array.isArray(cloud)) return null;
|
|
27
50
|
const raw = cloud;
|
|
28
51
|
const safe = {};
|
|
29
|
-
if (typeof raw.apiKey === "string" && raw.apiKey.trim()) safe.apiKey =
|
|
52
|
+
if (typeof raw.apiKey === "string" && raw.apiKey.trim()) safe.apiKey = maskSecretLength(raw.apiKey);
|
|
30
53
|
if (typeof raw.projectId === "string" && raw.projectId.trim()) safe.projectId = raw.projectId.trim();
|
|
31
54
|
if (typeof raw.region === "string" && raw.region.trim()) safe.region = raw.region.trim();
|
|
32
55
|
return Object.keys(safe).length > 0 ? safe : null;
|
|
@@ -120,7 +143,7 @@ async function buildSafeWebConfigPayload(service) {
|
|
|
120
143
|
}
|
|
121
144
|
},
|
|
122
145
|
channels: channelsPayload,
|
|
123
|
-
providers: Object.fromEntries(await Promise.all(getAllProviders().map(async (provider) => [provider, await
|
|
146
|
+
providers: Object.fromEntries(await Promise.all(getAllProviders().map(async (provider) => [provider, await maskLlmProviderApiKeyForWeb(provider)]))),
|
|
124
147
|
/** Masked `cfg.providers` for capability keys (image / STT / etc.). */
|
|
125
148
|
providersConfig: buildSafeProvidersConfigForWeb(config.providers),
|
|
126
149
|
gateway: {
|
|
@@ -134,8 +157,8 @@ async function buildSafeWebConfigPayload(service) {
|
|
|
134
157
|
security: { strict: config.gateway?.security?.strict === true },
|
|
135
158
|
auth: {
|
|
136
159
|
mode: config.gateway?.auth?.mode || "token",
|
|
137
|
-
token: config.gateway?.auth?.token
|
|
138
|
-
password: config.gateway?.auth?.password ?
|
|
160
|
+
token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : "",
|
|
161
|
+
password: config.gateway?.auth?.password ? maskSecretLength(config.gateway.auth.password) : "",
|
|
139
162
|
trustedProxy: config.gateway?.auth?.trustedProxy ? {
|
|
140
163
|
userHeader: config.gateway.auth.trustedProxy.userHeader,
|
|
141
164
|
requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-payload.js","names":[],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../../channels/plugins/registry.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveCronConfigForWeb,\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { bundledChannelPlugins } from '../../../generated/bundled-channel-plugins.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport {\n agentImageGenerationModelAutoProviderFallback,\n agentImageGenerationModelTimeoutMs,\n agentModelFallbacksToArray,\n agentModelRefToString,\n} from './agent-model.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = '***';\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: Config['agents']['defaults']['browser'] | undefined) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: browser.enabled !== false,\n headless: browser.headless === true,\n allowPrivateUrls: browser.allowPrivateUrls === true,\n commandTimeout:\n typeof browser.commandTimeout === 'number' && Number.isFinite(browser.commandTimeout)\n ? Math.floor(browser.commandTimeout)\n : null,\n backend:\n browser.backend === 'local' ||\n browser.backend === 'cdp' ||\n browser.backend === 'cloud' ||\n browser.backend === 'extension' ||\n browser.backend === 'cloakbrowser'\n ? browser.backend\n : null,\n cloudProvider:\n browser.cloudProvider === 'browserbase' || browser.cloudProvider === 'browser-use'\n ? browser.cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb(browser.cloud),\n cdpUrl: typeof browser.cdpUrl === 'string' && browser.cdpUrl.trim() ? browser.cdpUrl.trim() : null,\n extension: browser.extension && typeof browser.extension === 'object' && !Array.isArray(browser.extension)\n ? browser.extension\n : null,\n cloakbrowser:\n browser.cloakbrowser && typeof browser.cloakbrowser === 'object' && !Array.isArray(browser.cloakbrowser)\n ? browser.cloakbrowser\n : null,\n humanize: typeof browser.humanize === 'boolean' ? browser.humanize : null,\n humanPreset: browser.humanPreset === 'default' || browser.humanPreset === 'careful' ? browser.humanPreset : null,\n dialogPolicy:\n browser.dialogPolicy === 'must_respond' ||\n browser.dialogPolicy === 'auto_accept' ||\n browser.dialogPolicy === 'auto_dismiss'\n ? browser.dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof browser.dialogTimeoutSeconds === 'number' && Number.isFinite(browser.dialogTimeoutSeconds)\n ? Math.floor(browser.dialogTimeoutSeconds)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService) {\n const config = service.currentConfig;\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n const channelsPayload = Object.fromEntries(\n listChannelPlugins().map((plugin) => {\n if (plugin.configSurface) {\n return [plugin.id, plugin.configSurface.buildConfigSurface(config)];\n }\n const channelCfg = config.channels?.[plugin.id] as Record<string, unknown> | undefined;\n return [\n plugin.id,\n {\n enabled: channelCfg?.enabled ?? false,\n configured: plugin.config.listAccountIds(config).length > 0,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n ...(typeof e.name === 'string' && e.name.trim() ? { name: e.name.trim() } : {}),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.model) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.model),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelTimeoutMs: agentImageGenerationModelTimeoutMs(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelAutoProviderFallback: agentImageGenerationModelAutoProviderFallback(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: buildSafeBrowserConfigForWeb(config.agents?.defaults?.browser),\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n (await isProviderConfigured(provider)) ? '***' : '',\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token || '',\n password: config.gateway?.auth?.password ? '••••••••••••' : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'skillhub',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n cron: resolveCronConfigForWeb(config),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-payload.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../../channels/plugins/registry.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport { bundledChannelPlugins } from '../../../generated/bundled-channel-plugins.js';\nimport {\n GENERIC_MASKED_SECRET,\n maskSecretLength,\n} from './mask-secret-length.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveCronConfigForWeb,\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { CredentialResolver } from '../../../auth/credentials.js';\nimport { loadModelsJson, getModelsJsonPath } from '../../../config/models-json.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport {\n agentImageGenerationModelAutoProviderFallback,\n agentImageGenerationModelTimeoutMs,\n agentModelFallbacksToArray,\n agentModelRefToString,\n} from './agent-model.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\nfunction readModelsJsonProviderApiKey(providerId: string): string | undefined {\n const { config } = loadModelsJson(getModelsJsonPath());\n const entry = config.providers?.[providerId];\n const key = entry?.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\n/** Length-preserving mask for LLM provider keys in GET `/api/config`. */\nasync function maskLlmProviderApiKeyForWeb(provider: string): Promise<string> {\n const resolver = new CredentialResolver();\n const stored = await resolver.revealGatewayStoredApiKey(provider);\n if (stored) return maskSecretLength(stored);\n\n const fromModelsJson = readModelsJsonProviderApiKey(provider);\n if (fromModelsJson) return maskSecretLength(fromModelsJson);\n\n // Extension plugins manage their own auth; don't show a fake gateway key mask.\n if (getProviderRegistry().has(provider)) return '';\n\n if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;\n return '';\n}\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = maskSecretLength(raw.apiKey);\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: Config['agents']['defaults']['browser'] | undefined) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: browser.enabled !== false,\n headless: browser.headless === true,\n allowPrivateUrls: browser.allowPrivateUrls === true,\n commandTimeout:\n typeof browser.commandTimeout === 'number' && Number.isFinite(browser.commandTimeout)\n ? Math.floor(browser.commandTimeout)\n : null,\n backend:\n browser.backend === 'local' ||\n browser.backend === 'cdp' ||\n browser.backend === 'cloud' ||\n browser.backend === 'extension' ||\n browser.backend === 'cloakbrowser'\n ? browser.backend\n : null,\n cloudProvider:\n browser.cloudProvider === 'browserbase' || browser.cloudProvider === 'browser-use'\n ? browser.cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb(browser.cloud),\n cdpUrl: typeof browser.cdpUrl === 'string' && browser.cdpUrl.trim() ? browser.cdpUrl.trim() : null,\n extension: browser.extension && typeof browser.extension === 'object' && !Array.isArray(browser.extension)\n ? browser.extension\n : null,\n cloakbrowser:\n browser.cloakbrowser && typeof browser.cloakbrowser === 'object' && !Array.isArray(browser.cloakbrowser)\n ? browser.cloakbrowser\n : null,\n humanize: typeof browser.humanize === 'boolean' ? browser.humanize : null,\n humanPreset: browser.humanPreset === 'default' || browser.humanPreset === 'careful' ? browser.humanPreset : null,\n dialogPolicy:\n browser.dialogPolicy === 'must_respond' ||\n browser.dialogPolicy === 'auto_accept' ||\n browser.dialogPolicy === 'auto_dismiss'\n ? browser.dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof browser.dialogTimeoutSeconds === 'number' && Number.isFinite(browser.dialogTimeoutSeconds)\n ? Math.floor(browser.dialogTimeoutSeconds)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService) {\n const config = service.currentConfig;\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n const channelsPayload = Object.fromEntries(\n listChannelPlugins().map((plugin) => {\n if (plugin.configSurface) {\n return [plugin.id, plugin.configSurface.buildConfigSurface(config)];\n }\n const channelCfg = config.channels?.[plugin.id] as Record<string, unknown> | undefined;\n return [\n plugin.id,\n {\n enabled: channelCfg?.enabled ?? false,\n configured: plugin.config.listAccountIds(config).length > 0,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n ...(typeof e.name === 'string' && e.name.trim() ? { name: e.name.trim() } : {}),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.model) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.model),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelTimeoutMs: agentImageGenerationModelTimeoutMs(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelAutoProviderFallback: agentImageGenerationModelAutoProviderFallback(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: buildSafeBrowserConfigForWeb(config.agents?.defaults?.browser),\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n await maskLlmProviderApiKeyForWeb(provider),\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : '',\n password: config.gateway?.auth?.password\n ? maskSecretLength(config.gateway.auth.password)\n : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'skillhub',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n cron: resolveCronConfigForWeb(config),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Fallback mask when the real secret length is unknown (e.g. env-sourced keys). */
|
|
2
|
+
export declare const GENERIC_MASKED_SECRET = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
3
|
+
/** Length-preserving mask for secrets returned to the web console. */
|
|
4
|
+
export declare function maskSecretLength(secret: string): string;
|
|
5
|
+
/** True when a PATCH body carries a masked sentinel instead of a new secret. */
|
|
6
|
+
export declare function isMaskedSecretPatchValue(value: string): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/gateway/hono/lib/mask-secret-length.ts
|
|
2
|
+
/** Fallback mask when the real secret length is unknown (e.g. env-sourced keys). */
|
|
3
|
+
const GENERIC_MASKED_SECRET = "••••••••••••";
|
|
4
|
+
/** Length-preserving mask for secrets returned to the web console. */
|
|
5
|
+
function maskSecretLength(secret) {
|
|
6
|
+
const trimmed = secret.trim();
|
|
7
|
+
return trimmed ? "•".repeat(trimmed.length) : "";
|
|
8
|
+
}
|
|
9
|
+
/** True when a PATCH body carries a masked sentinel instead of a new secret. */
|
|
10
|
+
function isMaskedSecretPatchValue(value) {
|
|
11
|
+
return value === "***" || value === "••••••••••••" || /^•+$/.test(value);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { GENERIC_MASKED_SECRET, isMaskedSecretPatchValue, maskSecretLength };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=mask-secret-length.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-secret-length.js","names":[],"sources":["../../../../../src/gateway/hono/lib/mask-secret-length.ts"],"sourcesContent":["/** Fallback mask when the real secret length is unknown (e.g. env-sourced keys). */\nexport const GENERIC_MASKED_SECRET = '••••••••••••';\n\n/** Length-preserving mask for secrets returned to the web console. */\nexport function maskSecretLength(secret: string): string {\n const trimmed = secret.trim();\n return trimmed ? '•'.repeat(trimmed.length) : '';\n}\n\n/** True when a PATCH body carries a masked sentinel instead of a new secret. */\nexport function isMaskedSecretPatchValue(value: string): boolean {\n return value === '***' || value === GENERIC_MASKED_SECRET || /^•+$/.test(value);\n}\n"],"mappings":";;AACA,MAAa,wBAAwB;;AAGrC,SAAgB,iBAAiB,QAAwB;CACvD,MAAM,UAAU,OAAO,MAAM;AAC7B,QAAO,UAAU,IAAI,OAAO,QAAQ,OAAO,GAAG;;;AAIhD,SAAgB,yBAAyB,OAAwB;AAC/D,QAAO,UAAU,SAAS,UAAA,kBAAmC,OAAO,KAAK,MAAM"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Config } from '../../../config/schema.js';
|
|
2
2
|
/**
|
|
3
3
|
* Per-vendor slice of {@link Config.providers} safe for GET `/api/config`.
|
|
4
|
-
* Secrets are never sent verbatim; `apiKey` is
|
|
4
|
+
* Secrets are never sent verbatim; `apiKey` is length-preserving bullets when set, else empty.
|
|
5
5
|
*/
|
|
6
6
|
export type SafeProviderAuthEntry = {
|
|
7
7
|
apiKey: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { maskSecretLength } from "./mask-secret-length.js";
|
|
1
2
|
//#region src/gateway/hono/lib/safe-providers-config.ts
|
|
2
3
|
/**
|
|
3
4
|
* Build a redacted `providersConfig` map for the gateway console (image / audio / video keys).
|
|
@@ -9,7 +10,7 @@ function buildSafeProvidersConfigForWeb(providers) {
|
|
|
9
10
|
if (!id) continue;
|
|
10
11
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
11
12
|
const o = raw;
|
|
12
|
-
const entry = { apiKey: typeof o.apiKey === "string" && o.apiKey.trim() ?
|
|
13
|
+
const entry = { apiKey: typeof o.apiKey === "string" && o.apiKey.trim() ? maskSecretLength(o.apiKey) : "" };
|
|
13
14
|
if (typeof o.region === "string" && o.region.trim()) entry.region = o.region.trim();
|
|
14
15
|
if (typeof o.baseUrl === "string" && o.baseUrl.trim()) entry.baseUrl = o.baseUrl.trim();
|
|
15
16
|
if (typeof o.imageBaseUrl === "string" && o.imageBaseUrl.trim()) entry.imageBaseUrl = o.imageBaseUrl.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-providers-config.js","names":[],"sources":["../../../../../src/gateway/hono/lib/safe-providers-config.ts"],"sourcesContent":["import type { Config } from '../../../config/schema.js';\n\n/**\n * Per-vendor slice of {@link Config.providers} safe for GET `/api/config`.\n * Secrets are never sent verbatim; `apiKey` is
|
|
1
|
+
{"version":3,"file":"safe-providers-config.js","names":[],"sources":["../../../../../src/gateway/hono/lib/safe-providers-config.ts"],"sourcesContent":["import type { Config } from '../../../config/schema.js';\nimport { maskSecretLength } from './mask-secret-length.js';\n\n/**\n * Per-vendor slice of {@link Config.providers} safe for GET `/api/config`.\n * Secrets are never sent verbatim; `apiKey` is length-preserving bullets when set, else empty.\n */\nexport type SafeProviderAuthEntry = {\n apiKey: string;\n region?: string;\n baseUrl?: string;\n imageBaseUrl?: string;\n};\n\n/**\n * Build a redacted `providersConfig` map for the gateway console (image / audio / video keys).\n */\nexport function buildSafeProvidersConfigForWeb(\n providers: Config['providers'] | undefined,\n): Record<string, SafeProviderAuthEntry> {\n if (!providers || typeof providers !== 'object') return {};\n const out: Record<string, SafeProviderAuthEntry> = {};\n for (const [id, raw] of Object.entries(providers)) {\n if (!id) continue;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue;\n const o = raw as Record<string, unknown>;\n const apiKey =\n typeof o.apiKey === 'string' && o.apiKey.trim() ? maskSecretLength(o.apiKey) : '';\n const entry: SafeProviderAuthEntry = { apiKey };\n if (typeof o.region === 'string' && o.region.trim()) {\n entry.region = o.region.trim();\n }\n if (typeof o.baseUrl === 'string' && o.baseUrl.trim()) {\n entry.baseUrl = o.baseUrl.trim();\n }\n if (typeof o.imageBaseUrl === 'string' && o.imageBaseUrl.trim()) {\n entry.imageBaseUrl = o.imageBaseUrl.trim();\n }\n out[id] = entry;\n }\n return out;\n}\n"],"mappings":";;;;;AAiBA,SAAgB,+BACd,WACuC;AACvC,KAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO,EAAE;CAC1D,MAAM,MAA6C,EAAE;AACrD,MAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,UAAU,EAAE;AACjD,MAAI,CAAC,GAAI;AACT,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE;EAC3D,MAAM,IAAI;EAGV,MAAM,QAA+B,EAAE,QADrC,OAAO,EAAE,WAAW,YAAY,EAAE,OAAO,MAAM,GAAG,iBAAiB,EAAE,OAAO,GAAG,IAClC;AAC/C,MAAI,OAAO,EAAE,WAAW,YAAY,EAAE,OAAO,MAAM,CACjD,OAAM,SAAS,EAAE,OAAO,MAAM;AAEhC,MAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,MAAM,CACnD,OAAM,UAAU,EAAE,QAAQ,MAAM;AAElC,MAAI,OAAO,EAAE,iBAAiB,YAAY,EAAE,aAAa,MAAM,CAC7D,OAAM,eAAe,EAAE,aAAa,MAAM;AAE5C,MAAI,MAAM;;AAEZ,QAAO"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { maskSecretLength } from "./mask-secret-length.js";
|
|
1
2
|
import { isMaskedApiKey } from "../../config-tools-web.js";
|
|
2
3
|
//#region src/gateway/hono/lib/safe-voice-config.ts
|
|
3
4
|
function maskProviderSlice(slice) {
|
|
4
5
|
const next = { ...slice };
|
|
5
|
-
if (typeof next.apiKey === "string" && next.apiKey.trim()) next.apiKey =
|
|
6
|
+
if (typeof next.apiKey === "string" && next.apiKey.trim()) next.apiKey = maskSecretLength(next.apiKey);
|
|
6
7
|
return next;
|
|
7
8
|
}
|
|
8
9
|
function mergeApiKeyField(incoming, previous) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-voice-config.js","names":[],"sources":["../../../../../src/gateway/hono/lib/safe-voice-config.ts"],"sourcesContent":["import { isMaskedApiKey } from '../../config-tools-web.js';\n\nfunction maskProviderSlice(slice: Record<string, unknown>): Record<string, unknown> {\n const next = { ...slice };\n if (typeof next.apiKey === 'string' && next.apiKey.trim()) {\n next.apiKey =
|
|
1
|
+
{"version":3,"file":"safe-voice-config.js","names":[],"sources":["../../../../../src/gateway/hono/lib/safe-voice-config.ts"],"sourcesContent":["import { isMaskedApiKey } from '../../config-tools-web.js';\nimport { maskSecretLength } from './mask-secret-length.js';\n\nfunction maskProviderSlice(slice: Record<string, unknown>): Record<string, unknown> {\n const next = { ...slice };\n if (typeof next.apiKey === 'string' && next.apiKey.trim()) {\n next.apiKey = maskSecretLength(next.apiKey);\n }\n return next;\n}\n\nfunction mergeApiKeyField(incoming: unknown, previous: unknown): unknown {\n if (typeof incoming !== 'string') return previous;\n if (isMaskedApiKey(incoming) && typeof previous === 'string' && previous.trim()) {\n return previous;\n }\n return incoming;\n}\n\nfunction mergeProviderSlice(\n incoming: Record<string, unknown>,\n previous: Record<string, unknown>,\n): Record<string, unknown> {\n const next = { ...previous, ...incoming };\n if ('apiKey' in incoming) {\n next.apiKey = mergeApiKeyField(incoming.apiKey, previous.apiKey);\n }\n return next;\n}\n\nfunction maskProviderMap(providers: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [id, value] of Object.entries(providers)) {\n out[id] =\n value && typeof value === 'object' && !Array.isArray(value)\n ? maskProviderSlice(value as Record<string, unknown>)\n : value;\n }\n return out;\n}\n\nfunction mergeProviderMap(\n incoming: Record<string, unknown>,\n previous: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...previous };\n for (const [id, value] of Object.entries(incoming)) {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[id] = mergeProviderSlice(value as Record<string, unknown>, (out[id] ?? {}) as Record<string, unknown>);\n } else {\n out[id] = value;\n }\n }\n return out;\n}\n\nfunction maskVoiceConfigForWeb(cfg: unknown): unknown {\n if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) return cfg;\n const raw = { ...(cfg as Record<string, unknown>) };\n if (raw.providers && typeof raw.providers === 'object' && !Array.isArray(raw.providers)) {\n raw.providers = maskProviderMap(raw.providers as Record<string, unknown>);\n }\n return raw;\n}\n\n/** Mask STT api keys for GET `/api/config`. */\nexport function maskSttConfigForWeb(stt: unknown): unknown {\n return maskVoiceConfigForWeb(stt);\n}\n\n/** Mask TTS api keys for GET `/api/config`. */\nexport function maskTtsConfigForWeb(tts: unknown): unknown {\n return maskVoiceConfigForWeb(tts);\n}\n\nfunction mergeVoiceConfigPatch(previous: unknown, incoming: unknown): unknown {\n if (!incoming || typeof incoming !== 'object' || Array.isArray(incoming)) return previous ?? incoming;\n const prev = previous && typeof previous === 'object' && !Array.isArray(previous)\n ? (previous as Record<string, unknown>)\n : {};\n const patch = incoming as Record<string, unknown>;\n const next: Record<string, unknown> = { ...prev, ...patch };\n if (patch.providers && typeof patch.providers === 'object' && !Array.isArray(patch.providers)) {\n next.providers = mergeProviderMap(\n patch.providers as Record<string, unknown>,\n (prev.providers ?? {}) as Record<string, unknown>,\n );\n }\n return next;\n}\n\n/** Merge incoming STT patch, preserving api keys when the UI sends masked sentinels. */\nexport function mergeSttConfigPatch(previous: unknown, incoming: unknown): unknown {\n return mergeVoiceConfigPatch(previous, incoming);\n}\n\n/** Merge incoming TTS patch, preserving api keys when the UI sends masked sentinels. */\nexport function mergeTtsConfigPatch(previous: unknown, incoming: unknown): unknown {\n return mergeVoiceConfigPatch(previous, incoming);\n}\n"],"mappings":";;;AAGA,SAAS,kBAAkB,OAAyD;CAClF,MAAM,OAAO,EAAE,GAAG,OAAO;AACzB,KAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,MAAM,CACvD,MAAK,SAAS,iBAAiB,KAAK,OAAO;AAE7C,QAAO;;AAGT,SAAS,iBAAiB,UAAmB,UAA4B;AACvE,KAAI,OAAO,aAAa,SAAU,QAAO;AACzC,KAAI,eAAe,SAAS,IAAI,OAAO,aAAa,YAAY,SAAS,MAAM,CAC7E,QAAO;AAET,QAAO;;AAGT,SAAS,mBACP,UACA,UACyB;CACzB,MAAM,OAAO;EAAE,GAAG;EAAU,GAAG;EAAU;AACzC,KAAI,YAAY,SACd,MAAK,SAAS,iBAAiB,SAAS,QAAQ,SAAS,OAAO;AAElE,QAAO;;AAGT,SAAS,gBAAgB,WAA6D;CACpF,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,UAAU,CACjD,KAAI,MACF,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,GACvD,kBAAkB,MAAiC,GACnD;AAER,QAAO;;AAGT,SAAS,iBACP,UACA,UACyB;CACzB,MAAM,MAA+B,EAAE,GAAG,UAAU;AACpD,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,CAChD,KAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAC7D,KAAI,MAAM,mBAAmB,OAAmC,IAAI,OAAO,EAAE,CAA6B;KAE1G,KAAI,MAAM;AAGd,QAAO;;AAGT,SAAS,sBAAsB,KAAuB;AACpD,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO;CAClE,MAAM,MAAM,EAAE,GAAI,KAAiC;AACnD,KAAI,IAAI,aAAa,OAAO,IAAI,cAAc,YAAY,CAAC,MAAM,QAAQ,IAAI,UAAU,CACrF,KAAI,YAAY,gBAAgB,IAAI,UAAqC;AAE3E,QAAO;;;AAIT,SAAgB,oBAAoB,KAAuB;AACzD,QAAO,sBAAsB,IAAI;;;AAInC,SAAgB,oBAAoB,KAAuB;AACzD,QAAO,sBAAsB,IAAI;;AAGnC,SAAS,sBAAsB,UAAmB,UAA4B;AAC5E,KAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,SAAS,CAAE,QAAO,YAAY;CAC7F,MAAM,OAAO,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,SAAS,GAC5E,WACD,EAAE;CACN,MAAM,QAAQ;CACd,MAAM,OAAgC;EAAE,GAAG;EAAM,GAAG;EAAO;AAC3D,KAAI,MAAM,aAAa,OAAO,MAAM,cAAc,YAAY,CAAC,MAAM,QAAQ,MAAM,UAAU,CAC3F,MAAK,YAAY,iBACf,MAAM,WACL,KAAK,aAAa,EAAE,CACtB;AAEH,QAAO;;;AAIT,SAAgB,oBAAoB,UAAmB,UAA4B;AACjF,QAAO,sBAAsB,UAAU,SAAS;;;AAIlD,SAAgB,oBAAoB,UAAmB,UAA4B;AACjF,QAAO,sBAAsB,UAAU,SAAS"}
|
|
@@ -45,7 +45,7 @@ const SESSION_TTL_MS = 600 * 1e3;
|
|
|
45
45
|
setInterval(() => {
|
|
46
46
|
const now = Date.now();
|
|
47
47
|
for (const [id, session] of oauthSessions.entries()) if (now > session.expiresAt) {
|
|
48
|
-
|
|
48
|
+
cancelOAuthSession(session, "OAuth flow expired");
|
|
49
49
|
oauthSessions.delete(id);
|
|
50
50
|
log.debug({ sessionId: id }, "Cleaned up expired OAuth session");
|
|
51
51
|
}
|
|
@@ -53,6 +53,14 @@ setInterval(() => {
|
|
|
53
53
|
function generateSessionId() {
|
|
54
54
|
return `oauth_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
55
55
|
}
|
|
56
|
+
function cancelOAuthSession(session, message = "OAuth flow cancelled") {
|
|
57
|
+
if (session.abortController) session.abortController.abort();
|
|
58
|
+
if (session.manualCodeResolve) session.manualCodeResolve("");
|
|
59
|
+
session.manualCodeResolve = void 0;
|
|
60
|
+
session.manualCodeReject = void 0;
|
|
61
|
+
session.status = "cancelled";
|
|
62
|
+
session.message = message;
|
|
63
|
+
}
|
|
56
64
|
function createOAuthAsyncHandler(service) {
|
|
57
65
|
const oauth = new Hono();
|
|
58
66
|
/**
|
|
@@ -142,14 +150,7 @@ function createOAuthAsyncHandler(service) {
|
|
|
142
150
|
const sessionId = c.req.param("sessionId");
|
|
143
151
|
const session = oauthSessions.get(sessionId);
|
|
144
152
|
if (!session) return c.json({ error: "Session not found" }, 404);
|
|
145
|
-
|
|
146
|
-
if (session.manualCodeReject) {
|
|
147
|
-
session.manualCodeReject(/* @__PURE__ */ new Error("OAuth cancelled by user"));
|
|
148
|
-
session.manualCodeReject = void 0;
|
|
149
|
-
session.manualCodeResolve = void 0;
|
|
150
|
-
}
|
|
151
|
-
session.status = "cancelled";
|
|
152
|
-
session.message = "OAuth flow cancelled";
|
|
153
|
+
cancelOAuthSession(session);
|
|
153
154
|
return c.json({
|
|
154
155
|
ok: true,
|
|
155
156
|
payload: { message: "OAuth flow cancelled" }
|
|
@@ -162,8 +163,7 @@ function createOAuthAsyncHandler(service) {
|
|
|
162
163
|
oauth.delete("/:sessionId", (c) => {
|
|
163
164
|
const sessionId = c.req.param("sessionId");
|
|
164
165
|
if (oauthSessions.has(sessionId)) {
|
|
165
|
-
|
|
166
|
-
if (session.abortController) session.abortController.abort();
|
|
166
|
+
cancelOAuthSession(oauthSessions.get(sessionId));
|
|
167
167
|
oauthSessions.delete(sessionId);
|
|
168
168
|
}
|
|
169
169
|
return c.json({ ok: true });
|
|
@@ -197,6 +197,14 @@ async function runOAuthFlow(session, oauthProvider, _service) {
|
|
|
197
197
|
session.message = "Complete authorization in browser";
|
|
198
198
|
}
|
|
199
199
|
},
|
|
200
|
+
onDeviceCode: (info) => {
|
|
201
|
+
session.status = "waiting_auth";
|
|
202
|
+
session.authUrl = info.verificationUri;
|
|
203
|
+
session.deviceCode = info.userCode;
|
|
204
|
+
session.verificationUri = info.verificationUri;
|
|
205
|
+
session.instructions = `Enter code ${info.userCode}`;
|
|
206
|
+
session.message = `Open ${info.verificationUri} and enter code ${info.userCode}`;
|
|
207
|
+
},
|
|
200
208
|
onPrompt: async (prompt) => {
|
|
201
209
|
session.status = "waiting_code";
|
|
202
210
|
session.deviceCode = prompt.deviceCode;
|
|
@@ -221,13 +229,30 @@ async function runOAuthFlow(session, oauthProvider, _service) {
|
|
|
221
229
|
if (manualCodePromise) return manualCodePromise;
|
|
222
230
|
return "";
|
|
223
231
|
},
|
|
232
|
+
onSelect: async (prompt) => {
|
|
233
|
+
const browserOption = prompt.options.find((option) => option.id === "browser");
|
|
234
|
+
const firstOption = prompt.options[0];
|
|
235
|
+
const selectedOption = browserOption ?? firstOption;
|
|
236
|
+
if (!selectedOption) throw new Error("OAuth login did not provide any selectable auth method");
|
|
237
|
+
log.debug({
|
|
238
|
+
sessionId: session.id,
|
|
239
|
+
provider: session.provider,
|
|
240
|
+
selected: selectedOption.id
|
|
241
|
+
}, "Selected OAuth auth method");
|
|
242
|
+
return selectedOption.id;
|
|
243
|
+
},
|
|
224
244
|
signal: abortController.signal
|
|
225
245
|
};
|
|
226
246
|
try {
|
|
227
247
|
const credentials = await oauthProvider.login(callbacks);
|
|
228
248
|
setOAuthCredentialsToCache(session.provider, credentials);
|
|
229
|
-
|
|
230
|
-
|
|
249
|
+
await new CredentialResolver().saveOAuthToken(session.provider, {
|
|
250
|
+
access: oauthProvider.getApiKey(credentials),
|
|
251
|
+
refresh: credentials.refresh,
|
|
252
|
+
expiresAt: credentials.expires,
|
|
253
|
+
scope: Array.isArray(credentials.scope) ? credentials.scope.filter((value) => typeof value === "string") : void 0,
|
|
254
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
255
|
+
});
|
|
231
256
|
session.status = "completed";
|
|
232
257
|
session.credentials = credentials;
|
|
233
258
|
session.message = "OAuth login successful";
|
|
@@ -236,9 +261,9 @@ async function runOAuthFlow(session, oauthProvider, _service) {
|
|
|
236
261
|
provider: session.provider
|
|
237
262
|
}, "OAuth login completed");
|
|
238
263
|
} catch (err) {
|
|
239
|
-
if (abortController.signal.aborted) {
|
|
264
|
+
if (abortController.signal.aborted || session.status === "cancelled") {
|
|
240
265
|
session.status = "cancelled";
|
|
241
|
-
session.message
|
|
266
|
+
session.message ??= "OAuth flow cancelled by user";
|
|
242
267
|
} else {
|
|
243
268
|
session.status = "failed";
|
|
244
269
|
session.error = formatOAuthAsyncError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-async.js","names":["googleGeminiCliOAuthProvider","googleAntigravityOAuthProvider"],"sources":["../../../../src/gateway/hono/oauth-async.ts"],"sourcesContent":["/**\n * Async OAuth Handler\n * \n * Provides non-blocking OAuth flow with session-based state management.\n * This allows OAuth flows that require user interaction (browser login) \n * without blocking the HTTP request.\n */\n\nimport { Hono } from 'hono';\nimport type { GatewayService } from '../service.js';\nimport { \n type OAuthProviderInterface, \n type OAuthLoginCallbacks,\n type OAuthCredentials \n} from '../../auth/oauth/types.js';\nimport {\n kimiCodingOAuthProvider,\n minimaxOAuthProvider,\n minimaxCnOAuthProvider,\n anthropicOAuthProvider,\n githubCopilotOAuthProvider,\n googleGeminiCliOAuthProvider,\n googleAntigravityOAuthProvider,\n openaiCodexOAuthProvider,\n} from '../../auth/oauth/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { CredentialResolver } from '../../auth/credentials.js';\n\nconst log = createLogger('OAuthAsync');\n\n/** User-facing message when undici/fetch fails (often DNS, firewall, or wrong machine for localhost callback). */\nfunction formatOAuthAsyncError(err: unknown): string {\n\tconst base = err instanceof Error ? err.message : 'OAuth login failed';\n\tconst cause =\n\t\terr instanceof Error && err.cause instanceof Error\n\t\t\t? err.cause.message\n\t\t\t: err instanceof Error && typeof err.cause === 'string'\n\t\t\t\t? err.cause\n\t\t\t\t: '';\n\tconst detail = cause ? ` (${cause})` : '';\n\tif (/^fetch failed$/i.test(base) || base.includes('fetch failed')) {\n\t\treturn (\n\t\t\t`Network request failed${detail}. If the browser opened on another device, the redirect goes to that device's localhost — ` +\n\t\t\t`copy the full URL from the browser address bar after sign-in (starts with http://127.0.0.1 or http://localhost) and paste it below. ` +\n\t\t\t`Otherwise check VPN/proxy/DNS/firewall access to Google OAuth.`\n\t\t);\n\t}\n\treturn base;\n}\n\n// Static OAuth providers map\nconst OAUTH_PROVIDERS: Record<string, OAuthProviderInterface> = {\n 'kimi-coding': kimiCodingOAuthProvider,\n 'minimax': minimaxOAuthProvider,\n 'minimax-cn': minimaxCnOAuthProvider,\n 'anthropic': anthropicOAuthProvider,\n 'github-copilot': githubCopilotOAuthProvider,\n 'google-gemini-cli': googleGeminiCliOAuthProvider,\n 'google-antigravity': googleAntigravityOAuthProvider,\n 'openai-codex': openaiCodexOAuthProvider,\n};\n\n// OAuth session state\ninterface OAuthSession {\n id: string;\n provider: string;\n status: 'pending' | 'waiting_auth' | 'waiting_code' | 'completed' | 'failed' | 'cancelled';\n authUrl?: string;\n instructions?: string;\n deviceCode?: string;\n verificationUri?: string;\n message?: string;\n error?: string;\n credentials?: OAuthCredentials;\n createdAt: number;\n expiresAt: number;\n abortController?: AbortController;\n manualCodeResolve?: (code: string) => void;\n manualCodeReject?: (error: Error) => void;\n}\n\n// In-memory session store (could be moved to Redis for production)\nconst oauthSessions = new Map<string, OAuthSession>();\nconst SESSION_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\n// Clean up expired sessions periodically\nsetInterval(() => {\n const now = Date.now();\n for (const [id, session] of oauthSessions.entries()) {\n if (now > session.expiresAt) {\n if (session.abortController) {\n session.abortController.abort();\n }\n oauthSessions.delete(id);\n log.debug({ sessionId: id }, 'Cleaned up expired OAuth session');\n }\n }\n}, 60 * 1000);\n\nfunction generateSessionId(): string {\n return `oauth_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;\n}\n\nexport function createOAuthAsyncHandler(service: GatewayService) {\n const oauth = new Hono();\n\n /**\n * POST /api/auth/oauth-async/start\n * Start async OAuth flow - returns immediately with session ID\n */\n oauth.post('/start', async (c) => {\n const { provider } = await c.req.json().catch(() => ({}));\n \n if (!provider) {\n return c.json({ error: 'Provider is required' }, 400);\n }\n\n const oauthProvider = OAUTH_PROVIDERS[provider];\n if (!oauthProvider) {\n return c.json({ error: `Unknown OAuth provider: ${provider}` }, 400);\n }\n\n const sessionId = generateSessionId();\n const session: OAuthSession = {\n id: sessionId,\n provider,\n status: 'pending',\n createdAt: Date.now(),\n expiresAt: Date.now() + SESSION_TTL_MS,\n };\n\n oauthSessions.set(sessionId, session);\n\n // Start OAuth flow in background\n runOAuthFlow(session, oauthProvider, service).catch(err => {\n log.error({ sessionId, provider, error: err }, 'Background OAuth flow failed');\n session.status = 'failed';\n session.error = err instanceof Error ? err.message : 'OAuth flow failed';\n });\n\n return c.json({ \n ok: true, \n payload: { \n sessionId,\n provider,\n status: session.status,\n } \n });\n });\n\n /**\n * GET /api/auth/oauth-async/:sessionId/status\n * Check OAuth session status\n */\n oauth.get('/:sessionId/status', (c) => {\n const sessionId = c.req.param('sessionId');\n const session = oauthSessions.get(sessionId);\n\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json({ \n ok: true, \n payload: { \n sessionId: session.id,\n provider: session.provider,\n status: session.status,\n authUrl: session.authUrl,\n instructions: session.instructions,\n deviceCode: session.deviceCode,\n verificationUri: session.verificationUri,\n message: session.message,\n error: session.error,\n expiresAt: session.expiresAt,\n } \n });\n });\n\n /**\n * POST /api/auth/oauth-async/:sessionId/code\n * Submit manual authorization code\n */\n oauth.post('/:sessionId/code', async (c) => {\n const sessionId = c.req.param('sessionId');\n const { code } = await c.req.json().catch(() => ({}));\n \n if (!code) {\n return c.json({ error: 'Code is required' }, 400);\n }\n\n const session = oauthSessions.get(sessionId);\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n if (session.status !== 'waiting_code' || !session.manualCodeResolve) {\n return c.json({ error: 'Session is not waiting for code' }, 400);\n }\n\n // Resolve the manual code promise\n session.manualCodeResolve(code);\n session.manualCodeResolve = undefined;\n session.manualCodeReject = undefined;\n\n return c.json({ \n ok: true, \n payload: { \n message: 'Code submitted, processing...',\n } \n });\n });\n\n /**\n * POST /api/auth/oauth-async/:sessionId/cancel\n * Cancel OAuth flow\n */\n oauth.post('/:sessionId/cancel', async (c) => {\n const sessionId = c.req.param('sessionId');\n const session = oauthSessions.get(sessionId);\n\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n if (session.abortController) {\n session.abortController.abort();\n }\n\n if (session.manualCodeReject) {\n session.manualCodeReject(new Error('OAuth cancelled by user'));\n session.manualCodeReject = undefined;\n session.manualCodeResolve = undefined;\n }\n\n session.status = 'cancelled';\n session.message = 'OAuth flow cancelled';\n\n return c.json({ \n ok: true, \n payload: { \n message: 'OAuth flow cancelled',\n } \n });\n });\n\n /**\n * DELETE /api/auth/oauth-async/:sessionId\n * Clean up OAuth session\n */\n oauth.delete('/:sessionId', (c) => {\n const sessionId = c.req.param('sessionId');\n \n if (oauthSessions.has(sessionId)) {\n const session = oauthSessions.get(sessionId)!;\n if (session.abortController) {\n session.abortController.abort();\n }\n oauthSessions.delete(sessionId);\n }\n\n return c.json({ ok: true });\n });\n\n return oauth;\n}\n\n/**\n * Run OAuth flow in background\n */\nasync function runOAuthFlow(\n session: OAuthSession,\n oauthProvider: OAuthProviderInterface,\n _service: GatewayService\n): Promise<void> {\n const abortController = new AbortController();\n session.abortController = abortController;\n\n let manualCodePromise: Promise<string> | null = null;\n let manualCodeResolve: ((code: string) => void) | undefined;\n let manualCodeReject: ((error: Error) => void) | undefined;\n\n const callbacks: OAuthLoginCallbacks = {\n onAuth: (auth: { url: string; instructions?: string }) => {\n session.authUrl = auth.url;\n session.instructions = auth.instructions;\n \n if (oauthProvider.usesCallbackServer) {\n // For callback server providers, prepare for manual code input\n session.status = 'waiting_code';\n session.message = 'Complete authorization in browser, or paste the redirect URL below';\n manualCodePromise = new Promise((resolve, reject) => {\n manualCodeResolve = resolve;\n manualCodeReject = reject;\n });\n session.manualCodeResolve = manualCodeResolve;\n session.manualCodeReject = manualCodeReject;\n } else {\n session.status = 'waiting_auth';\n session.message = 'Complete authorization in browser';\n }\n },\n onPrompt: async (prompt: { message: string; deviceCode?: string; verificationUri?: string }) => {\n session.status = 'waiting_code';\n session.deviceCode = prompt.deviceCode;\n session.verificationUri = prompt.verificationUri;\n session.message = prompt.message;\n \n // For device code flow, wait for manual input\n manualCodePromise = new Promise((resolve, reject) => {\n manualCodeResolve = resolve;\n manualCodeReject = reject;\n });\n session.manualCodeResolve = manualCodeResolve;\n session.manualCodeReject = manualCodeReject;\n \n // Return empty for now, will be resolved by manual code submission\n return '';\n },\n onProgress: (message: string) => {\n log.debug({ sessionId: session.id, message }, 'OAuth progress');\n session.message = message;\n },\n onManualCodeInput: async () => {\n // Return the manual code promise for callback server providers\n if (manualCodePromise) {\n return manualCodePromise;\n }\n return '';\n },\n signal: abortController.signal,\n };\n\n try {\n const credentials = await oauthProvider.login(callbacks);\n \n // Save credentials to cache\n setOAuthCredentialsToCache(session.provider, credentials);\n\n // Get API key from OAuth credentials\n const apiKey = oauthProvider.getApiKey(credentials);\n\n // Save API key to credential system\n const resolver = new CredentialResolver();\n await resolver.saveApiKey(session.provider, apiKey, { profileName: 'default' });\n\n session.status = 'completed';\n session.credentials = credentials;\n session.message = 'OAuth login successful';\n \n log.info({ sessionId: session.id, provider: session.provider }, 'OAuth login completed');\n } catch (err) {\n if (abortController.signal.aborted) {\n session.status = 'cancelled';\n session.message = 'OAuth flow cancelled by user';\n } else {\n session.status = 'failed';\n session.error = formatOAuthAsyncError(err);\n log.error({ sessionId: session.id, provider: session.provider, error: err }, 'OAuth login failed');\n }\n } finally {\n session.abortController = undefined;\n session.manualCodeResolve = undefined;\n session.manualCodeReject = undefined;\n }\n}\n\n// Simple in-memory cache for OAuth credentials\nconst oauthCredentialsCache: Map<string, OAuthCredentials> = new Map();\n\nexport function getOAuthCredentialsFromCache(provider: string): OAuthCredentials | undefined {\n return oauthCredentialsCache.get(provider);\n}\n\nexport function setOAuthCredentialsToCache(provider: string, creds: OAuthCredentials): void {\n oauthCredentialsCache.set(provider, creds);\n}\n\nexport function deleteOAuthCredentialsFromCache(provider: string): void {\n oauthCredentialsCache.delete(provider);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth-async.js","names":["googleGeminiCliOAuthProvider","googleAntigravityOAuthProvider"],"sources":["../../../../src/gateway/hono/oauth-async.ts"],"sourcesContent":["/**\n * Async OAuth Handler\n * \n * Provides non-blocking OAuth flow with session-based state management.\n * This allows OAuth flows that require user interaction (browser login) \n * without blocking the HTTP request.\n */\n\nimport { Hono } from 'hono';\nimport type { GatewayService } from '../service.js';\nimport { \n type OAuthProviderInterface, \n type OAuthLoginCallbacks,\n type OAuthCredentials \n} from '../../auth/oauth/types.js';\nimport {\n kimiCodingOAuthProvider,\n minimaxOAuthProvider,\n minimaxCnOAuthProvider,\n anthropicOAuthProvider,\n githubCopilotOAuthProvider,\n googleGeminiCliOAuthProvider,\n googleAntigravityOAuthProvider,\n openaiCodexOAuthProvider,\n} from '../../auth/oauth/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { CredentialResolver } from '../../auth/credentials.js';\n\nconst log = createLogger('OAuthAsync');\n\n/** User-facing message when undici/fetch fails (often DNS, firewall, or wrong machine for localhost callback). */\nfunction formatOAuthAsyncError(err: unknown): string {\n\tconst base = err instanceof Error ? err.message : 'OAuth login failed';\n\tconst cause =\n\t\terr instanceof Error && err.cause instanceof Error\n\t\t\t? err.cause.message\n\t\t\t: err instanceof Error && typeof err.cause === 'string'\n\t\t\t\t? err.cause\n\t\t\t\t: '';\n\tconst detail = cause ? ` (${cause})` : '';\n\tif (/^fetch failed$/i.test(base) || base.includes('fetch failed')) {\n\t\treturn (\n\t\t\t`Network request failed${detail}. If the browser opened on another device, the redirect goes to that device's localhost — ` +\n\t\t\t`copy the full URL from the browser address bar after sign-in (starts with http://127.0.0.1 or http://localhost) and paste it below. ` +\n\t\t\t`Otherwise check VPN/proxy/DNS/firewall access to Google OAuth.`\n\t\t);\n\t}\n\treturn base;\n}\n\n// Static OAuth providers map\nconst OAUTH_PROVIDERS: Record<string, OAuthProviderInterface> = {\n 'kimi-coding': kimiCodingOAuthProvider,\n 'minimax': minimaxOAuthProvider,\n 'minimax-cn': minimaxCnOAuthProvider,\n 'anthropic': anthropicOAuthProvider,\n 'github-copilot': githubCopilotOAuthProvider,\n 'google-gemini-cli': googleGeminiCliOAuthProvider,\n 'google-antigravity': googleAntigravityOAuthProvider,\n 'openai-codex': openaiCodexOAuthProvider,\n};\n\n// OAuth session state\ninterface OAuthSession {\n id: string;\n provider: string;\n status: 'pending' | 'waiting_auth' | 'waiting_code' | 'completed' | 'failed' | 'cancelled';\n authUrl?: string;\n instructions?: string;\n deviceCode?: string;\n verificationUri?: string;\n message?: string;\n error?: string;\n credentials?: OAuthCredentials;\n createdAt: number;\n expiresAt: number;\n abortController?: AbortController;\n manualCodeResolve?: (code: string) => void;\n manualCodeReject?: (error: Error) => void;\n}\n\n// In-memory session store (could be moved to Redis for production)\nconst oauthSessions = new Map<string, OAuthSession>();\nconst SESSION_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\n// Clean up expired sessions periodically\nsetInterval(() => {\n const now = Date.now();\n for (const [id, session] of oauthSessions.entries()) {\n if (now > session.expiresAt) {\n cancelOAuthSession(session, 'OAuth flow expired');\n oauthSessions.delete(id);\n log.debug({ sessionId: id }, 'Cleaned up expired OAuth session');\n }\n }\n}, 60 * 1000);\n\nfunction generateSessionId(): string {\n return `oauth_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;\n}\n\nfunction cancelOAuthSession(session: OAuthSession, message = 'OAuth flow cancelled'): void {\n if (session.abortController) {\n session.abortController.abort();\n }\n\n if (session.manualCodeResolve) {\n session.manualCodeResolve('');\n }\n\n session.manualCodeResolve = undefined;\n session.manualCodeReject = undefined;\n session.status = 'cancelled';\n session.message = message;\n}\n\nexport function createOAuthAsyncHandler(service: GatewayService) {\n const oauth = new Hono();\n\n /**\n * POST /api/auth/oauth-async/start\n * Start async OAuth flow - returns immediately with session ID\n */\n oauth.post('/start', async (c) => {\n const { provider } = await c.req.json().catch(() => ({}));\n \n if (!provider) {\n return c.json({ error: 'Provider is required' }, 400);\n }\n\n const oauthProvider = OAUTH_PROVIDERS[provider];\n if (!oauthProvider) {\n return c.json({ error: `Unknown OAuth provider: ${provider}` }, 400);\n }\n\n const sessionId = generateSessionId();\n const session: OAuthSession = {\n id: sessionId,\n provider,\n status: 'pending',\n createdAt: Date.now(),\n expiresAt: Date.now() + SESSION_TTL_MS,\n };\n\n oauthSessions.set(sessionId, session);\n\n // Start OAuth flow in background\n runOAuthFlow(session, oauthProvider, service).catch(err => {\n log.error({ sessionId, provider, error: err }, 'Background OAuth flow failed');\n session.status = 'failed';\n session.error = err instanceof Error ? err.message : 'OAuth flow failed';\n });\n\n return c.json({ \n ok: true, \n payload: { \n sessionId,\n provider,\n status: session.status,\n } \n });\n });\n\n /**\n * GET /api/auth/oauth-async/:sessionId/status\n * Check OAuth session status\n */\n oauth.get('/:sessionId/status', (c) => {\n const sessionId = c.req.param('sessionId');\n const session = oauthSessions.get(sessionId);\n\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json({ \n ok: true, \n payload: { \n sessionId: session.id,\n provider: session.provider,\n status: session.status,\n authUrl: session.authUrl,\n instructions: session.instructions,\n deviceCode: session.deviceCode,\n verificationUri: session.verificationUri,\n message: session.message,\n error: session.error,\n expiresAt: session.expiresAt,\n } \n });\n });\n\n /**\n * POST /api/auth/oauth-async/:sessionId/code\n * Submit manual authorization code\n */\n oauth.post('/:sessionId/code', async (c) => {\n const sessionId = c.req.param('sessionId');\n const { code } = await c.req.json().catch(() => ({}));\n \n if (!code) {\n return c.json({ error: 'Code is required' }, 400);\n }\n\n const session = oauthSessions.get(sessionId);\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n if (session.status !== 'waiting_code' || !session.manualCodeResolve) {\n return c.json({ error: 'Session is not waiting for code' }, 400);\n }\n\n // Resolve the manual code promise\n session.manualCodeResolve(code);\n session.manualCodeResolve = undefined;\n session.manualCodeReject = undefined;\n\n return c.json({ \n ok: true, \n payload: { \n message: 'Code submitted, processing...',\n } \n });\n });\n\n /**\n * POST /api/auth/oauth-async/:sessionId/cancel\n * Cancel OAuth flow\n */\n oauth.post('/:sessionId/cancel', async (c) => {\n const sessionId = c.req.param('sessionId');\n const session = oauthSessions.get(sessionId);\n\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n cancelOAuthSession(session);\n\n return c.json({ \n ok: true, \n payload: { \n message: 'OAuth flow cancelled',\n } \n });\n });\n\n /**\n * DELETE /api/auth/oauth-async/:sessionId\n * Clean up OAuth session\n */\n oauth.delete('/:sessionId', (c) => {\n const sessionId = c.req.param('sessionId');\n \n if (oauthSessions.has(sessionId)) {\n const session = oauthSessions.get(sessionId)!;\n cancelOAuthSession(session);\n oauthSessions.delete(sessionId);\n }\n\n return c.json({ ok: true });\n });\n\n return oauth;\n}\n\n/**\n * Run OAuth flow in background\n */\nasync function runOAuthFlow(\n session: OAuthSession,\n oauthProvider: OAuthProviderInterface,\n _service: GatewayService\n): Promise<void> {\n const abortController = new AbortController();\n session.abortController = abortController;\n\n let manualCodePromise: Promise<string> | null = null;\n let manualCodeResolve: ((code: string) => void) | undefined;\n let manualCodeReject: ((error: Error) => void) | undefined;\n\n const callbacks: OAuthLoginCallbacks = {\n onAuth: (auth: { url: string; instructions?: string }) => {\n session.authUrl = auth.url;\n session.instructions = auth.instructions;\n \n if (oauthProvider.usesCallbackServer) {\n // For callback server providers, prepare for manual code input\n session.status = 'waiting_code';\n session.message = 'Complete authorization in browser, or paste the redirect URL below';\n manualCodePromise = new Promise((resolve, reject) => {\n manualCodeResolve = resolve;\n manualCodeReject = reject;\n });\n session.manualCodeResolve = manualCodeResolve;\n session.manualCodeReject = manualCodeReject;\n } else {\n session.status = 'waiting_auth';\n session.message = 'Complete authorization in browser';\n }\n },\n onDeviceCode: (info) => {\n session.status = 'waiting_auth';\n session.authUrl = info.verificationUri;\n session.deviceCode = info.userCode;\n session.verificationUri = info.verificationUri;\n session.instructions = `Enter code ${info.userCode}`;\n session.message = `Open ${info.verificationUri} and enter code ${info.userCode}`;\n },\n onPrompt: async (prompt: { message: string; deviceCode?: string; verificationUri?: string }) => {\n session.status = 'waiting_code';\n session.deviceCode = prompt.deviceCode;\n session.verificationUri = prompt.verificationUri;\n session.message = prompt.message;\n \n // For device code flow, wait for manual input\n manualCodePromise = new Promise((resolve, reject) => {\n manualCodeResolve = resolve;\n manualCodeReject = reject;\n });\n session.manualCodeResolve = manualCodeResolve;\n session.manualCodeReject = manualCodeReject;\n \n // Return empty for now, will be resolved by manual code submission\n return '';\n },\n onProgress: (message: string) => {\n log.debug({ sessionId: session.id, message }, 'OAuth progress');\n session.message = message;\n },\n onManualCodeInput: async () => {\n // Return the manual code promise for callback server providers\n if (manualCodePromise) {\n return manualCodePromise;\n }\n return '';\n },\n onSelect: async (prompt) => {\n const browserOption = prompt.options.find((option) => option.id === 'browser');\n const firstOption = prompt.options[0];\n const selectedOption = browserOption ?? firstOption;\n if (!selectedOption) {\n throw new Error('OAuth login did not provide any selectable auth method');\n }\n log.debug(\n { sessionId: session.id, provider: session.provider, selected: selectedOption.id },\n 'Selected OAuth auth method',\n );\n return selectedOption.id;\n },\n signal: abortController.signal,\n };\n\n try {\n const credentials = await oauthProvider.login(callbacks);\n \n // Save credentials to cache and persist them as first-class OAuth credentials.\n setOAuthCredentialsToCache(session.provider, credentials);\n\n const resolver = new CredentialResolver();\n await resolver.saveOAuthToken(session.provider, {\n access: oauthProvider.getApiKey(credentials),\n refresh: credentials.refresh,\n expiresAt: credentials.expires,\n scope: Array.isArray(credentials.scope) ? credentials.scope.filter((value): value is string => typeof value === 'string') : undefined,\n createdAt: new Date().toISOString(),\n });\n\n session.status = 'completed';\n session.credentials = credentials;\n session.message = 'OAuth login successful';\n \n log.info({ sessionId: session.id, provider: session.provider }, 'OAuth login completed');\n } catch (err) {\n if (abortController.signal.aborted || session.status === 'cancelled') {\n session.status = 'cancelled';\n session.message ??= 'OAuth flow cancelled by user';\n } else {\n session.status = 'failed';\n session.error = formatOAuthAsyncError(err);\n log.error({ sessionId: session.id, provider: session.provider, error: err }, 'OAuth login failed');\n }\n } finally {\n session.abortController = undefined;\n session.manualCodeResolve = undefined;\n session.manualCodeReject = undefined;\n }\n}\n\n// Simple in-memory cache for OAuth credentials\nconst oauthCredentialsCache: Map<string, OAuthCredentials> = new Map();\n\nexport function getOAuthCredentialsFromCache(provider: string): OAuthCredentials | undefined {\n return oauthCredentialsCache.get(provider);\n}\n\nexport function setOAuthCredentialsToCache(provider: string, creds: OAuthCredentials): void {\n oauthCredentialsCache.set(provider, creds);\n}\n\nexport function deleteOAuthCredentialsFromCache(provider: string): void {\n oauthCredentialsCache.delete(provider);\n}\n"],"mappings":"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"}
|