@midscene/core 1.16.2 → 1.16.6-beta-20261023073257.0
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/dist/es/agent/agent.mjs +11 -11
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/insight.mjs.map +1 -1
- package/dist/es/agent/tasks.mjs +72 -39
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/connectivity/run-connectivity-test.mjs +27 -22
- package/dist/es/ai-model/connectivity/run-connectivity-test.mjs.map +1 -1
- package/dist/es/ai-model/index.mjs +4 -2
- package/dist/es/ai-model/model-adapter/resolve.mjs +4 -2
- package/dist/es/ai-model/model-adapter/resolve.mjs.map +1 -1
- package/dist/es/ai-model/models/jev/adapter.mjs +6 -110
- package/dist/es/ai-model/models/jev/adapter.mjs.map +1 -1
- package/dist/es/ai-model/models/jev/boolean.mjs +48 -0
- package/dist/es/ai-model/models/jev/boolean.mjs.map +1 -0
- package/dist/es/ai-model/models/jev/image.mjs +39 -36
- package/dist/es/ai-model/models/jev/image.mjs.map +1 -1
- package/dist/es/ai-model/models/jev/locate.mjs +48 -0
- package/dist/es/ai-model/models/jev/locate.mjs.map +1 -0
- package/dist/es/ai-model/models/jev/planning-action.mjs +35 -0
- package/dist/es/ai-model/models/jev/planning-action.mjs.map +1 -0
- package/dist/es/ai-model/models/jev/planning-context.mjs +38 -0
- package/dist/es/ai-model/models/jev/planning-context.mjs.map +1 -0
- package/dist/es/ai-model/models/jev/planning-decision.mjs +68 -0
- package/dist/es/ai-model/models/jev/planning-decision.mjs.map +1 -0
- package/dist/es/ai-model/models/jev/planning.mjs +19 -131
- package/dist/es/ai-model/models/jev/planning.mjs.map +1 -1
- package/dist/es/ai-model/models/model-fallback.mjs +28 -0
- package/dist/es/ai-model/models/model-fallback.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/call-ai.mjs +54 -27
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/codex/call-codex.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/index.mjs +2 -2
- package/dist/es/ai-model/service-caller/model-call.mjs +3 -3
- package/dist/es/ai-model/service-caller/model-call.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs +4 -2
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/chat-completion/chat-completion.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/openai-client.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/responses/responses.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs +117 -0
- package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/credential-storage.mjs +66 -0
- package/dist/es/ai-model/service-caller/openai/siwc/credential-storage.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/errors.mjs +42 -0
- package/dist/es/ai-model/service-caller/openai/siwc/errors.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/index.mjs +7 -0
- package/dist/es/ai-model/service-caller/openai/siwc/prepare-call.mjs +33 -0
- package/dist/es/ai-model/service-caller/openai/siwc/prepare-call.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs +60 -0
- package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs +51 -0
- package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/system-one/call-system-one.mjs +69 -0
- package/dist/es/ai-model/service-caller/system-one/call-system-one.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/system-one/index.mjs +3 -0
- package/dist/es/ai-model/service-caller/system-one/prepare-system-one.mjs +44 -0
- package/dist/es/ai-model/service-caller/system-one/prepare-system-one.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/system-one/schema.mjs +32 -0
- package/dist/es/ai-model/service-caller/system-one/schema.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/system-one/utils.mjs +34 -0
- package/dist/es/ai-model/service-caller/system-one/utils.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/utils.mjs +2 -2
- package/dist/es/ai-model/service-caller/utils.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/factory.mjs +7 -3
- package/dist/es/ai-model/shared/model-locate-result/factory.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/pixel-mapper.mjs +10 -4
- package/dist/es/ai-model/shared/model-locate-result/pixel-mapper.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/types.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/validation.mjs +4 -0
- package/dist/es/ai-model/shared/model-locate-result/validation.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/locate.mjs +2 -2
- package/dist/es/ai-model/workflows/grounding/locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs +2 -2
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/insight/boolean.mjs +1 -1
- package/dist/es/ai-model/workflows/insight/boolean.mjs.map +1 -1
- package/dist/es/ai-model/workflows/insight/extraction.mjs +3 -3
- package/dist/es/ai-model/workflows/insight/extraction.mjs.map +1 -1
- package/dist/es/ai-model/workflows/planning/standard-planning.mjs +4 -3
- package/dist/es/ai-model/workflows/planning/standard-planning.mjs.map +1 -1
- package/dist/es/ai-model/workflows/recorder-generation/playwright.mjs +2 -2
- package/dist/es/ai-model/workflows/recorder-generation/playwright.mjs.map +1 -1
- package/dist/es/ai-model/workflows/recorder-generation/yaml.mjs +2 -2
- package/dist/es/ai-model/workflows/recorder-generation/yaml.mjs.map +1 -1
- package/dist/es/device/index.mjs +7 -0
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/service/index.mjs +2 -1
- package/dist/es/service/index.mjs.map +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/lib/agent/agent.js +11 -11
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/insight.js.map +1 -1
- package/dist/lib/agent/tasks.js +72 -39
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/connectivity/run-connectivity-test.js +27 -22
- package/dist/lib/ai-model/connectivity/run-connectivity-test.js.map +1 -1
- package/dist/lib/ai-model/index.js +34 -5
- package/dist/lib/ai-model/model-adapter/resolve.js +4 -2
- package/dist/lib/ai-model/model-adapter/resolve.js.map +1 -1
- package/dist/lib/ai-model/models/jev/adapter.js +6 -110
- package/dist/lib/ai-model/models/jev/adapter.js.map +1 -1
- package/dist/lib/ai-model/models/jev/boolean.js +82 -0
- package/dist/lib/ai-model/models/jev/boolean.js.map +1 -0
- package/dist/lib/ai-model/models/jev/image.js +41 -35
- package/dist/lib/ai-model/models/jev/image.js.map +1 -1
- package/dist/lib/ai-model/models/jev/locate.js +82 -0
- package/dist/lib/ai-model/models/jev/locate.js.map +1 -0
- package/dist/lib/ai-model/models/jev/planning-action.js +69 -0
- package/dist/lib/ai-model/models/jev/planning-action.js.map +1 -0
- package/dist/lib/ai-model/models/jev/planning-context.js +72 -0
- package/dist/lib/ai-model/models/jev/planning-context.js.map +1 -0
- package/dist/lib/ai-model/models/jev/planning-decision.js +102 -0
- package/dist/lib/ai-model/models/jev/planning-decision.js.map +1 -0
- package/dist/lib/ai-model/models/jev/planning.js +19 -131
- package/dist/lib/ai-model/models/jev/planning.js.map +1 -1
- package/dist/lib/ai-model/models/{task-routing.js → model-fallback.js} +11 -11
- package/dist/lib/ai-model/models/model-fallback.js.map +1 -0
- package/dist/lib/ai-model/service-caller/call-ai.js +59 -29
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/codex/call-codex.js.map +1 -1
- package/dist/lib/ai-model/service-caller/index.js +6 -3
- package/dist/lib/ai-model/service-caller/model-call.js +2 -2
- package/dist/lib/ai-model/service-caller/model-call.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/call-openai.js +4 -2
- package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/chat-completion/chat-completion.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/openai-client.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/responses/responses.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js +157 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/credential-storage.js +103 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/credential-storage.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/errors.js +85 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/errors.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/index.js +64 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/index.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/prepare-call.js +67 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/prepare-call.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js +94 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js +85 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js.map +1 -0
- package/dist/lib/ai-model/service-caller/system-one/call-system-one.js +103 -0
- package/dist/lib/ai-model/service-caller/system-one/call-system-one.js.map +1 -0
- package/dist/lib/ai-model/service-caller/system-one/index.js +42 -0
- package/dist/lib/ai-model/service-caller/system-one/index.js.map +1 -0
- package/dist/lib/ai-model/service-caller/system-one/prepare-system-one.js +78 -0
- package/dist/lib/ai-model/service-caller/system-one/prepare-system-one.js.map +1 -0
- package/dist/lib/ai-model/service-caller/system-one/schema.js +69 -0
- package/dist/lib/ai-model/service-caller/system-one/schema.js.map +1 -0
- package/dist/lib/ai-model/service-caller/system-one/utils.js +68 -0
- package/dist/lib/ai-model/service-caller/system-one/utils.js.map +1 -0
- package/dist/lib/ai-model/service-caller/utils.js +2 -2
- package/dist/lib/ai-model/service-caller/utils.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/factory.js +7 -3
- package/dist/lib/ai-model/shared/model-locate-result/factory.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/pixel-mapper.js +10 -4
- package/dist/lib/ai-model/shared/model-locate-result/pixel-mapper.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/types.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/validation.js +4 -0
- package/dist/lib/ai-model/shared/model-locate-result/validation.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/insight/boolean.js +1 -1
- package/dist/lib/ai-model/workflows/insight/boolean.js.map +1 -1
- package/dist/lib/ai-model/workflows/insight/extraction.js +2 -2
- package/dist/lib/ai-model/workflows/insight/extraction.js.map +1 -1
- package/dist/lib/ai-model/workflows/planning/standard-planning.js +3 -2
- package/dist/lib/ai-model/workflows/planning/standard-planning.js.map +1 -1
- package/dist/lib/ai-model/workflows/recorder-generation/playwright.js +1 -1
- package/dist/lib/ai-model/workflows/recorder-generation/playwright.js.map +1 -1
- package/dist/lib/ai-model/workflows/recorder-generation/yaml.js +1 -1
- package/dist/lib/ai-model/workflows/recorder-generation/yaml.js.map +1 -1
- package/dist/lib/device/index.js +7 -0
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/service/index.js +2 -1
- package/dist/lib/service/index.js.map +1 -1
- package/dist/lib/test-runner/engine/index.js +2 -2
- package/dist/lib/test-runner/node/index.js +2 -2
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/agent/insight.d.ts +2 -2
- package/dist/types/agent/insight.d.ts.map +1 -1
- package/dist/types/agent/tasks.d.ts.map +1 -1
- package/dist/types/ai-model/connectivity/run-connectivity-test.d.ts.map +1 -1
- package/dist/types/ai-model/index.d.ts +4 -1
- package/dist/types/ai-model/index.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/resolve.d.ts +2 -1
- package/dist/types/ai-model/model-adapter/resolve.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/types.d.ts +8 -5
- package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
- package/dist/types/ai-model/models/gpt.d.ts +2 -2
- package/dist/types/ai-model/models/jev/adapter.d.ts +4 -5
- package/dist/types/ai-model/models/jev/adapter.d.ts.map +1 -1
- package/dist/types/ai-model/models/jev/boolean.d.ts +3 -0
- package/dist/types/ai-model/models/jev/boolean.d.ts.map +1 -0
- package/dist/types/ai-model/models/jev/image.d.ts +11 -11
- package/dist/types/ai-model/models/jev/image.d.ts.map +1 -1
- package/dist/types/ai-model/models/jev/locate.d.ts +3 -0
- package/dist/types/ai-model/models/jev/locate.d.ts.map +1 -0
- package/dist/types/ai-model/models/jev/planning-action.d.ts +6 -0
- package/dist/types/ai-model/models/jev/planning-action.d.ts.map +1 -0
- package/dist/types/ai-model/models/jev/planning-context.d.ts +23 -0
- package/dist/types/ai-model/models/jev/planning-context.d.ts.map +1 -0
- package/dist/types/ai-model/models/jev/planning-decision.d.ts +35 -0
- package/dist/types/ai-model/models/jev/planning-decision.d.ts.map +1 -0
- package/dist/types/ai-model/models/jev/planning.d.ts.map +1 -1
- package/dist/types/ai-model/models/model-fallback.d.ts +6 -0
- package/dist/types/ai-model/models/model-fallback.d.ts.map +1 -0
- package/dist/types/ai-model/models/registry.d.ts +4 -3
- package/dist/types/ai-model/models/registry.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/call-ai.d.ts +3 -1
- package/dist/types/ai-model/service-caller/call-ai.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/codex/call-codex.d.ts +5 -3
- package/dist/types/ai-model/service-caller/codex/call-codex.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/index.d.ts +1 -1
- package/dist/types/ai-model/service-caller/index.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/model-call.d.ts +2 -2
- package/dist/types/ai-model/service-caller/model-call.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/call-openai.d.ts +2 -2
- package/dist/types/ai-model/service-caller/openai/call-openai.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/chat-completion/chat-completion.d.ts +4 -2
- package/dist/types/ai-model/service-caller/openai/chat-completion/chat-completion.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/responses/responses.d.ts +4 -2
- package/dist/types/ai-model/service-caller/openai/responses/responses.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/siwc/authorization.d.ts +42 -0
- package/dist/types/ai-model/service-caller/openai/siwc/authorization.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/credential-storage.d.ts +21 -0
- package/dist/types/ai-model/service-caller/openai/siwc/credential-storage.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/errors.d.ts +10 -0
- package/dist/types/ai-model/service-caller/openai/siwc/errors.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/index.d.ts +7 -0
- package/dist/types/ai-model/service-caller/openai/siwc/index.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/prepare-call.d.ts +8 -0
- package/dist/types/ai-model/service-caller/openai/siwc/prepare-call.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts +19 -0
- package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts +10 -0
- package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/system-one/call-system-one.d.ts +4 -0
- package/dist/types/ai-model/service-caller/system-one/call-system-one.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/system-one/index.d.ts +3 -0
- package/dist/types/ai-model/service-caller/system-one/index.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/system-one/prepare-system-one.d.ts +21 -0
- package/dist/types/ai-model/service-caller/system-one/prepare-system-one.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/{system-one.d.ts → system-one/schema.d.ts} +1 -22
- package/dist/types/ai-model/service-caller/system-one/schema.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/system-one/utils.d.ts +6 -0
- package/dist/types/ai-model/service-caller/system-one/utils.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/types.d.ts +0 -3
- package/dist/types/ai-model/service-caller/types.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/utils.d.ts +3 -3
- package/dist/types/ai-model/shared/model-locate-result/factory.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/index.d.ts +1 -1
- package/dist/types/ai-model/shared/model-locate-result/index.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/pixel-mapper.d.ts +3 -3
- package/dist/types/ai-model/shared/model-locate-result/pixel-mapper.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/types.d.ts +4 -0
- package/dist/types/ai-model/shared/model-locate-result/types.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/validation.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/grounding/locate.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/grounding/section-locate.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/insight/boolean.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/insight/extraction.d.ts +2 -0
- package/dist/types/ai-model/workflows/insight/extraction.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/planning/standard-planning.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/planning/types.d.ts +2 -1
- package/dist/types/ai-model/workflows/planning/types.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/recorder-generation/playwright.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/recorder-generation/yaml.d.ts.map +1 -1
- package/dist/types/device/index.d.ts +1 -0
- package/dist/types/device/index.d.ts.map +1 -1
- package/dist/types/service/index.d.ts +1 -1
- package/dist/types/service/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +14 -3
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/es/ai-model/models/task-routing.mjs +0 -28
- package/dist/es/ai-model/models/task-routing.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/system-one.mjs +0 -163
- package/dist/es/ai-model/service-caller/system-one.mjs.map +0 -1
- package/dist/lib/ai-model/models/task-routing.js.map +0 -1
- package/dist/lib/ai-model/service-caller/system-one.js +0 -206
- package/dist/lib/ai-model/service-caller/system-one.js.map +0 -1
- package/dist/types/ai-model/models/task-routing.d.ts +0 -6
- package/dist/types/ai-model/models/task-routing.d.ts.map +0 -1
- package/dist/types/ai-model/service-caller/system-one.d.ts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { callAIWithObjectResponse, callAIWithStringResponse, parseAIObjectResponse } from "./model-call.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { callGenerativeAI, callSystemOneAI } from "./call-ai.mjs";
|
|
3
3
|
import { AIResponseParseError, INTERNAL_CALL_ID_FIELD } from "./utils.mjs";
|
|
4
4
|
import { extractJSONFromCodeBlock, parseModelResponseJson } from "../shared/json.mjs";
|
|
5
|
-
export { AIResponseParseError, INTERNAL_CALL_ID_FIELD,
|
|
5
|
+
export { AIResponseParseError, INTERNAL_CALL_ID_FIELD, callAIWithObjectResponse, callAIWithStringResponse, callGenerativeAI, callSystemOneAI, extractJSONFromCodeBlock, parseAIObjectResponse, parseModelResponseJson };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from "@midscene/shared/utils";
|
|
2
2
|
import { assertJsonObject } from "../shared/json.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { callGenerativeAI } from "./call-ai.mjs";
|
|
4
4
|
import { callAiAndParseWithRetry, withSemanticRetryFeedback } from "./semantic-retry.mjs";
|
|
5
5
|
import { AIResponseParseError } from "./utils.mjs";
|
|
6
6
|
function parseAIObjectResponse(response, modelRuntime, jsonParserSource = 'generic-object') {
|
|
@@ -21,7 +21,7 @@ function parseAIObjectResponse(response, modelRuntime, jsonParserSource = 'gener
|
|
|
21
21
|
async function callAIWithObjectResponse(messages, modelRuntime, options) {
|
|
22
22
|
const { config: modelConfig } = modelRuntime;
|
|
23
23
|
return callAiAndParseWithRetry({
|
|
24
|
-
callAi: (retryAttempt, previousParseError)=>
|
|
24
|
+
callAi: (retryAttempt, previousParseError)=>callGenerativeAI(withSemanticRetryFeedback(messages, previousParseError), modelRuntime, {
|
|
25
25
|
abortSignal: options?.abortSignal,
|
|
26
26
|
expectedJsonObjectResponse: true,
|
|
27
27
|
semanticRetryAttempt: retryAttempt
|
|
@@ -37,7 +37,7 @@ async function callAIWithObjectResponse(messages, modelRuntime, options) {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
async function callAIWithStringResponse(msgs, modelRuntime, options) {
|
|
40
|
-
const { content, usage, rawAssistantOutput } = await
|
|
40
|
+
const { content, usage, rawAssistantOutput } = await callGenerativeAI(msgs, modelRuntime, options);
|
|
41
41
|
return {
|
|
42
42
|
content,
|
|
43
43
|
usage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/model-call.mjs","sources":["../../../../src/ai-model/service-caller/model-call.ts"],"sourcesContent":["import type { RawAssistantOutput } from '@/types';\nimport type { AIUsageInfo } from '@/types';\nimport { assert } from '@midscene/shared/utils';\nimport type { ModelRuntime } from '../models';\nimport { type JsonParserSource, assertJsonObject } from '../shared/json';\nimport {
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/model-call.mjs","sources":["../../../../src/ai-model/service-caller/model-call.ts"],"sourcesContent":["import type { RawAssistantOutput } from '@/types';\nimport type { AIUsageInfo } from '@/types';\nimport { assert } from '@midscene/shared/utils';\nimport type { ModelRuntime } from '../models';\nimport { type JsonParserSource, assertJsonObject } from '../shared/json';\nimport { callGenerativeAI } from './call-ai';\nimport {\n callAiAndParseWithRetry,\n withSemanticRetryFeedback,\n} from './semantic-retry';\nimport type { CallAIOptions } from './types';\nimport type { ModelCallMessages } from './types';\nimport { AIResponseParseError } from './utils';\n\nexport type AIObjectResponse<T> = {\n // TODO: `content` is a misleading name here because this is already the parsed object response. Consider renaming it to `object` or `data`.\n content: T;\n contentString: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n rawAssistantOutput?: RawAssistantOutput;\n};\n\nexport function parseAIObjectResponse<T>(\n response: Awaited<ReturnType<typeof callGenerativeAI>>,\n modelRuntime: ModelRuntime,\n jsonParserSource: JsonParserSource = 'generic-object',\n): AIObjectResponse<T> {\n const { adapter } = modelRuntime;\n assert(response, 'empty response');\n const jsonContent = adapter.jsonParser(response.content, {\n source: jsonParserSource,\n });\n assertJsonObject(jsonContent);\n return {\n content: jsonContent as T,\n contentString: response.content,\n usage: response.usage,\n reasoning_content: response.reasoning_content,\n rawAssistantOutput: response.rawAssistantOutput,\n };\n}\n\nexport async function callAIWithObjectResponse<T>(\n messages: ModelCallMessages,\n modelRuntime: ModelRuntime,\n options?: {\n abortSignal?: AbortSignal;\n jsonParserSource?: JsonParserSource;\n retryTimes?: number;\n retryInterval?: number;\n },\n): Promise<AIObjectResponse<T>> {\n const { config: modelConfig } = modelRuntime;\n return callAiAndParseWithRetry({\n callAi: (retryAttempt, previousParseError) =>\n callGenerativeAI(\n withSemanticRetryFeedback(messages, previousParseError),\n modelRuntime,\n {\n abortSignal: options?.abortSignal,\n expectedJsonObjectResponse: true,\n semanticRetryAttempt: retryAttempt,\n },\n ),\n parseResponse: (response) =>\n parseAIObjectResponse<T>(\n response,\n modelRuntime,\n options?.jsonParserSource,\n ),\n toParseError: (error, response) => {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return new AIResponseParseError(\n errorMessage,\n response.content,\n response.usage,\n response.rawAssistantOutput,\n response.reasoning_content,\n );\n },\n parseRetryTimes: options?.retryTimes ?? modelConfig.retryCount,\n parseRetryInterval: options?.retryInterval ?? modelConfig.retryInterval,\n abortSignal: options?.abortSignal,\n });\n}\n\nexport async function callAIWithStringResponse(\n msgs: ModelCallMessages,\n modelRuntime: ModelRuntime,\n options?: Pick<CallAIOptions, 'abortSignal' | 'requiresOriginalImageDetail'>,\n): Promise<{\n content: string;\n usage?: AIUsageInfo;\n rawAssistantOutput?: RawAssistantOutput;\n}> {\n const { content, usage, rawAssistantOutput } = await callGenerativeAI(\n msgs,\n modelRuntime,\n options,\n );\n return { content, usage, rawAssistantOutput };\n}\n"],"names":["parseAIObjectResponse","response","modelRuntime","jsonParserSource","adapter","assert","jsonContent","assertJsonObject","callAIWithObjectResponse","messages","options","modelConfig","callAiAndParseWithRetry","retryAttempt","previousParseError","callGenerativeAI","withSemanticRetryFeedback","error","errorMessage","Error","String","AIResponseParseError","callAIWithStringResponse","msgs","content","usage","rawAssistantOutput"],"mappings":";;;;;AAuBO,SAASA,sBACdC,QAAsD,EACtDC,YAA0B,EAC1BC,mBAAqC,gBAAgB;IAErD,MAAM,EAAEC,OAAO,EAAE,GAAGF;IACpBG,OAAOJ,UAAU;IACjB,MAAMK,cAAcF,QAAQ,UAAU,CAACH,SAAS,OAAO,EAAE;QACvD,QAAQE;IACV;IACAI,iBAAiBD;IACjB,OAAO;QACL,SAASA;QACT,eAAeL,SAAS,OAAO;QAC/B,OAAOA,SAAS,KAAK;QACrB,mBAAmBA,SAAS,iBAAiB;QAC7C,oBAAoBA,SAAS,kBAAkB;IACjD;AACF;AAEO,eAAeO,yBACpBC,QAA2B,EAC3BP,YAA0B,EAC1BQ,OAKC;IAED,MAAM,EAAE,QAAQC,WAAW,EAAE,GAAGT;IAChC,OAAOU,wBAAwB;QAC7B,QAAQ,CAACC,cAAcC,qBACrBC,iBACEC,0BAA0BP,UAAUK,qBACpCZ,cACA;gBACE,aAAaQ,SAAS;gBACtB,4BAA4B;gBAC5B,sBAAsBG;YACxB;QAEJ,eAAe,CAACZ,WACdD,sBACEC,UACAC,cACAQ,SAAS;QAEb,cAAc,CAACO,OAAOhB;YACpB,MAAMiB,eACJD,iBAAiBE,QAAQF,MAAM,OAAO,GAAGG,OAAOH;YAClD,OAAO,IAAII,qBACTH,cACAjB,SAAS,OAAO,EAChBA,SAAS,KAAK,EACdA,SAAS,kBAAkB,EAC3BA,SAAS,iBAAiB;QAE9B;QACA,iBAAiBS,SAAS,cAAcC,YAAY,UAAU;QAC9D,oBAAoBD,SAAS,iBAAiBC,YAAY,aAAa;QACvE,aAAaD,SAAS;IACxB;AACF;AAEO,eAAeY,yBACpBC,IAAuB,EACvBrB,YAA0B,EAC1BQ,OAA4E;IAM5E,MAAM,EAAEc,OAAO,EAAEC,KAAK,EAAEC,kBAAkB,EAAE,GAAG,MAAMX,iBACnDQ,MACArB,cACAQ;IAEF,OAAO;QAAEc;QAASC;QAAOC;IAAmB;AAC9C"}
|
|
@@ -4,6 +4,7 @@ import { callChatCompletion } from "./chat-completion/chat-completion.mjs";
|
|
|
4
4
|
import { createClient } from "./openai-client.mjs";
|
|
5
5
|
import { formatOpenAIAPIErrorDetails } from "./openai-request-context.mjs";
|
|
6
6
|
import { callResponses } from "./responses/responses.mjs";
|
|
7
|
+
import { SiwcTokenExpiredError } from "./siwc/index.mjs";
|
|
7
8
|
async function callOpenAI({ modelRuntime, options, executionId, recordEvent, requestSignal, effectiveTimeoutMs }, prepared) {
|
|
8
9
|
const { config: modelConfig } = modelRuntime;
|
|
9
10
|
const { modelName } = modelConfig;
|
|
@@ -25,7 +26,7 @@ async function callOpenAI({ modelRuntime, options, executionId, recordEvent, req
|
|
|
25
26
|
};
|
|
26
27
|
try {
|
|
27
28
|
const startTime = Date.now();
|
|
28
|
-
const response = 'responses' === prepared.
|
|
29
|
+
const response = 'responses' === prepared.kind ? await callResponses({
|
|
29
30
|
...requestOptions,
|
|
30
31
|
responses: openai.responses,
|
|
31
32
|
requestBodyParams: prepared.input.requestParams
|
|
@@ -74,7 +75,8 @@ async function callOpenAI({ modelRuntime, options, executionId, recordEvent, req
|
|
|
74
75
|
}
|
|
75
76
|
})('call AI error', error);
|
|
76
77
|
if (error instanceof AIResponseParseError) throw error;
|
|
77
|
-
|
|
78
|
+
const RequestError = 'siwc' === modelConfig.authMode && 401 === error.status && 'token_expired' === error.code ? SiwcTokenExpiredError : Error;
|
|
79
|
+
throw new RequestError(`failed to call ${isStreaming ? 'streaming ' : ''}AI model service (${modelName}): ${error.message}${formatOpenAIAPIErrorDetails(error, openAIRequestContext)}\nTrouble shooting: https://midscenejs.com/model-config#verify-model-connectivity`, {
|
|
78
80
|
cause: error
|
|
79
81
|
});
|
|
80
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/openai/call-openai.mjs","sources":["../../../../../src/ai-model/service-caller/openai/call-openai.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ModelCallContext, ModelCallResult } from '../types';\nimport { AIResponseParseError } from '../utils';\nimport {\n callChatCompletion,\n type prepareChatCompletion,\n} from './chat-completion/chat-completion';\nimport { createClient } from './openai-client';\nimport { formatOpenAIAPIErrorDetails } from './openai-request-context';\nimport { callResponses, type prepareResponses } from './responses/responses';\n\nexport type PreparedOpenAIInput =\n | {\n
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/call-openai.mjs","sources":["../../../../../src/ai-model/service-caller/openai/call-openai.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ModelCallContext, ModelCallResult } from '../types';\nimport { AIResponseParseError } from '../utils';\nimport {\n callChatCompletion,\n type prepareChatCompletion,\n} from './chat-completion/chat-completion';\nimport { createClient } from './openai-client';\nimport { formatOpenAIAPIErrorDetails } from './openai-request-context';\nimport { callResponses, type prepareResponses } from './responses/responses';\nimport { SiwcTokenExpiredError } from './siwc';\n\nexport type PreparedOpenAIInput =\n | {\n kind: 'chat-completion';\n input: Awaited<ReturnType<typeof prepareChatCompletion>>;\n }\n | {\n kind: 'responses';\n input: Awaited<ReturnType<typeof prepareResponses>>;\n };\n\nexport async function callOpenAI(\n {\n modelRuntime,\n options,\n executionId,\n recordEvent,\n requestSignal,\n effectiveTimeoutMs,\n }: ModelCallContext,\n prepared: PreparedOpenAIInput,\n): Promise<ModelCallResult> {\n const { config: modelConfig } = modelRuntime;\n const { modelName } = modelConfig;\n const isStreaming = options?.stream ?? modelConfig.streamMode === 'stream';\n const { openai, openAIRequestContext } = await createClient({\n modelConfig,\n proxyAgent: prepared.input.proxyAgent,\n effectiveTimeoutMs,\n executionId,\n recordEvent,\n });\n const debugCall = getDebug('ai:call');\n debugCall(\n `sending ${isStreaming ? 'streaming ' : ''}request to ${modelName}`,\n );\n const requestOptions = {\n requestSignal,\n openAIRequestContext,\n onChunk: options?.onChunk ?? (() => undefined),\n recordEvent,\n };\n try {\n const startTime = Date.now();\n const response =\n prepared.kind === 'responses'\n ? await callResponses(\n {\n ...requestOptions,\n responses: openai.responses,\n requestBodyParams: prepared.input.requestParams,\n },\n isStreaming,\n )\n : await callChatCompletion(\n {\n ...requestOptions,\n completion: openai.chat.completions,\n requestBodyParams: prepared.input.requestParams,\n extractContentAndReasoning:\n prepared.input.extractContentAndReasoning,\n useReasoningAsContentFallback:\n prepared.input.useReasoningAsContentFallback,\n },\n isStreaming,\n );\n const { reasoningContent, ...result } = response;\n requestSignal.throwIfAborted();\n const timeCost = Date.now() - startTime;\n debugCall(`response reasoning content: ${reasoningContent}`);\n debugCall(`response content: ${result.content}`);\n recordEvent?.({\n type: 'response',\n http: openAIRequestContext.httpResponse,\n final: {\n content: result.content,\n reasoningContent,\n usage: result.rawUsage,\n requestId: result.requestId,\n timeCost,\n responseModelName: result.responseModelName,\n },\n });\n return {\n ...result,\n reasoning_content: reasoningContent,\n isStreamed: isStreaming,\n timeCost,\n };\n } catch (error: any) {\n // Fetch already records non-2xx responses. Preserve successful HTTP\n // responses too when SDK parsing or completion validation fails.\n if (openAIRequestContext.httpResponse?.ok) {\n recordEvent?.({\n type: 'error',\n ...openAIRequestContext.httpResponse,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n getDebug('ai:call', {\n console: true,\n warning: { kind: 'model-request-detail', modelName, error },\n })('call AI error', error);\n if (error instanceof AIResponseParseError) {\n throw error;\n }\n const RequestError =\n modelConfig.authMode === 'siwc' &&\n error.status === 401 &&\n error.code === 'token_expired'\n ? SiwcTokenExpiredError\n : Error;\n throw new RequestError(\n `failed to call ${isStreaming ? 'streaming ' : ''}AI model service (${modelName}): ${error.message}${formatOpenAIAPIErrorDetails(error, openAIRequestContext)}\\nTrouble shooting: https://midscenejs.com/model-config#verify-model-connectivity`,\n { cause: error },\n );\n }\n}\n"],"names":["callOpenAI","modelRuntime","options","executionId","recordEvent","requestSignal","effectiveTimeoutMs","prepared","modelConfig","modelName","isStreaming","openai","openAIRequestContext","createClient","debugCall","getDebug","requestOptions","undefined","startTime","Date","response","callResponses","callChatCompletion","reasoningContent","result","timeCost","error","Error","String","AIResponseParseError","RequestError","SiwcTokenExpiredError","formatOpenAIAPIErrorDetails"],"mappings":";;;;;;;AAsBO,eAAeA,WACpB,EACEC,YAAY,EACZC,OAAO,EACPC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,kBAAkB,EACD,EACnBC,QAA6B;IAE7B,MAAM,EAAE,QAAQC,WAAW,EAAE,GAAGP;IAChC,MAAM,EAAEQ,SAAS,EAAE,GAAGD;IACtB,MAAME,cAAcR,SAAS,UAAUM,AAA2B,aAA3BA,YAAY,UAAU;IAC7D,MAAM,EAAEG,MAAM,EAAEC,oBAAoB,EAAE,GAAG,MAAMC,aAAa;QAC1DL;QACA,YAAYD,SAAS,KAAK,CAAC,UAAU;QACrCD;QACAH;QACAC;IACF;IACA,MAAMU,YAAYC,SAAS;IAC3BD,UACE,CAAC,QAAQ,EAAEJ,cAAc,eAAe,GAAG,WAAW,EAAED,WAAW;IAErE,MAAMO,iBAAiB;QACrBX;QACAO;QACA,SAASV,SAAS,WAAa,KAAKe,MAAQ;QAC5Cb;IACF;IACA,IAAI;QACF,MAAMc,YAAYC,KAAK,GAAG;QAC1B,MAAMC,WACJb,AAAkB,gBAAlBA,SAAS,IAAI,GACT,MAAMc,cACJ;YACE,GAAGL,cAAc;YACjB,WAAWL,OAAO,SAAS;YAC3B,mBAAmBJ,SAAS,KAAK,CAAC,aAAa;QACjD,GACAG,eAEF,MAAMY,mBACJ;YACE,GAAGN,cAAc;YACjB,YAAYL,OAAO,IAAI,CAAC,WAAW;YACnC,mBAAmBJ,SAAS,KAAK,CAAC,aAAa;YAC/C,4BACEA,SAAS,KAAK,CAAC,0BAA0B;YAC3C,+BACEA,SAAS,KAAK,CAAC,6BAA6B;QAChD,GACAG;QAER,MAAM,EAAEa,gBAAgB,EAAE,GAAGC,QAAQ,GAAGJ;QACxCf,cAAc,cAAc;QAC5B,MAAMoB,WAAWN,KAAK,GAAG,KAAKD;QAC9BJ,UAAU,CAAC,4BAA4B,EAAES,kBAAkB;QAC3DT,UAAU,CAAC,kBAAkB,EAAEU,OAAO,OAAO,EAAE;QAC/CpB,cAAc;YACZ,MAAM;YACN,MAAMQ,qBAAqB,YAAY;YACvC,OAAO;gBACL,SAASY,OAAO,OAAO;gBACvBD;gBACA,OAAOC,OAAO,QAAQ;gBACtB,WAAWA,OAAO,SAAS;gBAC3BC;gBACA,mBAAmBD,OAAO,iBAAiB;YAC7C;QACF;QACA,OAAO;YACL,GAAGA,MAAM;YACT,mBAAmBD;YACnB,YAAYb;YACZe;QACF;IACF,EAAE,OAAOC,OAAY;QAGnB,IAAId,qBAAqB,YAAY,EAAE,IACrCR,cAAc;YACZ,MAAM;YACN,GAAGQ,qBAAqB,YAAY;YACpC,OAAOc,iBAAiBC,QAAQD,MAAM,OAAO,GAAGE,OAAOF;QACzD;QAEFX,SAAS,WAAW;YAClB,SAAS;YACT,SAAS;gBAAE,MAAM;gBAAwBN;gBAAWiB;YAAM;QAC5D,GAAG,iBAAiBA;QACpB,IAAIA,iBAAiBG,sBACnB,MAAMH;QAER,MAAMI,eACJtB,AAAyB,WAAzBA,YAAY,QAAQ,IACpBkB,AAAiB,QAAjBA,MAAM,MAAM,IACZA,AAAe,oBAAfA,MAAM,IAAI,GACNK,wBACAJ;QACN,MAAM,IAAIG,aACR,CAAC,eAAe,EAAEpB,cAAc,eAAe,GAAG,kBAAkB,EAAED,UAAU,GAAG,EAAEiB,MAAM,OAAO,GAAGM,4BAA4BN,OAAOd,sBAAsB,iFAAiF,CAAC,EAChP;YAAE,OAAOc;QAAM;IAEnB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/openai/chat-completion/chat-completion.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/chat-completion/chat-completion.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ResolveImageDetail } from '../../../model-adapter/types';\nimport type { ModelRequestConfigInput } from '../../../model-adapter/types';\nimport { createProxyAgentIfNeeded } from '../../proxy';\nimport type {
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/chat-completion/chat-completion.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/chat-completion/chat-completion.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ResolveImageDetail } from '../../../model-adapter/types';\nimport type { ModelRequestConfigInput } from '../../../model-adapter/types';\nimport { createProxyAgentIfNeeded } from '../../proxy';\nimport type {\n ModelCallContext,\n ModelCallMessages,\n OpenAIProtocolCallResult,\n} from '../../types';\nimport { stringifyForDebug } from '../../utils';\nimport { callChatCompletionNonStreaming } from './non-stream';\nimport { callChatCompletionStream } from './stream';\nimport type { ChatCompletionCallOptions } from './types';\nimport { toChatMessages } from './utils';\n\nexport const prepareChatCompletion = async ({\n messages,\n modelRuntime,\n options,\n}: Pick<ModelCallContext, 'modelRuntime' | 'options'> & {\n messages: ModelCallMessages;\n}) => {\n const debugCall = getDebug('ai:call');\n const { config: modelConfig, adapter } = modelRuntime;\n\n const modelCallInput: ModelRequestConfigInput = {\n intent: modelConfig.intent,\n userConfig: {\n temperature: modelConfig.temperature,\n reasoningEnabled: modelConfig.reasoningEnabled,\n reasoningEffort: modelConfig.reasoningEffort,\n reasoningBudget: modelConfig.reasoningBudget,\n responseFormat: modelConfig.responseFormat,\n },\n semanticRetryAttempt: options?.semanticRetryAttempt,\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n expectedJsonObjectResponse: options?.expectedJsonObjectResponse,\n };\n\n const { config: adapterChatCompletionParams } =\n adapter.chatCompletion.buildChatCompletionParams(modelCallInput);\n\n debugCall(\n `adapter chat completion params: ${stringifyForDebug({\n config: adapterChatCompletionParams,\n })}`,\n );\n\n const resolveImageDetail: ResolveImageDetail = ({ imageDetail }) =>\n adapter.resolveImageDetail({\n imageDetail,\n intent: modelConfig.intent,\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n });\n\n const requestBodyParams: ChatCompletionCallOptions['requestBodyParams'] = {\n model: modelConfig.modelName,\n messages: toChatMessages(messages, resolveImageDetail),\n ...adapterChatCompletionParams,\n ...(modelConfig.extraBody ?? {}),\n };\n\n const proxyAgent = await createProxyAgentIfNeeded({\n socksProxy: modelConfig.socksProxy,\n httpProxy: modelConfig.httpProxy,\n });\n\n return {\n requestParams: requestBodyParams,\n extractContentAndReasoning:\n adapter.chatCompletion.extractContentAndReasoning,\n useReasoningAsContentFallback:\n adapter.chatCompletion.useReasoningAsContentFallback,\n proxyAgent,\n };\n};\n\nexport function callChatCompletion(\n options: ChatCompletionCallOptions,\n stream: boolean,\n): Promise<OpenAIProtocolCallResult> {\n return stream\n ? callChatCompletionStream(options)\n : callChatCompletionNonStreaming(options);\n}\n"],"names":["prepareChatCompletion","messages","modelRuntime","options","debugCall","getDebug","modelConfig","adapter","modelCallInput","adapterChatCompletionParams","stringifyForDebug","resolveImageDetail","imageDetail","requestBodyParams","toChatMessages","proxyAgent","createProxyAgentIfNeeded","callChatCompletion","stream","callChatCompletionStream","callChatCompletionNonStreaming"],"mappings":";;;;;;AAeO,MAAMA,wBAAwB,OAAO,EAC1CC,QAAQ,EACRC,YAAY,EACZC,OAAO,EAGR;IACC,MAAMC,YAAYC,SAAS;IAC3B,MAAM,EAAE,QAAQC,WAAW,EAAEC,OAAO,EAAE,GAAGL;IAEzC,MAAMM,iBAA0C;QAC9C,QAAQF,YAAY,MAAM;QAC1B,YAAY;YACV,aAAaA,YAAY,WAAW;YACpC,kBAAkBA,YAAY,gBAAgB;YAC9C,iBAAiBA,YAAY,eAAe;YAC5C,iBAAiBA,YAAY,eAAe;YAC5C,gBAAgBA,YAAY,cAAc;QAC5C;QACA,sBAAsBH,SAAS;QAC/B,6BAA6BA,SAAS;QACtC,4BAA4BA,SAAS;IACvC;IAEA,MAAM,EAAE,QAAQM,2BAA2B,EAAE,GAC3CF,QAAQ,cAAc,CAAC,yBAAyB,CAACC;IAEnDJ,UACE,CAAC,gCAAgC,EAAEM,kBAAkB;QACnD,QAAQD;IACV,IAAI;IAGN,MAAME,qBAAyC,CAAC,EAAEC,WAAW,EAAE,GAC7DL,QAAQ,kBAAkB,CAAC;YACzBK;YACA,QAAQN,YAAY,MAAM;YAC1B,6BAA6BH,SAAS;QACxC;IAEF,MAAMU,oBAAoE;QACxE,OAAOP,YAAY,SAAS;QAC5B,UAAUQ,eAAeb,UAAUU;QACnC,GAAGF,2BAA2B;QAC9B,GAAIH,YAAY,SAAS,IAAI,CAAC,CAAC;IACjC;IAEA,MAAMS,aAAa,MAAMC,yBAAyB;QAChD,YAAYV,YAAY,UAAU;QAClC,WAAWA,YAAY,SAAS;IAClC;IAEA,OAAO;QACL,eAAeO;QACf,4BACEN,QAAQ,cAAc,CAAC,0BAA0B;QACnD,+BACEA,QAAQ,cAAc,CAAC,6BAA6B;QACtDQ;IACF;AACF;AAEO,SAASE,mBACdd,OAAkC,EAClCe,MAAe;IAEf,OAAOA,SACHC,yBAAyBhB,WACzBiB,+BAA+BjB;AACrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/openai/openai-client.mjs","sources":["../../../../../src/ai-model/service-caller/openai/openai-client.ts"],"sourcesContent":["import {\n type IModelConfig,\n MIDSCENE_LANGFUSE_DEBUG,\n MIDSCENE_LANGSMITH_DEBUG,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport { getVersion } from '../../../utils';\nimport type { createProxyAgentIfNeeded } from '../proxy';\nimport {\n type OpenAIRequestContext,\n wrapOpenAICompatibleFetch,\n} from './openai-request-context';\n\nconst createAndWrapClient = async ({\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n createOpenAIClient,\n effectiveTimeoutMs,\n proxyAgent,\n executionId,\n openAIRequestContext,\n}: {\n openaiBaseURL: IModelConfig['openaiBaseURL'];\n openaiApiKey: IModelConfig['openaiApiKey'];\n openaiExtraConfig: IModelConfig['openaiExtraConfig'];\n createOpenAIClient: IModelConfig['createOpenAIClient'];\n effectiveTimeoutMs: number | null;\n proxyAgent: Awaited<ReturnType<typeof createProxyAgentIfNeeded>>;\n executionId: string;\n openAIRequestContext: OpenAIRequestContext;\n}): Promise<OpenAI> => {\n const warnClient = getDebug('ai:call', { console: true });\n\n const openAIOptions = {\n baseURL: openaiBaseURL,\n apiKey: openaiApiKey,\n // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n // Note: Type assertion needed due to undici version mismatch between dependencies\n ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n ...openaiExtraConfig,\n // Midscene exposes this setting through MIDSCENE_*_INIT_CONFIG_JSON, so\n // defaultHeaders is expected to be a plain JSON object rather than another\n // HeadersLike representation supported by the OpenAI SDK.\n defaultHeaders: {\n ...(openaiExtraConfig?.defaultHeaders as\n | Record<string, string>\n | undefined),\n // These Midscene-owned headers intentionally override user-supplied\n // headers with the same names.\n 'x-midscene-version': getVersion(),\n 'x-midscene-execution-id': executionId,\n },\n fetch: wrapOpenAICompatibleFetch(openAIRequestContext),\n // Midscene handles request retries in
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/openai-client.mjs","sources":["../../../../../src/ai-model/service-caller/openai/openai-client.ts"],"sourcesContent":["import {\n type IModelConfig,\n MIDSCENE_LANGFUSE_DEBUG,\n MIDSCENE_LANGSMITH_DEBUG,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport { getVersion } from '../../../utils';\nimport type { createProxyAgentIfNeeded } from '../proxy';\nimport {\n type OpenAIRequestContext,\n wrapOpenAICompatibleFetch,\n} from './openai-request-context';\n\nconst createAndWrapClient = async ({\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n createOpenAIClient,\n effectiveTimeoutMs,\n proxyAgent,\n executionId,\n openAIRequestContext,\n}: {\n openaiBaseURL: IModelConfig['openaiBaseURL'];\n openaiApiKey: IModelConfig['openaiApiKey'];\n openaiExtraConfig: IModelConfig['openaiExtraConfig'];\n createOpenAIClient: IModelConfig['createOpenAIClient'];\n effectiveTimeoutMs: number | null;\n proxyAgent: Awaited<ReturnType<typeof createProxyAgentIfNeeded>>;\n executionId: string;\n openAIRequestContext: OpenAIRequestContext;\n}): Promise<OpenAI> => {\n const warnClient = getDebug('ai:call', { console: true });\n\n const openAIOptions = {\n baseURL: openaiBaseURL,\n apiKey: openaiApiKey,\n // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n // Note: Type assertion needed due to undici version mismatch between dependencies\n ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n ...openaiExtraConfig,\n // Midscene exposes this setting through MIDSCENE_*_INIT_CONFIG_JSON, so\n // defaultHeaders is expected to be a plain JSON object rather than another\n // HeadersLike representation supported by the OpenAI SDK.\n defaultHeaders: {\n ...(openaiExtraConfig?.defaultHeaders as\n | Record<string, string>\n | undefined),\n // These Midscene-owned headers intentionally override user-supplied\n // headers with the same names.\n 'x-midscene-version': getVersion(),\n 'x-midscene-execution-id': executionId,\n },\n fetch: wrapOpenAICompatibleFetch(openAIRequestContext),\n // Midscene handles request retries in callGenerativeAI(), so disable SDK-level retries\n // to avoid duplicate attempts and duplicated backoff latency.\n maxRetries: 0,\n // Disabling the Midscene hard timeout leaves the SDK timeout at its\n // configured value or default; it does not disable SDK/network timeouts.\n ...(effectiveTimeoutMs !== null ? { timeout: effectiveTimeoutMs } : {}),\n dangerouslyAllowBrowser: true,\n };\n\n const baseOpenAI = new OpenAI(openAIOptions);\n\n let openai: OpenAI = baseOpenAI;\n\n // LangSmith wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGSMITH_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langsmith is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langsmith wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langsmithModule = 'langsmith/wrappers';\n const { wrapOpenAI } = await import(langsmithModule);\n openai = wrapOpenAI(openai);\n }\n\n // Langfuse wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGFUSE_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langfuse is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langfuse wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langfuseModule = '@langfuse/openai';\n const { observeOpenAI } = await import(langfuseModule);\n openai = observeOpenAI(openai);\n }\n\n if (createOpenAIClient) {\n const wrappedClient = await createOpenAIClient(baseOpenAI, openAIOptions);\n\n if (wrappedClient) {\n openai = wrappedClient as OpenAI;\n }\n }\n\n return openai;\n};\n\nexport async function createClient({\n modelConfig,\n proxyAgent,\n effectiveTimeoutMs,\n executionId,\n recordEvent,\n}: {\n modelConfig: IModelConfig;\n proxyAgent: Awaited<ReturnType<typeof createProxyAgentIfNeeded>>;\n effectiveTimeoutMs: number | null;\n executionId: string;\n recordEvent?: (event: Record<string, unknown>) => void;\n}): Promise<{\n openai: OpenAI;\n openAIRequestContext: OpenAIRequestContext;\n}> {\n const { openaiBaseURL, openaiApiKey, openaiExtraConfig, createOpenAIClient } =\n modelConfig;\n\n const openAIRequestContext: OpenAIRequestContext = {\n recordEvent,\n };\n\n const openai = await createAndWrapClient({\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n createOpenAIClient,\n effectiveTimeoutMs,\n proxyAgent,\n executionId,\n openAIRequestContext,\n });\n\n return {\n openai,\n openAIRequestContext,\n };\n}\n"],"names":["createAndWrapClient","openaiBaseURL","openaiApiKey","openaiExtraConfig","createOpenAIClient","effectiveTimeoutMs","proxyAgent","executionId","openAIRequestContext","warnClient","getDebug","openAIOptions","getVersion","wrapOpenAICompatibleFetch","baseOpenAI","OpenAI","openai","globalConfigManager","MIDSCENE_LANGSMITH_DEBUG","ifInBrowser","Error","langsmithModule","wrapOpenAI","MIDSCENE_LANGFUSE_DEBUG","langfuseModule","observeOpenAI","wrappedClient","createClient","modelConfig","recordEvent"],"mappings":";;;;;;AAgBA,MAAMA,sBAAsB,OAAO,EACjCC,aAAa,EACbC,YAAY,EACZC,iBAAiB,EACjBC,kBAAkB,EAClBC,kBAAkB,EAClBC,UAAU,EACVC,WAAW,EACXC,oBAAoB,EAUrB;IACC,MAAMC,aAAaC,SAAS,WAAW;QAAE,SAAS;IAAK;IAEvD,MAAMC,gBAAgB;QACpB,SAASV;QACT,QAAQC;QAGR,GAAII,aAAa;YAAE,cAAc;gBAAE,YAAYA;YAAkB;QAAE,IAAI,CAAC,CAAC;QACzE,GAAGH,iBAAiB;QAIpB,gBAAgB;YACd,GAAIA,mBAAmB,cAAc;YAKrC,sBAAsBS;YACtB,2BAA2BL;QAC7B;QACA,OAAOM,0BAA0BL;QAGjC,YAAY;QAGZ,GAAIH,AAAuB,SAAvBA,qBAA8B;YAAE,SAASA;QAAmB,IAAI,CAAC,CAAC;QACtE,yBAAyB;IAC3B;IAEA,MAAMS,aAAa,IAAIC,SAAOJ;IAE9B,IAAIK,SAAiBF;IAGrB,IACEE,UACAC,oBAAoB,qBAAqB,CAACC,2BAC1C;QACA,IAAIC,aACF,MAAM,IAAIC,MAAM;QAElBX,WAAW;QAEX,MAAMY,kBAAkB;QACxB,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAACD;QACpCL,SAASM,WAAWN;IACtB;IAGA,IACEA,UACAC,oBAAoB,qBAAqB,CAACM,0BAC1C;QACA,IAAIJ,aACF,MAAM,IAAIC,MAAM;QAElBX,WAAW;QAEX,MAAMe,iBAAiB;QACvB,MAAM,EAAEC,aAAa,EAAE,GAAG,MAAM,MAAM,CAACD;QACvCR,SAASS,cAAcT;IACzB;IAEA,IAAIZ,oBAAoB;QACtB,MAAMsB,gBAAgB,MAAMtB,mBAAmBU,YAAYH;QAE3D,IAAIe,eACFV,SAASU;IAEb;IAEA,OAAOV;AACT;AAEO,eAAeW,aAAa,EACjCC,WAAW,EACXtB,UAAU,EACVD,kBAAkB,EAClBE,WAAW,EACXsB,WAAW,EAOZ;IAIC,MAAM,EAAE5B,aAAa,EAAEC,YAAY,EAAEC,iBAAiB,EAAEC,kBAAkB,EAAE,GAC1EwB;IAEF,MAAMpB,uBAA6C;QACjDqB;IACF;IAEA,MAAMb,SAAS,MAAMhB,oBAAoB;QACvCC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;IACF;IAEA,OAAO;QACLQ;QACAR;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/openai/responses/responses.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/responses/responses.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ResolveImageDetail } from '../../../model-adapter/types';\nimport type { ModelRequestConfigInput } from '../../../model-adapter/types';\nimport { createProxyAgentIfNeeded } from '../../proxy';\nimport type {
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/responses/responses.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/responses/responses.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport type { ResolveImageDetail } from '../../../model-adapter/types';\nimport type { ModelRequestConfigInput } from '../../../model-adapter/types';\nimport { createProxyAgentIfNeeded } from '../../proxy';\nimport type {\n ModelCallContext,\n ModelCallMessages,\n OpenAIProtocolCallResult,\n} from '../../types';\nimport { stringifyForDebug } from '../../utils';\nimport { callResponsesNonStreaming } from './non-stream';\nimport { callResponsesStream } from './stream';\nimport type { ResponsesCallOptions } from './types';\nimport { toResponsesInput } from './utils';\n\nexport async function prepareResponses({\n messages,\n modelRuntime,\n options,\n}: Pick<ModelCallContext, 'modelRuntime' | 'options'> & {\n messages: ModelCallMessages;\n}) {\n const debugCall = getDebug('ai:call');\n const { config: modelConfig, adapter } = modelRuntime;\n const modelCallInput: ModelRequestConfigInput = {\n intent: modelConfig.intent,\n userConfig: {\n temperature: modelConfig.temperature,\n reasoningEnabled: modelConfig.reasoningEnabled,\n reasoningEffort: modelConfig.reasoningEffort,\n reasoningBudget: modelConfig.reasoningBudget,\n responseFormat: modelConfig.responseFormat,\n },\n semanticRetryAttempt: options?.semanticRetryAttempt,\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n expectedJsonObjectResponse: options?.expectedJsonObjectResponse,\n };\n const { config } = adapter.responses.buildResponsesParams(modelCallInput);\n debugCall(`adapter responses params: ${stringifyForDebug({ config })}`);\n const resolveImageDetail: ResolveImageDetail = ({ imageDetail }) =>\n adapter.resolveImageDetail({\n imageDetail,\n intent: modelConfig.intent,\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n });\n const requestParams = { ...config, ...modelConfig.extraBody };\n if (requestParams.background === true) {\n throw new Error('Responses background mode is not supported');\n }\n const requestBodyParams: ResponsesCallOptions['requestBodyParams'] = {\n store: false,\n ...requestParams,\n model: modelConfig.modelName,\n input: toResponsesInput(messages, resolveImageDetail),\n };\n const proxyAgent = await createProxyAgentIfNeeded({\n socksProxy: modelConfig.socksProxy,\n httpProxy: modelConfig.httpProxy,\n });\n return {\n requestParams: requestBodyParams,\n proxyAgent,\n };\n}\n\nexport function callResponses(\n options: ResponsesCallOptions,\n stream: boolean,\n): Promise<OpenAIProtocolCallResult> {\n return stream\n ? callResponsesStream(options)\n : callResponsesNonStreaming(options);\n}\n"],"names":["prepareResponses","messages","modelRuntime","options","debugCall","getDebug","modelConfig","adapter","modelCallInput","config","stringifyForDebug","resolveImageDetail","imageDetail","requestParams","Error","requestBodyParams","toResponsesInput","proxyAgent","createProxyAgentIfNeeded","callResponses","stream","callResponsesStream","callResponsesNonStreaming"],"mappings":";;;;;;AAeO,eAAeA,iBAAiB,EACrCC,QAAQ,EACRC,YAAY,EACZC,OAAO,EAGR;IACC,MAAMC,YAAYC,SAAS;IAC3B,MAAM,EAAE,QAAQC,WAAW,EAAEC,OAAO,EAAE,GAAGL;IACzC,MAAMM,iBAA0C;QAC9C,QAAQF,YAAY,MAAM;QAC1B,YAAY;YACV,aAAaA,YAAY,WAAW;YACpC,kBAAkBA,YAAY,gBAAgB;YAC9C,iBAAiBA,YAAY,eAAe;YAC5C,iBAAiBA,YAAY,eAAe;YAC5C,gBAAgBA,YAAY,cAAc;QAC5C;QACA,sBAAsBH,SAAS;QAC/B,6BAA6BA,SAAS;QACtC,4BAA4BA,SAAS;IACvC;IACA,MAAM,EAAEM,MAAM,EAAE,GAAGF,QAAQ,SAAS,CAAC,oBAAoB,CAACC;IAC1DJ,UAAU,CAAC,0BAA0B,EAAEM,kBAAkB;QAAED;IAAO,IAAI;IACtE,MAAME,qBAAyC,CAAC,EAAEC,WAAW,EAAE,GAC7DL,QAAQ,kBAAkB,CAAC;YACzBK;YACA,QAAQN,YAAY,MAAM;YAC1B,6BAA6BH,SAAS;QACxC;IACF,MAAMU,gBAAgB;QAAE,GAAGJ,MAAM;QAAE,GAAGH,YAAY,SAAS;IAAC;IAC5D,IAAIO,AAA6B,SAA7BA,cAAc,UAAU,EAC1B,MAAM,IAAIC,MAAM;IAElB,MAAMC,oBAA+D;QACnE,OAAO;QACP,GAAGF,aAAa;QAChB,OAAOP,YAAY,SAAS;QAC5B,OAAOU,iBAAiBf,UAAUU;IACpC;IACA,MAAMM,aAAa,MAAMC,yBAAyB;QAChD,YAAYZ,YAAY,UAAU;QAClC,WAAWA,YAAY,SAAS;IAClC;IACA,OAAO;QACL,eAAeS;QACfE;IACF;AACF;AAEO,SAASE,cACdhB,OAA6B,EAC7BiB,MAAe;IAEf,OAAOA,SACHC,oBAAoBlB,WACpBmB,0BAA0BnB;AAChC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
+
import { tokenResponseError, withSiwcStage } from "./errors.mjs";
|
|
3
|
+
const ISSUER = 'https://auth.openai.com';
|
|
4
|
+
const RESOURCE = 'https://api.openai.com/v1';
|
|
5
|
+
function base64url(bytes) {
|
|
6
|
+
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
7
|
+
}
|
|
8
|
+
async function createSiwcAuthorization(options) {
|
|
9
|
+
const { hostId, clientId, redirectUri, agentName } = options;
|
|
10
|
+
const random = ()=>base64url(crypto.getRandomValues(new Uint8Array(32)));
|
|
11
|
+
const attempt = {
|
|
12
|
+
hostId,
|
|
13
|
+
redirectUri,
|
|
14
|
+
clientId,
|
|
15
|
+
state: random(),
|
|
16
|
+
nonce: random(),
|
|
17
|
+
verifier: random(),
|
|
18
|
+
expiresAt: Date.now() + 300000
|
|
19
|
+
};
|
|
20
|
+
const challenge = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(attempt.verifier));
|
|
21
|
+
const url = new URL(`${ISSUER}/api/accounts/authorize`);
|
|
22
|
+
url.search = new URLSearchParams({
|
|
23
|
+
client_id: clientId ?? 'dynamic_agent_client',
|
|
24
|
+
...clientId ? {} : {
|
|
25
|
+
agent_name_hint: agentName
|
|
26
|
+
},
|
|
27
|
+
ext_agent_host_id: hostId,
|
|
28
|
+
response_type: 'code',
|
|
29
|
+
redirect_uri: attempt.redirectUri,
|
|
30
|
+
scope: 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct',
|
|
31
|
+
resource: RESOURCE,
|
|
32
|
+
state: attempt.state,
|
|
33
|
+
nonce: attempt.nonce,
|
|
34
|
+
code_challenge_method: 'S256',
|
|
35
|
+
code_challenge: base64url(new Uint8Array(challenge))
|
|
36
|
+
}).toString();
|
|
37
|
+
return {
|
|
38
|
+
attempt,
|
|
39
|
+
url: url.toString()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parseSiwcCallback(url, attempt) {
|
|
43
|
+
const callback = new URL(url);
|
|
44
|
+
const expected = new URL(attempt.redirectUri);
|
|
45
|
+
if (callback.origin !== expected.origin || callback.pathname !== expected.pathname) return;
|
|
46
|
+
const params = callback.searchParams;
|
|
47
|
+
if (1 !== params.getAll('state').length || params.get('state') !== attempt.state) return;
|
|
48
|
+
if (Date.now() >= attempt.expiresAt) throw new Error('Authorization timed out. Start authorization again.');
|
|
49
|
+
if (params.has('error')) throw new Error(`OpenAI authorization failed: ${params.get("error_description") || params.get('error')}`);
|
|
50
|
+
const code = params.get('code');
|
|
51
|
+
const clientId = params.get('client_id') || attempt.clientId;
|
|
52
|
+
if (!code || !clientId || 'dynamic_agent_client' === clientId || 1 !== params.getAll('code').length || params.getAll('client_id').length > 1 || attempt.clientId && clientId !== attempt.clientId) throw new Error('Invalid OpenAI authorization callback.');
|
|
53
|
+
return {
|
|
54
|
+
code,
|
|
55
|
+
clientId
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function exchangeSiwcCode(attempt, callback, signal, dependencies) {
|
|
59
|
+
signal.throwIfAborted();
|
|
60
|
+
const response = await withSiwcStage('Token exchange', async ()=>{
|
|
61
|
+
const response = await (dependencies?.fetch ?? fetch)(`${ISSUER}/api/accounts/oauth/token`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
65
|
+
},
|
|
66
|
+
body: new URLSearchParams({
|
|
67
|
+
grant_type: 'authorization_code',
|
|
68
|
+
client_id: callback.clientId,
|
|
69
|
+
code: callback.code,
|
|
70
|
+
code_verifier: attempt.verifier,
|
|
71
|
+
redirect_uri: attempt.redirectUri,
|
|
72
|
+
resource: RESOURCE
|
|
73
|
+
}),
|
|
74
|
+
signal,
|
|
75
|
+
redirect: 'error'
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) throw await tokenResponseError(response);
|
|
78
|
+
return response;
|
|
79
|
+
});
|
|
80
|
+
const tokens = await withSiwcStage('Token response parsing', ()=>response.json());
|
|
81
|
+
if (!tokens || 'string' != typeof tokens.access_token || !tokens.access_token || 'string' != typeof tokens.refresh_token || !tokens.refresh_token || 'string' != typeof tokens.id_token || 'string' != typeof tokens.scope || 'number' != typeof tokens.expires_in || !Number.isFinite(tokens.expires_in) || tokens.expires_in <= 0 || 'string' != typeof tokens.token_type || 'bearer' !== tokens.token_type.toLowerCase()) throw new Error('OpenAI returned an invalid token response.');
|
|
82
|
+
const scopes = tokens.scope.split(/\s+/);
|
|
83
|
+
if (!scopes.includes('chatgpt.tokens.use.direct')) throw new Error('ChatGPT plan usage was not authorized.');
|
|
84
|
+
const { payload } = await withSiwcStage('ID token verification', ()=>jwtVerify(tokens.id_token, dependencies?.keys ?? createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`)), {
|
|
85
|
+
issuer: ISSUER,
|
|
86
|
+
audience: callback.clientId,
|
|
87
|
+
algorithms: [
|
|
88
|
+
'RS256'
|
|
89
|
+
],
|
|
90
|
+
clockTolerance: 1 / 0,
|
|
91
|
+
requiredClaims: [
|
|
92
|
+
'sub',
|
|
93
|
+
'exp',
|
|
94
|
+
'iat',
|
|
95
|
+
'nonce'
|
|
96
|
+
]
|
|
97
|
+
}));
|
|
98
|
+
if (payload.nonce !== attempt.nonce || !payload.sub) throw new Error('OpenAI login identity or nonce did not match.');
|
|
99
|
+
signal.throwIfAborted();
|
|
100
|
+
return {
|
|
101
|
+
...'string' == typeof payload.email ? {
|
|
102
|
+
email: payload.email
|
|
103
|
+
} : {},
|
|
104
|
+
ext_agent_host_id: attempt.hostId,
|
|
105
|
+
client_id: callback.clientId,
|
|
106
|
+
subject: payload.sub,
|
|
107
|
+
issuer: ISSUER,
|
|
108
|
+
access_token: tokens.access_token,
|
|
109
|
+
refresh_token: tokens.refresh_token,
|
|
110
|
+
id_token: tokens.id_token,
|
|
111
|
+
scopes,
|
|
112
|
+
expires_at: Date.now() + 1000 * tokens.expires_in
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export { createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=authorization.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/authorization.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/authorization.ts"],"sourcesContent":["import { type JWTVerifyGetKey, createRemoteJWKSet, jwtVerify } from 'jose';\nimport { tokenResponseError, withSiwcStage } from './errors';\n\nconst ISSUER = 'https://auth.openai.com';\nconst RESOURCE = 'https://api.openai.com/v1';\nexport interface SiwcLoginAttempt {\n hostId: string;\n redirectUri: string;\n clientId?: string;\n state: string;\n nonce: string;\n verifier: string;\n expiresAt: number;\n}\n\nfunction base64url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\nexport async function createSiwcAuthorization(options: {\n hostId: string;\n clientId?: string;\n redirectUri: string;\n agentName: string;\n}) {\n const { hostId, clientId, redirectUri, agentName } = options;\n const random = () => base64url(crypto.getRandomValues(new Uint8Array(32)));\n const attempt: SiwcLoginAttempt = {\n hostId,\n redirectUri,\n clientId,\n state: random(),\n nonce: random(),\n verifier: random(),\n expiresAt: Date.now() + 5 * 60_000,\n };\n const challenge = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(attempt.verifier),\n );\n const url = new URL(`${ISSUER}/api/accounts/authorize`);\n url.search = new URLSearchParams({\n client_id: clientId ?? 'dynamic_agent_client',\n ...(clientId ? {} : { agent_name_hint: agentName }),\n ext_agent_host_id: hostId,\n response_type: 'code',\n redirect_uri: attempt.redirectUri,\n scope:\n 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct',\n resource: RESOURCE,\n state: attempt.state,\n nonce: attempt.nonce,\n code_challenge_method: 'S256',\n code_challenge: base64url(new Uint8Array(challenge)),\n }).toString();\n return { attempt, url: url.toString() };\n}\n\nexport function parseSiwcCallback(url: string, attempt: SiwcLoginAttempt) {\n const callback = new URL(url);\n const expected = new URL(attempt.redirectUri);\n if (\n callback.origin !== expected.origin ||\n callback.pathname !== expected.pathname\n ) {\n return undefined;\n }\n const params = callback.searchParams;\n if (\n params.getAll('state').length !== 1 ||\n params.get('state') !== attempt.state\n ) {\n return undefined;\n }\n if (Date.now() >= attempt.expiresAt) {\n throw new Error('Authorization timed out. Start authorization again.');\n }\n if (params.has('error')) {\n throw new Error(\n `OpenAI authorization failed: ${params.get('error_description') || params.get('error')}`,\n );\n }\n const code = params.get('code');\n const clientId = params.get('client_id') || attempt.clientId;\n if (\n !code ||\n !clientId ||\n clientId === 'dynamic_agent_client' ||\n params.getAll('code').length !== 1 ||\n params.getAll('client_id').length > 1 ||\n (attempt.clientId && clientId !== attempt.clientId)\n ) {\n throw new Error('Invalid OpenAI authorization callback.');\n }\n return { code, clientId };\n}\n\nexport async function exchangeSiwcCode(\n attempt: SiwcLoginAttempt,\n callback: { code: string; clientId: string },\n signal: AbortSignal,\n dependencies?: { fetch: typeof fetch; keys: JWTVerifyGetKey },\n) {\n signal.throwIfAborted();\n const response = await withSiwcStage('Token exchange', async () => {\n const response = await (dependencies?.fetch ?? fetch)(\n `${ISSUER}/api/accounts/oauth/token`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: callback.clientId,\n code: callback.code,\n code_verifier: attempt.verifier,\n redirect_uri: attempt.redirectUri,\n resource: RESOURCE,\n }),\n signal,\n redirect: 'error',\n },\n );\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n return response;\n });\n const tokens = await withSiwcStage('Token response parsing', () =>\n response.json(),\n );\n if (\n !tokens ||\n typeof tokens.access_token !== 'string' ||\n !tokens.access_token ||\n typeof tokens.refresh_token !== 'string' ||\n !tokens.refresh_token ||\n typeof tokens.id_token !== 'string' ||\n typeof tokens.scope !== 'string' ||\n typeof tokens.expires_in !== 'number' ||\n !Number.isFinite(tokens.expires_in) ||\n tokens.expires_in <= 0 ||\n typeof tokens.token_type !== 'string' ||\n tokens.token_type.toLowerCase() !== 'bearer'\n ) {\n throw new Error('OpenAI returned an invalid token response.');\n }\n const scopes = tokens.scope.split(/\\s+/);\n if (!scopes.includes('chatgpt.tokens.use.direct')) {\n throw new Error('ChatGPT plan usage was not authorized.');\n }\n const { payload } = await withSiwcStage('ID token verification', () =>\n jwtVerify(\n tokens.id_token,\n dependencies?.keys ??\n createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`)),\n {\n issuer: ISSUER,\n audience: callback.clientId,\n algorithms: ['RS256'],\n // Some CI machines have incorrect system clocks. Do not compare ID token\n // exp/nbf claims against local time when accepting newly returned tokens.\n // Keep signature, issuer, audience and required-claim validation.\n clockTolerance: Number.POSITIVE_INFINITY,\n requiredClaims: ['sub', 'exp', 'iat', 'nonce'],\n },\n ),\n );\n if (payload.nonce !== attempt.nonce || !payload.sub) {\n throw new Error('OpenAI login identity or nonce did not match.');\n }\n signal.throwIfAborted();\n return {\n ...(typeof payload.email === 'string' ? { email: payload.email } : {}),\n ext_agent_host_id: attempt.hostId,\n client_id: callback.clientId,\n subject: payload.sub,\n issuer: ISSUER,\n access_token: tokens.access_token as string,\n refresh_token: tokens.refresh_token as string,\n id_token: tokens.id_token as string,\n scopes,\n expires_at: Date.now() + tokens.expires_in * 1000,\n };\n}\n"],"names":["ISSUER","RESOURCE","base64url","bytes","btoa","String","createSiwcAuthorization","options","hostId","clientId","redirectUri","agentName","random","crypto","Uint8Array","attempt","Date","challenge","TextEncoder","url","URL","URLSearchParams","parseSiwcCallback","callback","expected","params","Error","code","exchangeSiwcCode","signal","dependencies","response","withSiwcStage","fetch","tokenResponseError","tokens","Number","scopes","payload","jwtVerify","createRemoteJWKSet"],"mappings":";;AAGA,MAAMA,SAAS;AACf,MAAMC,WAAW;AAWjB,SAASC,UAAUC,KAAiB;IAClC,OAAOC,KAAKC,OAAO,YAAY,IAAIF,QAChC,OAAO,CAAC,OAAO,KACf,OAAO,CAAC,OAAO,KACf,OAAO,CAAC,OAAO;AACpB;AAEO,eAAeG,wBAAwBC,OAK7C;IACC,MAAM,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,WAAW,EAAEC,SAAS,EAAE,GAAGJ;IACrD,MAAMK,SAAS,IAAMV,UAAUW,OAAO,eAAe,CAAC,IAAIC,WAAW;IACrE,MAAMC,UAA4B;QAChCP;QACAE;QACAD;QACA,OAAOG;QACP,OAAOA;QACP,UAAUA;QACV,WAAWI,KAAK,GAAG,KAAK;IAC1B;IACA,MAAMC,YAAY,MAAMJ,OAAO,MAAM,CAAC,MAAM,CAC1C,WACA,IAAIK,cAAc,MAAM,CAACH,QAAQ,QAAQ;IAE3C,MAAMI,MAAM,IAAIC,IAAI,GAAGpB,OAAO,uBAAuB,CAAC;IACtDmB,IAAI,MAAM,GAAG,IAAIE,gBAAgB;QAC/B,WAAWZ,YAAY;QACvB,GAAIA,WAAW,CAAC,IAAI;YAAE,iBAAiBE;QAAU,CAAC;QAClD,mBAAmBH;QACnB,eAAe;QACf,cAAcO,QAAQ,WAAW;QACjC,OACE;QACF,UAAUd;QACV,OAAOc,QAAQ,KAAK;QACpB,OAAOA,QAAQ,KAAK;QACpB,uBAAuB;QACvB,gBAAgBb,UAAU,IAAIY,WAAWG;IAC3C,GAAG,QAAQ;IACX,OAAO;QAAEF;QAAS,KAAKI,IAAI,QAAQ;IAAG;AACxC;AAEO,SAASG,kBAAkBH,GAAW,EAAEJ,OAAyB;IACtE,MAAMQ,WAAW,IAAIH,IAAID;IACzB,MAAMK,WAAW,IAAIJ,IAAIL,QAAQ,WAAW;IAC5C,IACEQ,SAAS,MAAM,KAAKC,SAAS,MAAM,IACnCD,SAAS,QAAQ,KAAKC,SAAS,QAAQ,EAEvC;IAEF,MAAMC,SAASF,SAAS,YAAY;IACpC,IACEE,AAAkC,MAAlCA,OAAO,MAAM,CAAC,SAAS,MAAM,IAC7BA,OAAO,GAAG,CAAC,aAAaV,QAAQ,KAAK,EAErC;IAEF,IAAIC,KAAK,GAAG,MAAMD,QAAQ,SAAS,EACjC,MAAM,IAAIW,MAAM;IAElB,IAAID,OAAO,GAAG,CAAC,UACb,MAAM,IAAIC,MACR,CAAC,6BAA6B,EAAED,OAAO,GAAG,CAAC,wBAAwBA,OAAO,GAAG,CAAC,UAAU;IAG5F,MAAME,OAAOF,OAAO,GAAG,CAAC;IACxB,MAAMhB,WAAWgB,OAAO,GAAG,CAAC,gBAAgBV,QAAQ,QAAQ;IAC5D,IACE,CAACY,QACD,CAAClB,YACDA,AAAa,2BAAbA,YACAgB,AAAiC,MAAjCA,OAAO,MAAM,CAAC,QAAQ,MAAM,IAC5BA,OAAO,MAAM,CAAC,aAAa,MAAM,GAAG,KACnCV,QAAQ,QAAQ,IAAIN,aAAaM,QAAQ,QAAQ,EAElD,MAAM,IAAIW,MAAM;IAElB,OAAO;QAAEC;QAAMlB;IAAS;AAC1B;AAEO,eAAemB,iBACpBb,OAAyB,EACzBQ,QAA4C,EAC5CM,MAAmB,EACnBC,YAA6D;IAE7DD,OAAO,cAAc;IACrB,MAAME,WAAW,MAAMC,cAAc,kBAAkB;QACrD,MAAMD,WAAW,MAAOD,AAAAA,CAAAA,cAAc,SAASG,KAAI,EACjD,GAAGjC,OAAO,yBAAyB,CAAC,EACpC;YACE,QAAQ;YACR,SAAS;gBAAE,gBAAgB;YAAoC;YAC/D,MAAM,IAAIqB,gBAAgB;gBACxB,YAAY;gBACZ,WAAWE,SAAS,QAAQ;gBAC5B,MAAMA,SAAS,IAAI;gBACnB,eAAeR,QAAQ,QAAQ;gBAC/B,cAAcA,QAAQ,WAAW;gBACjC,UAAUd;YACZ;YACA4B;YACA,UAAU;QACZ;QAEF,IAAI,CAACE,SAAS,EAAE,EACd,MAAM,MAAMG,mBAAmBH;QAEjC,OAAOA;IACT;IACA,MAAMI,SAAS,MAAMH,cAAc,0BAA0B,IAC3DD,SAAS,IAAI;IAEf,IACE,CAACI,UACD,AAA+B,YAA/B,OAAOA,OAAO,YAAY,IAC1B,CAACA,OAAO,YAAY,IACpB,AAAgC,YAAhC,OAAOA,OAAO,aAAa,IAC3B,CAACA,OAAO,aAAa,IACrB,AAA2B,YAA3B,OAAOA,OAAO,QAAQ,IACtB,AAAwB,YAAxB,OAAOA,OAAO,KAAK,IACnB,AAA6B,YAA7B,OAAOA,OAAO,UAAU,IACxB,CAACC,OAAO,QAAQ,CAACD,OAAO,UAAU,KAClCA,OAAO,UAAU,IAAI,KACrB,AAA6B,YAA7B,OAAOA,OAAO,UAAU,IACxBA,AAAoC,aAApCA,OAAO,UAAU,CAAC,WAAW,IAE7B,MAAM,IAAIT,MAAM;IAElB,MAAMW,SAASF,OAAO,KAAK,CAAC,KAAK,CAAC;IAClC,IAAI,CAACE,OAAO,QAAQ,CAAC,8BACnB,MAAM,IAAIX,MAAM;IAElB,MAAM,EAAEY,OAAO,EAAE,GAAG,MAAMN,cAAc,yBAAyB,IAC/DO,UACEJ,OAAO,QAAQ,EACfL,cAAc,QACZU,mBAAmB,IAAIpB,IAAI,GAAGpB,OAAO,sBAAsB,CAAC,IAC9D;YACE,QAAQA;YACR,UAAUuB,SAAS,QAAQ;YAC3B,YAAY;gBAAC;aAAQ;YAIrB,gBAAgBa;YAChB,gBAAgB;gBAAC;gBAAO;gBAAO;gBAAO;aAAQ;QAChD;IAGJ,IAAIE,QAAQ,KAAK,KAAKvB,QAAQ,KAAK,IAAI,CAACuB,QAAQ,GAAG,EACjD,MAAM,IAAIZ,MAAM;IAElBG,OAAO,cAAc;IACrB,OAAO;QACL,GAAI,AAAyB,YAAzB,OAAOS,QAAQ,KAAK,GAAgB;YAAE,OAAOA,QAAQ,KAAK;QAAC,IAAI,CAAC,CAAC;QACrE,mBAAmBvB,QAAQ,MAAM;QACjC,WAAWQ,SAAS,QAAQ;QAC5B,SAASe,QAAQ,GAAG;QACpB,QAAQtC;QACR,cAAcmC,OAAO,YAAY;QACjC,eAAeA,OAAO,aAAa;QACnC,UAAUA,OAAO,QAAQ;QACzBE;QACA,YAAYrB,KAAK,GAAG,KAAKmB,AAAoB,OAApBA,OAAO,UAAU;IAC5C;AACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
2
|
+
const SIWC_AUTH_STORAGE_KEY = 'midscene-siwc-auth';
|
|
3
|
+
async function getCredentialStorage() {
|
|
4
|
+
const chrome = globalThis.chrome;
|
|
5
|
+
if (chrome?.runtime?.id && chrome.storage?.local) {
|
|
6
|
+
const storage = chrome.storage.local;
|
|
7
|
+
const reauthorizationHint = 'Open Model Env Config and select "Use your ChatGPT plan" to sign in again. See https://midscenejs.com/chatgpt-subscription.html';
|
|
8
|
+
return {
|
|
9
|
+
read: async ()=>{
|
|
10
|
+
const saved = await storage.get(SIWC_AUTH_STORAGE_KEY);
|
|
11
|
+
const record = saved[SIWC_AUTH_STORAGE_KEY];
|
|
12
|
+
if ('string' != typeof record?.credentials?.access_token || !record.credentials.access_token.trim()) throw new Error(`SIWC credentials not found in this extension. ${reauthorizationHint}`);
|
|
13
|
+
return record;
|
|
14
|
+
},
|
|
15
|
+
save: (updated)=>storage.set({
|
|
16
|
+
[SIWC_AUTH_STORAGE_KEY]: updated
|
|
17
|
+
}),
|
|
18
|
+
reauthorizationHint
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (ifInBrowser) throw new Error('SIWC auth mode requires Node.js or the Midscene Chrome extension.');
|
|
22
|
+
const fsModule = 'node:fs/promises';
|
|
23
|
+
const osModule = 'node:os';
|
|
24
|
+
const pathModule = 'node:path';
|
|
25
|
+
const { readFile, writeFile, rename, rm } = await import(fsModule);
|
|
26
|
+
const { homedir } = await import(osModule);
|
|
27
|
+
const { join } = await import(pathModule);
|
|
28
|
+
const credentialsPath = join(homedir(), '.midscene', 'siwc.json');
|
|
29
|
+
const reauthorizationHint = 'Run "midscene model siwc login" to sign in again. See https://midscenejs.com/chatgpt-subscription.html';
|
|
30
|
+
return {
|
|
31
|
+
read: async ()=>{
|
|
32
|
+
const content = await readFile(credentialsPath, 'utf8').catch((cause)=>{
|
|
33
|
+
throw new Error('ENOENT' === cause.code ? `SIWC credentials not found at ${credentialsPath}. ${reauthorizationHint}` : `Unable to read SIWC credentials at ${credentialsPath}.`, {
|
|
34
|
+
cause
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
const record = (()=>{
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(content);
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error(`Invalid JSON in SIWC credentials at ${credentialsPath}.`);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
if ('string' != typeof record?.credentials?.access_token || !record.credentials.access_token.trim()) throw new Error(`Missing access token in ${credentialsPath}. ${reauthorizationHint}`);
|
|
45
|
+
return record;
|
|
46
|
+
},
|
|
47
|
+
save: async (updated)=>{
|
|
48
|
+
const temporary = `${credentialsPath}.${globalThis.crypto.randomUUID()}.tmp`;
|
|
49
|
+
try {
|
|
50
|
+
await writeFile(temporary, `${JSON.stringify(updated, null, 2)}\n`, {
|
|
51
|
+
mode: 384,
|
|
52
|
+
flag: 'wx'
|
|
53
|
+
});
|
|
54
|
+
await rename(temporary, credentialsPath);
|
|
55
|
+
} finally{
|
|
56
|
+
await rm(temporary, {
|
|
57
|
+
force: true
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
reauthorizationHint
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export { SIWC_AUTH_STORAGE_KEY, getCredentialStorage };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=credential-storage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/credential-storage.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/credential-storage.ts"],"sourcesContent":["import { ifInBrowser } from '@midscene/shared/utils';\n\nexport const SIWC_AUTH_STORAGE_KEY = 'midscene-siwc-auth';\n\ninterface SiwcAuth {\n created_at?: string;\n updated_at?: string;\n credentials: {\n access_token: string;\n expires_at: number;\n subject?: string;\n id_token?: string;\n client_id?: string;\n refresh_token?: string;\n [key: string]: unknown;\n };\n}\n\n// Keep Chrome types optional: Core also runs in Node.js and ordinary web pages.\ntype ChromeExtensionRuntime = {\n runtime?: { id?: string };\n storage?: {\n local?: {\n get(key: string): Promise<Record<string, SiwcAuth | undefined>>;\n set(value: Record<string, SiwcAuth>): Promise<void>;\n };\n };\n};\n\nexport async function getCredentialStorage(): Promise<{\n read: () => Promise<SiwcAuth>;\n save: (record: SiwcAuth) => Promise<void>;\n reauthorizationHint: string;\n}> {\n const chrome = (\n globalThis as typeof globalThis & { chrome?: ChromeExtensionRuntime }\n ).chrome;\n if (chrome?.runtime?.id && chrome.storage?.local) {\n const storage = chrome.storage.local;\n const reauthorizationHint =\n 'Open Model Env Config and select \"Use your ChatGPT plan\" to sign in again. See https://midscenejs.com/chatgpt-subscription.html';\n return {\n read: async () => {\n const saved = await storage.get(SIWC_AUTH_STORAGE_KEY);\n const record = saved[SIWC_AUTH_STORAGE_KEY];\n if (\n typeof record?.credentials?.access_token !== 'string' ||\n !record.credentials.access_token.trim()\n ) {\n throw new Error(\n `SIWC credentials not found in this extension. ${reauthorizationHint}`,\n );\n }\n return record;\n },\n save: (updated) => storage.set({ [SIWC_AUTH_STORAGE_KEY]: updated }),\n reauthorizationHint,\n };\n }\n if (ifInBrowser) {\n throw new Error(\n 'SIWC auth mode requires Node.js or the Midscene Chrome extension.',\n );\n }\n // Keep Node-only dependencies out of browser bundles.\n const fsModule = 'node:fs/promises';\n const osModule = 'node:os';\n const pathModule = 'node:path';\n const { readFile, writeFile, rename, rm } = await import(fsModule);\n const { homedir } = await import(osModule);\n const { join } = await import(pathModule);\n const credentialsPath = join(homedir(), '.midscene', 'siwc.json');\n const reauthorizationHint =\n 'Run \"midscene model siwc login\" to sign in again. See https://midscenejs.com/chatgpt-subscription.html';\n return {\n read: async () => {\n const content = await readFile(credentialsPath, 'utf8').catch(\n (cause: NodeJS.ErrnoException) => {\n throw new Error(\n cause.code === 'ENOENT'\n ? `SIWC credentials not found at ${credentialsPath}. ${reauthorizationHint}`\n : `Unable to read SIWC credentials at ${credentialsPath}.`,\n { cause },\n );\n },\n );\n const record = (() => {\n try {\n return JSON.parse(content) as SiwcAuth;\n } catch {\n throw new Error(\n `Invalid JSON in SIWC credentials at ${credentialsPath}.`,\n );\n }\n })();\n if (\n typeof record?.credentials?.access_token !== 'string' ||\n !record.credentials.access_token.trim()\n ) {\n throw new Error(\n `Missing access token in ${credentialsPath}. ${reauthorizationHint}`,\n );\n }\n return record;\n },\n save: async (updated) => {\n // Replace atomically so another reader never sees a partially written JSON file.\n const temporary = `${credentialsPath}.${globalThis.crypto.randomUUID()}.tmp`;\n try {\n await writeFile(temporary, `${JSON.stringify(updated, null, 2)}\\n`, {\n mode: 0o600,\n flag: 'wx',\n });\n await rename(temporary, credentialsPath);\n } finally {\n await rm(temporary, { force: true });\n }\n },\n reauthorizationHint,\n };\n}\n"],"names":["SIWC_AUTH_STORAGE_KEY","getCredentialStorage","chrome","globalThis","storage","reauthorizationHint","saved","record","Error","updated","ifInBrowser","fsModule","osModule","pathModule","readFile","writeFile","rename","rm","homedir","join","credentialsPath","content","cause","JSON","temporary"],"mappings":";AAEO,MAAMA,wBAAwB;AA2B9B,eAAeC;IAKpB,MAAMC,SACJC,WACA,MAAM;IACR,IAAID,QAAQ,SAAS,MAAMA,OAAO,OAAO,EAAE,OAAO;QAChD,MAAME,UAAUF,OAAO,OAAO,CAAC,KAAK;QACpC,MAAMG,sBACJ;QACF,OAAO;YACL,MAAM;gBACJ,MAAMC,QAAQ,MAAMF,QAAQ,GAAG,CAACJ;gBAChC,MAAMO,SAASD,KAAK,CAACN,sBAAsB;gBAC3C,IACE,AAA6C,YAA7C,OAAOO,QAAQ,aAAa,gBAC5B,CAACA,OAAO,WAAW,CAAC,YAAY,CAAC,IAAI,IAErC,MAAM,IAAIC,MACR,CAAC,8CAA8C,EAAEH,qBAAqB;gBAG1E,OAAOE;YACT;YACA,MAAM,CAACE,UAAYL,QAAQ,GAAG,CAAC;oBAAE,CAACJ,sBAAsB,EAAES;gBAAQ;YAClEJ;QACF;IACF;IACA,IAAIK,aACF,MAAM,IAAIF,MACR;IAIJ,MAAMG,WAAW;IACjB,MAAMC,WAAW;IACjB,MAAMC,aAAa;IACnB,MAAM,EAAEC,QAAQ,EAAEC,SAAS,EAAEC,MAAM,EAAEC,EAAE,EAAE,GAAG,MAAM,MAAM,CAACN;IACzD,MAAM,EAAEO,OAAO,EAAE,GAAG,MAAM,MAAM,CAACN;IACjC,MAAM,EAAEO,IAAI,EAAE,GAAG,MAAM,MAAM,CAACN;IAC9B,MAAMO,kBAAkBD,KAAKD,WAAW,aAAa;IACrD,MAAMb,sBACJ;IACF,OAAO;QACL,MAAM;YACJ,MAAMgB,UAAU,MAAMP,SAASM,iBAAiB,QAAQ,KAAK,CAC3D,CAACE;gBACC,MAAM,IAAId,MACRc,AAAe,aAAfA,MAAM,IAAI,GACN,CAAC,8BAA8B,EAAEF,gBAAgB,EAAE,EAAEf,qBAAqB,GAC1E,CAAC,mCAAmC,EAAEe,gBAAgB,CAAC,CAAC,EAC5D;oBAAEE;gBAAM;YAEZ;YAEF,MAAMf,SAAU,AAAC;gBACf,IAAI;oBACF,OAAOgB,KAAK,KAAK,CAACF;gBACpB,EAAE,OAAM;oBACN,MAAM,IAAIb,MACR,CAAC,oCAAoC,EAAEY,gBAAgB,CAAC,CAAC;gBAE7D;YACF;YACA,IACE,AAA6C,YAA7C,OAAOb,QAAQ,aAAa,gBAC5B,CAACA,OAAO,WAAW,CAAC,YAAY,CAAC,IAAI,IAErC,MAAM,IAAIC,MACR,CAAC,wBAAwB,EAAEY,gBAAgB,EAAE,EAAEf,qBAAqB;YAGxE,OAAOE;QACT;QACA,MAAM,OAAOE;YAEX,MAAMe,YAAY,GAAGJ,gBAAgB,CAAC,EAAEjB,WAAW,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;YAC5E,IAAI;gBACF,MAAMY,UAAUS,WAAW,GAAGD,KAAK,SAAS,CAACd,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE;oBAClE,MAAM;oBACN,MAAM;gBACR;gBACA,MAAMO,OAAOQ,WAAWJ;YAC1B,SAAU;gBACR,MAAMH,GAAGO,WAAW;oBAAE,OAAO;gBAAK;YACpC;QACF;QACAnB;IACF;AACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
3
|
+
value: value,
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true
|
|
7
|
+
});
|
|
8
|
+
else obj[key] = value;
|
|
9
|
+
return obj;
|
|
10
|
+
}
|
|
11
|
+
class SiwcTokenExpiredError extends Error {
|
|
12
|
+
constructor(...args){
|
|
13
|
+
super(...args), _define_property(this, "name", 'SiwcTokenExpiredError');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class SiwcError extends Error {
|
|
17
|
+
constructor(...args){
|
|
18
|
+
super(...args), _define_property(this, "name", 'SiwcError');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function withSiwcStage(stage, action) {
|
|
22
|
+
try {
|
|
23
|
+
return await action();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new SiwcError(`Stage: ${stage}\n${error instanceof Error ? error.message : String(error)}`, {
|
|
26
|
+
cause: error
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function tokenResponseError(response) {
|
|
31
|
+
try {
|
|
32
|
+
const body = await response.text();
|
|
33
|
+
return new SiwcError(`HTTP status: ${response.status}\nResponse body: ${body}`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return new SiwcError(`HTTP status: ${response.status}\nUnable to read response body.`, {
|
|
36
|
+
cause: error
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export { SiwcError, SiwcTokenExpiredError, tokenResponseError, withSiwcStage };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/errors.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/errors.ts"],"sourcesContent":["/** Signals an expired SIWC token that may be refreshed before replaying output. */\nexport class SiwcTokenExpiredError extends Error {\n override name = 'SiwcTokenExpiredError';\n}\n\nexport class SiwcError extends Error {\n override name = 'SiwcError';\n}\n\nexport async function withSiwcStage<T>(\n stage: string,\n action: () => Promise<T>,\n): Promise<T> {\n try {\n return await action();\n } catch (error) {\n // Browser callers often display only message; keep the cause readable there too.\n throw new SiwcError(\n `Stage: ${stage}\\n${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n}\n\nexport async function tokenResponseError(\n response: Response,\n): Promise<SiwcError> {\n try {\n const body = await response.text();\n return new SiwcError(\n `HTTP status: ${response.status}\\nResponse body: ${body}`,\n );\n } catch (error) {\n return new SiwcError(\n `HTTP status: ${response.status}\\nUnable to read response body.`,\n { cause: error },\n );\n }\n}\n"],"names":["SiwcTokenExpiredError","Error","SiwcError","withSiwcStage","stage","action","error","String","tokenResponseError","response","body"],"mappings":"AAAiF;;;;;;;;;;AAC1E,MAAMA,8BAA8BC;;QAApC,gBACL,uBAAS,QAAO;;AAClB;AAEO,MAAMC,kBAAkBD;;QAAxB,gBACL,uBAAS,QAAO;;AAClB;AAEO,eAAeE,cACpBC,KAAa,EACbC,MAAwB;IAExB,IAAI;QACF,OAAO,MAAMA;IACf,EAAE,OAAOC,OAAO;QAEd,MAAM,IAAIJ,UACR,CAAC,OAAO,EAAEE,MAAM,EAAE,EAAEE,iBAAiBL,QAAQK,MAAM,OAAO,GAAGC,OAAOD,QAAQ,EAC5E;YAAE,OAAOA;QAAM;IAEnB;AACF;AAEO,eAAeE,mBACpBC,QAAkB;IAElB,IAAI;QACF,MAAMC,OAAO,MAAMD,SAAS,IAAI;QAChC,OAAO,IAAIP,UACT,CAAC,aAAa,EAAEO,SAAS,MAAM,CAAC,iBAAiB,EAAEC,MAAM;IAE7D,EAAE,OAAOJ,OAAO;QACd,OAAO,IAAIJ,UACT,CAAC,aAAa,EAAEO,SAAS,MAAM,CAAC,+BAA+B,CAAC,EAChE;YAAE,OAAOH;QAAM;IAEnB;AACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SiwcTokenExpiredError } from "./errors.mjs";
|
|
2
|
+
import { prepareSiwcCall } from "./prepare-call.mjs";
|
|
3
|
+
import { SIWC_AUTH_STORAGE_KEY } from "./credential-storage.mjs";
|
|
4
|
+
import { siwcTokenManager } from "./token-manager.mjs";
|
|
5
|
+
import { refreshSiwcCredentials } from "./refresh.mjs";
|
|
6
|
+
import { createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback } from "./authorization.mjs";
|
|
7
|
+
export { SIWC_AUTH_STORAGE_KEY, SiwcTokenExpiredError, createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback, prepareSiwcCall, refreshSiwcCredentials, siwcTokenManager };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { siwcTokenManager } from "./token-manager.mjs";
|
|
2
|
+
async function prepareSiwcCall(modelRuntime, options) {
|
|
3
|
+
if ('siwc' !== modelRuntime.config.authMode) return {
|
|
4
|
+
modelRuntime,
|
|
5
|
+
options
|
|
6
|
+
};
|
|
7
|
+
const baseURL = 'https://api.openai.com/v1';
|
|
8
|
+
const apiKey = await siwcTokenManager.getAccessToken();
|
|
9
|
+
return {
|
|
10
|
+
modelRuntime: {
|
|
11
|
+
...modelRuntime,
|
|
12
|
+
config: {
|
|
13
|
+
...modelRuntime.config,
|
|
14
|
+
openaiBaseURL: baseURL,
|
|
15
|
+
openaiApiKey: apiKey,
|
|
16
|
+
openaiExtraConfig: {
|
|
17
|
+
...modelRuntime.config.openaiExtraConfig,
|
|
18
|
+
baseURL,
|
|
19
|
+
apiKey
|
|
20
|
+
},
|
|
21
|
+
protocol: 'openai-responses',
|
|
22
|
+
streamMode: 'stream'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
options: {
|
|
26
|
+
...options,
|
|
27
|
+
stream: true
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export { prepareSiwcCall };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=prepare-call.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/prepare-call.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/prepare-call.ts"],"sourcesContent":["import type { ModelRuntime } from '../../../models';\nimport type { CallAIOptions } from '../../types';\nimport { siwcTokenManager } from './token-manager';\n\n/** Build effective request settings without mutating the user's configuration. */\nexport async function prepareSiwcCall(\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<{ modelRuntime: ModelRuntime; options?: CallAIOptions }> {\n if (modelRuntime.config.authMode !== 'siwc') {\n return { modelRuntime, options };\n }\n const baseURL = 'https://api.openai.com/v1';\n const apiKey = await siwcTokenManager.getAccessToken();\n return {\n modelRuntime: {\n ...modelRuntime,\n config: {\n ...modelRuntime.config,\n openaiBaseURL: baseURL,\n openaiApiKey: apiKey,\n openaiExtraConfig: {\n ...modelRuntime.config.openaiExtraConfig,\n baseURL,\n apiKey,\n },\n protocol: 'openai-responses',\n streamMode: 'stream',\n },\n },\n options: { ...options, stream: true },\n };\n}\n"],"names":["prepareSiwcCall","modelRuntime","options","baseURL","apiKey","siwcTokenManager"],"mappings":";AAKO,eAAeA,gBACpBC,YAA0B,EAC1BC,OAAuB;IAEvB,IAAID,AAAiC,WAAjCA,aAAa,MAAM,CAAC,QAAQ,EAC9B,OAAO;QAAEA;QAAcC;IAAQ;IAEjC,MAAMC,UAAU;IAChB,MAAMC,SAAS,MAAMC,iBAAiB,cAAc;IACpD,OAAO;QACL,cAAc;YACZ,GAAGJ,YAAY;YACf,QAAQ;gBACN,GAAGA,aAAa,MAAM;gBACtB,eAAeE;gBACf,cAAcC;gBACd,mBAAmB;oBACjB,GAAGH,aAAa,MAAM,CAAC,iBAAiB;oBACxCE;oBACAC;gBACF;gBACA,UAAU;gBACV,YAAY;YACd;QACF;QACA,SAAS;YAAE,GAAGF,OAAO;YAAE,QAAQ;QAAK;IACtC;AACF"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
+
import { tokenResponseError, withSiwcStage } from "./errors.mjs";
|
|
3
|
+
async function refreshSiwcCredentials(saved, signal, dependencies) {
|
|
4
|
+
const { client_id: clientId, refresh_token: refreshToken } = saved;
|
|
5
|
+
if (!clientId || !refreshToken) throw new Error('Missing client ID or refresh token.');
|
|
6
|
+
const issuer = 'https://auth.openai.com';
|
|
7
|
+
signal.throwIfAborted();
|
|
8
|
+
const response = await withSiwcStage('Token refresh', async ()=>{
|
|
9
|
+
const response = await (dependencies?.fetch ?? fetch)(`${issuer}/api/accounts/oauth/token`, {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
headers: {
|
|
12
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
13
|
+
},
|
|
14
|
+
body: new URLSearchParams({
|
|
15
|
+
grant_type: 'refresh_token',
|
|
16
|
+
client_id: clientId,
|
|
17
|
+
refresh_token: refreshToken,
|
|
18
|
+
resource: 'https://api.openai.com/v1'
|
|
19
|
+
}),
|
|
20
|
+
signal,
|
|
21
|
+
redirect: 'error'
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) throw await tokenResponseError(response);
|
|
24
|
+
return response;
|
|
25
|
+
});
|
|
26
|
+
const tokens = await withSiwcStage('Refresh response parsing', ()=>response.json());
|
|
27
|
+
if (!tokens || 'string' != typeof tokens.access_token || !tokens.access_token.trim() || 'string' != typeof tokens.refresh_token || !tokens.refresh_token.trim() || 'number' != typeof tokens.expires_in || !Number.isFinite(tokens.expires_in) || tokens.expires_in <= 0 || 'string' != typeof tokens.token_type || 'bearer' !== tokens.token_type.toLowerCase() || 'string' != typeof tokens.scope) throw new Error('OpenAI returned an invalid refresh response.');
|
|
28
|
+
const scopes = tokens.scope.split(/\s+/);
|
|
29
|
+
if (!scopes.includes('chatgpt.tokens.use.direct')) throw new Error('ChatGPT plan usage is no longer authorized.');
|
|
30
|
+
if (void 0 !== tokens.id_token) {
|
|
31
|
+
if ('string' != typeof tokens.id_token || !tokens.id_token) throw new Error('Invalid refreshed ID token.');
|
|
32
|
+
const keys = dependencies?.keys ?? createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`));
|
|
33
|
+
const { payload } = await withSiwcStage('ID token verification', ()=>jwtVerify(tokens.id_token, keys, {
|
|
34
|
+
issuer,
|
|
35
|
+
audience: saved.client_id,
|
|
36
|
+
algorithms: [
|
|
37
|
+
'RS256'
|
|
38
|
+
],
|
|
39
|
+
clockTolerance: 1 / 0,
|
|
40
|
+
requiredClaims: [
|
|
41
|
+
'sub',
|
|
42
|
+
'exp',
|
|
43
|
+
'iat'
|
|
44
|
+
]
|
|
45
|
+
}));
|
|
46
|
+
if (payload.sub !== saved.subject) throw new Error('Refreshed identity does not match the saved account.');
|
|
47
|
+
}
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
return {
|
|
50
|
+
...saved,
|
|
51
|
+
access_token: tokens.access_token,
|
|
52
|
+
refresh_token: tokens.refresh_token,
|
|
53
|
+
id_token: tokens.id_token ?? saved.id_token,
|
|
54
|
+
scopes,
|
|
55
|
+
expires_at: Date.now() + 1000 * tokens.expires_in
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export { refreshSiwcCredentials };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=refresh.mjs.map
|