evolcore 0.0.4 → 0.0.6
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/CHANGELOG.md +65 -3
- package/README.md +4 -12
- package/dist/agents/baseagent.js +39 -0
- package/dist/agents/claude-runner.js +10 -2
- package/dist/agents/codex-runner.js +37 -8
- package/dist/agents/ecagent-runner.js +1159 -0
- package/dist/agents/gemini-runner.js +4 -1
- package/dist/agents/runner-types.js +20 -4
- package/dist/aun/msg/history.js +32 -9
- package/dist/aun/outbox.js +4 -9
- package/dist/channels/aun.js +32 -6
- package/dist/channels/daemon.js +7 -4
- package/dist/channels/dingtalk.js +3 -5
- package/dist/channels/feishu.js +16 -108
- package/dist/channels/wechat.js +4 -4
- package/dist/channels/wecom.js +4 -6
- package/dist/cli/agent-command.js +1 -1
- package/dist/cli/agent.js +42 -11
- package/dist/cli/aun-commands.js +50 -3
- package/dist/cli/daemon-commands.js +127 -21
- package/dist/cli/data-command.js +132 -0
- package/dist/cli/index.js +11 -3
- package/dist/cli/init.js +116 -37
- package/dist/cli/net-check.js +9 -20
- package/dist/cli/restart-monitor.js +11 -2
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg.js +12 -25
- package/dist/config/access-policy-domain.js +0 -20
- package/dist/config/access-policy.js +5 -29
- package/dist/config/builtin-roles.js +3 -1
- package/dist/config/config-field-policy.js +1 -1
- package/dist/config/gateway-config.js +21 -4
- package/dist/config/resolved-config-op.js +10 -0
- package/dist/core/auth/operation-catalog.js +1 -1
- package/dist/core/command/command-handler.js +2 -2
- package/dist/core/command/connect-menu.js +20 -42
- package/dist/core/command/menu-handler.js +11 -9
- package/dist/core/command/role-menu.js +27 -0
- package/dist/core/command/slash-handler.js +18 -14
- package/dist/core/data-migration.js +1434 -0
- package/dist/core/handoff/store.js +14 -4
- package/dist/core/inference/text-inference.js +42 -1
- package/dist/core/message/inbound-admission.js +23 -13
- package/dist/core/message/pending-hints.js +1 -1
- package/dist/core/message/response-engine.js +406 -167
- package/dist/core/message/retry-scheduler.js +71 -0
- package/dist/core/model/model-catalog.js +7 -2
- package/dist/core/model/model-fallback.js +81 -0
- package/dist/core/protected-paths.js +41 -8
- package/dist/core/session/adapters/ecagent-session-file-adapter.js +112 -0
- package/dist/core/session/session-fs-store.js +300 -61
- package/dist/core/session/session-manager.js +225 -21
- package/dist/core/session/session-title.js +1 -0
- package/dist/core/session/session-turn-coordinator.js +8 -1
- package/dist/core/system-channels.js +1 -0
- package/dist/index.js +10 -9
- package/dist/paths.js +35 -27
- package/dist/trigger/feedback.js +16 -0
- package/dist/trigger/legacy-session-history.js +19 -0
- package/dist/trigger/manager.js +12 -1
- package/dist/trigger/parser.js +4 -2
- package/dist/trigger/scheduler.js +185 -15
- package/dist/trigger/session-lock.js +62 -0
- package/dist/trigger/state.js +64 -0
- package/dist/trigger/validation.js +32 -2
- package/dist/utils/aid-bind.js +1 -1
- package/dist/utils/cross-platform.js +8 -2
- package/dist/utils/error-utils.js +9 -0
- package/dist/utils/process-introspect.js +19 -3
- package/dist/utils/welcome.js +2 -3
- package/ecagent/LICENSE +21 -0
- package/ecagent/NOTICE +11 -0
- package/ecagent/README.md +61 -0
- package/ecagent/dist/agent-loop.d.ts +24 -0
- package/ecagent/dist/agent-loop.d.ts.map +1 -0
- package/ecagent/dist/agent-loop.js +547 -0
- package/ecagent/dist/agent-loop.js.map +1 -0
- package/ecagent/dist/agent.d.ts +127 -0
- package/ecagent/dist/agent.d.ts.map +1 -0
- package/ecagent/dist/agent.js +386 -0
- package/ecagent/dist/agent.js.map +1 -0
- package/ecagent/dist/harness/agent-harness.d.ts +95 -0
- package/ecagent/dist/harness/agent-harness.d.ts.map +1 -0
- package/ecagent/dist/harness/agent-harness.js +926 -0
- package/ecagent/dist/harness/agent-harness.js.map +1 -0
- package/ecagent/dist/harness/compaction/branch-summarization.d.ts +51 -0
- package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js +174 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/ecagent/dist/harness/compaction/compaction.d.ts +95 -0
- package/ecagent/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/compaction.js +528 -0
- package/ecagent/dist/harness/compaction/compaction.js.map +1 -0
- package/ecagent/dist/harness/compaction/utils.d.ts +25 -0
- package/ecagent/dist/harness/compaction/utils.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/utils.js +131 -0
- package/ecagent/dist/harness/compaction/utils.js.map +1 -0
- package/ecagent/dist/harness/env/nodejs.d.ts +51 -0
- package/ecagent/dist/harness/env/nodejs.d.ts.map +1 -0
- package/ecagent/dist/harness/env/nodejs.js +513 -0
- package/ecagent/dist/harness/env/nodejs.js.map +1 -0
- package/ecagent/dist/harness/messages.d.ts +51 -0
- package/ecagent/dist/harness/messages.d.ts.map +1 -0
- package/ecagent/dist/harness/messages.js +102 -0
- package/ecagent/dist/harness/messages.js.map +1 -0
- package/ecagent/dist/harness/prompt-templates.d.ts +48 -0
- package/ecagent/dist/harness/prompt-templates.d.ts.map +1 -0
- package/ecagent/dist/harness/prompt-templates.js +230 -0
- package/ecagent/dist/harness/prompt-templates.js.map +1 -0
- package/ecagent/dist/harness/session/jsonl-repo.d.ts +26 -0
- package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +1 -0
- package/ecagent/dist/harness/session/jsonl-repo.js +100 -0
- package/ecagent/dist/harness/session/jsonl-repo.js.map +1 -0
- package/ecagent/dist/harness/session/jsonl-storage.d.ts +34 -0
- package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +1 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +234 -0
- package/ecagent/dist/harness/session/jsonl-storage.js.map +1 -0
- package/ecagent/dist/harness/session/memory-repo.d.ts +18 -0
- package/ecagent/dist/harness/session/memory-repo.d.ts.map +1 -0
- package/ecagent/dist/harness/session/memory-repo.js +44 -0
- package/ecagent/dist/harness/session/memory-repo.js.map +1 -0
- package/ecagent/dist/harness/session/memory-storage.d.ts +25 -0
- package/ecagent/dist/harness/session/memory-storage.d.ts.map +1 -0
- package/ecagent/dist/harness/session/memory-storage.js +111 -0
- package/ecagent/dist/harness/session/memory-storage.js.map +1 -0
- package/ecagent/dist/harness/session/repo-utils.d.ts +11 -0
- package/ecagent/dist/harness/session/repo-utils.d.ts.map +1 -0
- package/ecagent/dist/harness/session/repo-utils.js +39 -0
- package/ecagent/dist/harness/session/repo-utils.js.map +1 -0
- package/ecagent/dist/harness/session/session.d.ts +47 -0
- package/ecagent/dist/harness/session/session.d.ts.map +1 -0
- package/ecagent/dist/harness/session/session.js +245 -0
- package/ecagent/dist/harness/session/session.js.map +1 -0
- package/ecagent/dist/harness/session/uuid.d.ts +2 -0
- package/ecagent/dist/harness/session/uuid.d.ts.map +1 -0
- package/ecagent/dist/harness/session/uuid.js +50 -0
- package/ecagent/dist/harness/session/uuid.js.map +1 -0
- package/ecagent/dist/harness/skills.d.ts +44 -0
- package/ecagent/dist/harness/skills.d.ts.map +1 -0
- package/ecagent/dist/harness/skills.js +311 -0
- package/ecagent/dist/harness/skills.js.map +1 -0
- package/ecagent/dist/harness/system-prompt.d.ts +3 -0
- package/ecagent/dist/harness/system-prompt.d.ts.map +1 -0
- package/ecagent/dist/harness/system-prompt.js +30 -0
- package/ecagent/dist/harness/system-prompt.js.map +1 -0
- package/ecagent/dist/harness/types.d.ts +619 -0
- package/ecagent/dist/harness/types.d.ts.map +1 -0
- package/ecagent/dist/harness/types.js +81 -0
- package/ecagent/dist/harness/types.js.map +1 -0
- package/ecagent/dist/harness/utils/shell-output.d.ts +14 -0
- package/ecagent/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/ecagent/dist/harness/utils/shell-output.js +126 -0
- package/ecagent/dist/harness/utils/shell-output.js.map +1 -0
- package/ecagent/dist/harness/utils/truncate.d.ts +70 -0
- package/ecagent/dist/harness/utils/truncate.d.ts.map +1 -0
- package/ecagent/dist/harness/utils/truncate.js +290 -0
- package/ecagent/dist/harness/utils/truncate.js.map +1 -0
- package/ecagent/dist/index.d.ts +22 -0
- package/ecagent/dist/index.d.ts.map +1 -0
- package/ecagent/dist/index.js +27 -0
- package/ecagent/dist/index.js.map +1 -0
- package/ecagent/dist/node.d.ts +3 -0
- package/ecagent/dist/node.d.ts.map +1 -0
- package/ecagent/dist/node.js +3 -0
- package/ecagent/dist/node.js.map +1 -0
- package/ecagent/dist/proxy.d.ts +69 -0
- package/ecagent/dist/proxy.d.ts.map +1 -0
- package/ecagent/dist/proxy.js +278 -0
- package/ecagent/dist/proxy.js.map +1 -0
- package/ecagent/dist/runtime/api/lazy.d.ts +15 -0
- package/ecagent/dist/runtime/api/lazy.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/lazy.js +59 -0
- package/ecagent/dist/runtime/api/lazy.js.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.d.ts +97 -0
- package/ecagent/dist/runtime/api/pi-messages.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.js +307 -0
- package/ecagent/dist/runtime/api/pi-messages.js.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts +3 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.js +3 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +1 -0
- package/ecagent/dist/runtime/auth/context.d.ts +7 -0
- package/ecagent/dist/runtime/auth/context.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/context.js +42 -0
- package/ecagent/dist/runtime/auth/context.js.map +1 -0
- package/ecagent/dist/runtime/auth/credential-store.d.ts +16 -0
- package/ecagent/dist/runtime/auth/credential-store.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/credential-store.js +39 -0
- package/ecagent/dist/runtime/auth/credential-store.js.map +1 -0
- package/ecagent/dist/runtime/auth/helpers.d.ts +20 -0
- package/ecagent/dist/runtime/auth/helpers.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/helpers.js +46 -0
- package/ecagent/dist/runtime/auth/helpers.js.map +1 -0
- package/ecagent/dist/runtime/auth/resolve.d.ts +26 -0
- package/ecagent/dist/runtime/auth/resolve.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/resolve.js +100 -0
- package/ecagent/dist/runtime/auth/resolve.js.map +1 -0
- package/ecagent/dist/runtime/auth/types.d.ts +180 -0
- package/ecagent/dist/runtime/auth/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/types.js +2 -0
- package/ecagent/dist/runtime/auth/types.js.map +1 -0
- package/ecagent/dist/runtime/compat.d.ts +28 -0
- package/ecagent/dist/runtime/compat.d.ts.map +1 -0
- package/ecagent/dist/runtime/compat.js +86 -0
- package/ecagent/dist/runtime/compat.js.map +1 -0
- package/ecagent/dist/runtime/index.d.ts +20 -0
- package/ecagent/dist/runtime/index.d.ts.map +1 -0
- package/ecagent/dist/runtime/index.js +17 -0
- package/ecagent/dist/runtime/index.js.map +1 -0
- package/ecagent/dist/runtime/models.d.ts +142 -0
- package/ecagent/dist/runtime/models.d.ts.map +1 -0
- package/ecagent/dist/runtime/models.js +245 -0
- package/ecagent/dist/runtime/models.js.map +1 -0
- package/ecagent/dist/runtime/oauth.d.ts +2 -0
- package/ecagent/dist/runtime/oauth.d.ts.map +1 -0
- package/ecagent/dist/runtime/oauth.js +2 -0
- package/ecagent/dist/runtime/oauth.js.map +1 -0
- package/ecagent/dist/runtime/providers/faux.d.ts +97 -0
- package/ecagent/dist/runtime/providers/faux.d.ts.map +1 -0
- package/ecagent/dist/runtime/providers/faux.js +395 -0
- package/ecagent/dist/runtime/providers/faux.js.map +1 -0
- package/ecagent/dist/runtime/types.d.ts +607 -0
- package/ecagent/dist/runtime/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/types.js +2 -0
- package/ecagent/dist/runtime/types.js.map +1 -0
- package/ecagent/dist/runtime/utils/diagnostics.d.ts +19 -0
- package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/diagnostics.js +25 -0
- package/ecagent/dist/runtime/utils/diagnostics.js.map +1 -0
- package/ecagent/dist/runtime/utils/event-stream.d.ts +21 -0
- package/ecagent/dist/runtime/utils/event-stream.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/event-stream.js +77 -0
- package/ecagent/dist/runtime/utils/event-stream.js.map +1 -0
- package/ecagent/dist/runtime/utils/headers.d.ts +4 -0
- package/ecagent/dist/runtime/utils/headers.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/headers.js +18 -0
- package/ecagent/dist/runtime/utils/headers.js.map +1 -0
- package/ecagent/dist/runtime/utils/json-parse.d.ts +16 -0
- package/ecagent/dist/runtime/utils/json-parse.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/json-parse.js +113 -0
- package/ecagent/dist/runtime/utils/json-parse.js.map +1 -0
- package/ecagent/dist/runtime/utils/oauth/types.d.ts +64 -0
- package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/oauth/types.js +2 -0
- package/ecagent/dist/runtime/utils/oauth/types.js.map +1 -0
- package/ecagent/dist/runtime/utils/overflow.d.ts +58 -0
- package/ecagent/dist/runtime/utils/overflow.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/overflow.js +158 -0
- package/ecagent/dist/runtime/utils/overflow.js.map +1 -0
- package/ecagent/dist/runtime/utils/provider-env.d.ts +7 -0
- package/ecagent/dist/runtime/utils/provider-env.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/provider-env.js +44 -0
- package/ecagent/dist/runtime/utils/provider-env.js.map +1 -0
- package/ecagent/dist/runtime/utils/retry.d.ts +12 -0
- package/ecagent/dist/runtime/utils/retry.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/retry.js +90 -0
- package/ecagent/dist/runtime/utils/retry.js.map +1 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.d.ts +17 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.js +21 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.js.map +1 -0
- package/ecagent/dist/runtime/utils/validation.d.ts +18 -0
- package/ecagent/dist/runtime/utils/validation.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/validation.js +269 -0
- package/ecagent/dist/runtime/utils/validation.js.map +1 -0
- package/ecagent/dist/types.d.ts +401 -0
- package/ecagent/dist/types.d.ts.map +1 -0
- package/ecagent/dist/types.js +2 -0
- package/ecagent/dist/types.js.map +1 -0
- package/ecagent/package.json +93 -0
- package/ecagent/src/agent-loop.ts +792 -0
- package/ecagent/src/agent.ts +575 -0
- package/ecagent/src/harness/agent-harness.ts +1029 -0
- package/ecagent/src/harness/compaction/branch-summarization.ts +261 -0
- package/ecagent/src/harness/compaction/compaction.ts +753 -0
- package/ecagent/src/harness/compaction/utils.ts +144 -0
- package/ecagent/src/harness/env/nodejs.ts +569 -0
- package/ecagent/src/harness/messages.ts +164 -0
- package/ecagent/src/harness/prompt-templates.ts +267 -0
- package/ecagent/src/harness/session/jsonl-repo.ts +179 -0
- package/ecagent/src/harness/session/jsonl-storage.ts +314 -0
- package/ecagent/src/harness/session/memory-repo.ts +50 -0
- package/ecagent/src/harness/session/memory-storage.ts +133 -0
- package/ecagent/src/harness/session/repo-utils.ts +51 -0
- package/ecagent/src/harness/session/session.ts +338 -0
- package/ecagent/src/harness/session/uuid.ts +54 -0
- package/ecagent/src/harness/skills.ts +375 -0
- package/ecagent/src/harness/system-prompt.ts +34 -0
- package/ecagent/src/harness/types.ts +838 -0
- package/ecagent/src/harness/utils/shell-output.ts +135 -0
- package/ecagent/src/harness/utils/truncate.ts +344 -0
- package/ecagent/src/index.ts +46 -0
- package/ecagent/src/node.ts +2 -0
- package/ecagent/src/proxy.ts +367 -0
- package/ecagent/src/runtime/api/lazy.ts +70 -0
- package/ecagent/src/runtime/api/pi-messages.lazy.ts +4 -0
- package/ecagent/src/runtime/api/pi-messages.ts +436 -0
- package/ecagent/src/runtime/auth/context.ts +45 -0
- package/ecagent/src/runtime/auth/credential-store.ts +47 -0
- package/ecagent/src/runtime/auth/helpers.ts +46 -0
- package/ecagent/src/runtime/auth/resolve.ts +141 -0
- package/ecagent/src/runtime/auth/types.ts +182 -0
- package/ecagent/src/runtime/compat.ts +158 -0
- package/ecagent/src/runtime/index.ts +31 -0
- package/ecagent/src/runtime/models.ts +452 -0
- package/ecagent/src/runtime/oauth.ts +1 -0
- package/ecagent/src/runtime/providers/faux.ts +538 -0
- package/ecagent/src/runtime/types.ts +718 -0
- package/ecagent/src/runtime/utils/diagnostics.ts +45 -0
- package/ecagent/src/runtime/utils/event-stream.ts +88 -0
- package/ecagent/src/runtime/utils/headers.ts +18 -0
- package/ecagent/src/runtime/utils/json-parse.ts +124 -0
- package/ecagent/src/runtime/utils/oauth/types.ts +79 -0
- package/ecagent/src/runtime/utils/overflow.ts +165 -0
- package/ecagent/src/runtime/utils/provider-env.ts +52 -0
- package/ecagent/src/runtime/utils/retry.ts +101 -0
- package/ecagent/src/runtime/utils/typebox-helpers.ts +24 -0
- package/ecagent/src/runtime/utils/validation.ts +310 -0
- package/ecagent/src/types.ts +430 -0
- package/ecagent/tsconfig.build.json +32 -0
- package/kits/docs/evolcore/config.md +4 -4
- package/kits/docs/evolcore/contact.md +2 -1
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.10.json +370 -0
- package/kits/schemas/agent-config.schema.9.json +2 -12
- package/kits/schemas/defaults.schema.4.json +93 -0
- package/kits/schemas/relation-config.schema.8.json +69 -0
- package/kits/templates/roles/admin.json +1 -1
- package/package.json +22 -6
- package/skills/eclink/SKILL.md +70 -0
- package/skills/eclink/agents/openai.yaml +4 -0
- package/assets/.env.template +0 -4
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-messages API implementation.
|
|
3
|
+
*
|
|
4
|
+
* Streams pi's own message protocol directly to a backend: the request is a
|
|
5
|
+
* single POST of `{ model, context, options }` to `<baseUrl>/messages`, the
|
|
6
|
+
* response is an SSE stream of serialized assistant-message events plus a
|
|
7
|
+
* terminal `done`/`error` event. This is the wire protocol spoken by the
|
|
8
|
+
* Radius gateway, but any backend implementing it can be used, e.g. via a
|
|
9
|
+
* models.json custom provider with `"api": "pi-messages"`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
AssistantMessage,
|
|
14
|
+
AssistantMessageEvent,
|
|
15
|
+
CacheRetention,
|
|
16
|
+
Context,
|
|
17
|
+
Model,
|
|
18
|
+
ProviderEnv,
|
|
19
|
+
SimpleStreamOptions,
|
|
20
|
+
StreamFunction,
|
|
21
|
+
StreamOptions,
|
|
22
|
+
ThinkingLevel,
|
|
23
|
+
ToolCall,
|
|
24
|
+
} from "../types.ts";
|
|
25
|
+
import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic } from "../utils/diagnostics.ts";
|
|
26
|
+
import { AssistantMessageEventStream } from "../utils/event-stream.ts";
|
|
27
|
+
import { headersToRecord, providerHeadersToRecord } from "../utils/headers.ts";
|
|
28
|
+
import { parseStreamingJson } from "../utils/json-parse.ts";
|
|
29
|
+
import { getProviderEnvValue } from "../utils/provider-env.ts";
|
|
30
|
+
|
|
31
|
+
export interface PiMessagesOptions extends StreamOptions {
|
|
32
|
+
reasoning?: ThinkingLevel;
|
|
33
|
+
toolChoice?: "auto" | "none" | "required" | { type: "function"; function: { name: string } };
|
|
34
|
+
/** Ask the backend for debug metadata (e.g. routing response headers). */
|
|
35
|
+
debug?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type PiMessagesUsage = AssistantMessage["usage"];
|
|
39
|
+
type PiMessagesStopReason = AssistantMessage["stopReason"];
|
|
40
|
+
|
|
41
|
+
/** Impact summary of a server-side message rewrite (e.g. a gateway policy). */
|
|
42
|
+
export type PiMessagesRewriteImpact = {
|
|
43
|
+
policyId: string;
|
|
44
|
+
policyVersion: number;
|
|
45
|
+
changed: boolean;
|
|
46
|
+
tokenCountChange: number;
|
|
47
|
+
messageCountChange: number;
|
|
48
|
+
systemPromptChanged: boolean;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Serialized assistant-message event as sent by a pi-messages backend. */
|
|
52
|
+
export type PiMessagesEvent =
|
|
53
|
+
| { type: "start" }
|
|
54
|
+
| { type: "text_start"; contentIndex: number }
|
|
55
|
+
| { type: "text_delta"; contentIndex: number; delta: string }
|
|
56
|
+
| { type: "text_end"; contentIndex: number; content: string; contentSignature?: string }
|
|
57
|
+
| { type: "thinking_start"; contentIndex: number }
|
|
58
|
+
| { type: "thinking_delta"; contentIndex: number; delta: string }
|
|
59
|
+
| {
|
|
60
|
+
type: "thinking_end";
|
|
61
|
+
contentIndex: number;
|
|
62
|
+
content: string;
|
|
63
|
+
contentSignature?: string;
|
|
64
|
+
redacted?: boolean;
|
|
65
|
+
}
|
|
66
|
+
| { type: "toolcall_start"; contentIndex: number; id: string; toolName: string }
|
|
67
|
+
| { type: "toolcall_delta"; contentIndex: number; delta: string }
|
|
68
|
+
| { type: "toolcall_end"; contentIndex: number; toolCall: ToolCall }
|
|
69
|
+
| {
|
|
70
|
+
type: "done";
|
|
71
|
+
reason: Extract<PiMessagesStopReason, "stop" | "length" | "toolUse">;
|
|
72
|
+
usage: PiMessagesUsage;
|
|
73
|
+
responseId?: string;
|
|
74
|
+
rewrite?: PiMessagesRewriteImpact;
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
type: "error";
|
|
78
|
+
reason: Extract<PiMessagesStopReason, "aborted" | "error">;
|
|
79
|
+
usage: PiMessagesUsage;
|
|
80
|
+
errorMessage?: string;
|
|
81
|
+
responseId?: string;
|
|
82
|
+
rewrite?: PiMessagesRewriteImpact;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type PiMessagesErrorBody = {
|
|
86
|
+
error?: {
|
|
87
|
+
message?: unknown;
|
|
88
|
+
code?: unknown;
|
|
89
|
+
details?: unknown;
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export class PiMessagesResponseError extends Error {
|
|
95
|
+
code?: string;
|
|
96
|
+
readonly diagnosticDetails: Record<string, unknown>;
|
|
97
|
+
|
|
98
|
+
constructor(message: string, code: string | undefined, diagnosticDetails: Record<string, unknown>) {
|
|
99
|
+
super(message);
|
|
100
|
+
this.name = "PiMessagesResponseError";
|
|
101
|
+
this.code = code;
|
|
102
|
+
this.diagnosticDetails = diagnosticDetails;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
107
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parsePiMessagesErrorBody(body: string): PiMessagesErrorBody | undefined {
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(body) as unknown;
|
|
113
|
+
return isRecord(parsed) && isRecord(parsed.error) ? (parsed as PiMessagesErrorBody) : undefined;
|
|
114
|
+
} catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function truncateDiagnosticString(value: string): string {
|
|
120
|
+
const maxLength = 8192;
|
|
121
|
+
return value.length > maxLength ? `${value.slice(0, maxLength)}…` : value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatPiMessagesResponseError(
|
|
125
|
+
response: Response,
|
|
126
|
+
body: string,
|
|
127
|
+
errorBody: PiMessagesErrorBody | undefined,
|
|
128
|
+
): string {
|
|
129
|
+
const message = typeof errorBody?.error?.message === "string" ? errorBody.error.message : undefined;
|
|
130
|
+
const code = typeof errorBody?.error?.code === "string" ? errorBody.error.code : undefined;
|
|
131
|
+
const suffix = message ?? body;
|
|
132
|
+
const codeSuffix = code ? ` (${code})` : "";
|
|
133
|
+
return `${response.status} ${response.statusText}: ${suffix}${codeSuffix}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function createPiMessagesResponseError(
|
|
137
|
+
model: Model<"pi-messages">,
|
|
138
|
+
url: URL,
|
|
139
|
+
response: Response,
|
|
140
|
+
body: string,
|
|
141
|
+
): PiMessagesResponseError {
|
|
142
|
+
const errorBody = parsePiMessagesErrorBody(body);
|
|
143
|
+
const code = typeof errorBody?.error?.code === "string" ? errorBody.error.code : undefined;
|
|
144
|
+
return new PiMessagesResponseError(formatPiMessagesResponseError(response, body, errorBody), code, {
|
|
145
|
+
version: 1,
|
|
146
|
+
provider: model.provider,
|
|
147
|
+
model: model.id,
|
|
148
|
+
url: url.toString(),
|
|
149
|
+
status: response.status,
|
|
150
|
+
statusText: response.statusText,
|
|
151
|
+
error: errorBody?.error,
|
|
152
|
+
body: errorBody ? undefined : truncateDiagnosticString(body),
|
|
153
|
+
timestampMs: Date.now(),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function createEmptyUsage(): PiMessagesUsage {
|
|
158
|
+
return {
|
|
159
|
+
input: 0,
|
|
160
|
+
output: 0,
|
|
161
|
+
cacheRead: 0,
|
|
162
|
+
cacheWrite: 0,
|
|
163
|
+
totalTokens: 0,
|
|
164
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function appendRewriteDiagnostic(message: AssistantMessage, rewrite: PiMessagesRewriteImpact | undefined): void {
|
|
169
|
+
if (!rewrite) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
appendAssistantMessageDiagnostic(message, {
|
|
173
|
+
type: "pi_messages_rewrite",
|
|
174
|
+
timestamp: Date.now(),
|
|
175
|
+
details: { ...rewrite },
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function createEventConverter(model: Model<"pi-messages">) {
|
|
180
|
+
const partial: AssistantMessage = {
|
|
181
|
+
role: "assistant",
|
|
182
|
+
content: [],
|
|
183
|
+
api: model.api,
|
|
184
|
+
provider: model.provider,
|
|
185
|
+
model: model.id,
|
|
186
|
+
usage: createEmptyUsage(),
|
|
187
|
+
stopReason: "stop",
|
|
188
|
+
timestamp: Date.now(),
|
|
189
|
+
};
|
|
190
|
+
const toolJson = new Map<number, string>();
|
|
191
|
+
|
|
192
|
+
return (event: PiMessagesEvent): AssistantMessageEvent => {
|
|
193
|
+
switch (event.type) {
|
|
194
|
+
case "done":
|
|
195
|
+
Object.assign(partial, {
|
|
196
|
+
stopReason: event.reason,
|
|
197
|
+
usage: event.usage,
|
|
198
|
+
responseId: event.responseId,
|
|
199
|
+
});
|
|
200
|
+
appendRewriteDiagnostic(partial, event.rewrite);
|
|
201
|
+
return { type: "done", reason: event.reason, message: partial };
|
|
202
|
+
case "error":
|
|
203
|
+
Object.assign(partial, {
|
|
204
|
+
stopReason: event.reason,
|
|
205
|
+
usage: event.usage,
|
|
206
|
+
errorMessage: event.errorMessage,
|
|
207
|
+
responseId: event.responseId,
|
|
208
|
+
});
|
|
209
|
+
appendRewriteDiagnostic(partial, event.rewrite);
|
|
210
|
+
return { type: "error", reason: event.reason, error: partial };
|
|
211
|
+
case "start":
|
|
212
|
+
break;
|
|
213
|
+
case "text_start":
|
|
214
|
+
partial.content[event.contentIndex] = { type: "text", text: "" };
|
|
215
|
+
break;
|
|
216
|
+
case "text_delta":
|
|
217
|
+
(partial.content[event.contentIndex] as { text: string }).text += event.delta;
|
|
218
|
+
break;
|
|
219
|
+
case "text_end":
|
|
220
|
+
Object.assign(partial.content[event.contentIndex]!, {
|
|
221
|
+
text: event.content,
|
|
222
|
+
textSignature: event.contentSignature,
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
case "thinking_start":
|
|
226
|
+
partial.content[event.contentIndex] = { type: "thinking", thinking: "" };
|
|
227
|
+
break;
|
|
228
|
+
case "thinking_delta":
|
|
229
|
+
(partial.content[event.contentIndex] as { thinking: string }).thinking += event.delta;
|
|
230
|
+
break;
|
|
231
|
+
case "thinking_end":
|
|
232
|
+
Object.assign(partial.content[event.contentIndex]!, {
|
|
233
|
+
thinking: event.content,
|
|
234
|
+
thinkingSignature: event.contentSignature,
|
|
235
|
+
redacted: event.redacted,
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
case "toolcall_start":
|
|
239
|
+
partial.content[event.contentIndex] = {
|
|
240
|
+
type: "toolCall",
|
|
241
|
+
id: event.id,
|
|
242
|
+
name: event.toolName,
|
|
243
|
+
arguments: {},
|
|
244
|
+
};
|
|
245
|
+
toolJson.set(event.contentIndex, "");
|
|
246
|
+
break;
|
|
247
|
+
case "toolcall_delta": {
|
|
248
|
+
const json = `${toolJson.get(event.contentIndex) ?? ""}${event.delta}`;
|
|
249
|
+
toolJson.set(event.contentIndex, json);
|
|
250
|
+
(partial.content[event.contentIndex] as ToolCall).arguments =
|
|
251
|
+
parseStreamingJson<ToolCall["arguments"]>(json);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case "toolcall_end":
|
|
255
|
+
Object.assign(partial.content[event.contentIndex]!, event.toolCall);
|
|
256
|
+
toolJson.delete(event.contentIndex);
|
|
257
|
+
return {
|
|
258
|
+
type: "toolcall_end",
|
|
259
|
+
contentIndex: event.contentIndex,
|
|
260
|
+
toolCall: partial.content[event.contentIndex] as ToolCall,
|
|
261
|
+
partial,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return { ...event, partial } as AssistantMessageEvent;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function* readPiMessagesEvents(stream: ReadableStream<Uint8Array>): AsyncGenerator<PiMessagesEvent> {
|
|
270
|
+
const decoder = new TextDecoder();
|
|
271
|
+
const reader = stream.getReader();
|
|
272
|
+
let buffer = "";
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
while (true) {
|
|
276
|
+
const { done, value } = await reader.read();
|
|
277
|
+
buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
|
|
278
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
279
|
+
|
|
280
|
+
let split = buffer.indexOf("\n\n");
|
|
281
|
+
while (split !== -1) {
|
|
282
|
+
const event = parsePiMessagesEvent(buffer.slice(0, split));
|
|
283
|
+
if (event) {
|
|
284
|
+
yield event;
|
|
285
|
+
}
|
|
286
|
+
buffer = buffer.slice(split + 2);
|
|
287
|
+
split = buffer.indexOf("\n\n");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (done) {
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (buffer.trim()) {
|
|
296
|
+
const event = parsePiMessagesEvent(buffer);
|
|
297
|
+
if (event) {
|
|
298
|
+
yield event;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} finally {
|
|
302
|
+
reader.releaseLock();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function parsePiMessagesEvent(raw: string): PiMessagesEvent | undefined {
|
|
307
|
+
const data = raw
|
|
308
|
+
.split("\n")
|
|
309
|
+
.find((line) => line.startsWith("data:"))
|
|
310
|
+
?.slice(5)
|
|
311
|
+
.trim();
|
|
312
|
+
|
|
313
|
+
return data && data !== "[DONE]" ? (JSON.parse(data) as PiMessagesEvent) : undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function createErrorEvent(model: Model<"pi-messages">, error: unknown, aborted: boolean): AssistantMessageEvent {
|
|
317
|
+
const reason = aborted ? "aborted" : "error";
|
|
318
|
+
const assistantMessage: AssistantMessage = {
|
|
319
|
+
role: "assistant",
|
|
320
|
+
content: [],
|
|
321
|
+
api: model.api,
|
|
322
|
+
provider: model.provider,
|
|
323
|
+
model: model.id,
|
|
324
|
+
usage: createEmptyUsage(),
|
|
325
|
+
stopReason: reason,
|
|
326
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
327
|
+
timestamp: Date.now(),
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
if (!aborted && error instanceof PiMessagesResponseError) {
|
|
331
|
+
appendAssistantMessageDiagnostic(
|
|
332
|
+
assistantMessage,
|
|
333
|
+
createAssistantMessageDiagnostic("pi_messages_response_failure", error, error.diagnosticDetails),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return { type: "error", reason, error: assistantMessage };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function resolveCacheRetention(cacheRetention?: CacheRetention, env?: ProviderEnv): CacheRetention | undefined {
|
|
341
|
+
if (cacheRetention) {
|
|
342
|
+
return cacheRetention;
|
|
343
|
+
}
|
|
344
|
+
// Backend defaults apply when unset; only the legacy env opt-in is mapped.
|
|
345
|
+
return getProviderEnvValue("PI_CACHE_RETENTION", env) === "long" ? "long" : undefined;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export const stream: StreamFunction<"pi-messages", PiMessagesOptions> = (
|
|
349
|
+
model: Model<"pi-messages">,
|
|
350
|
+
context: Context,
|
|
351
|
+
options?: PiMessagesOptions,
|
|
352
|
+
): AssistantMessageEventStream => {
|
|
353
|
+
const eventStream = new AssistantMessageEventStream();
|
|
354
|
+
const convertEvent = createEventConverter(model);
|
|
355
|
+
|
|
356
|
+
void (async () => {
|
|
357
|
+
try {
|
|
358
|
+
const apiKey = options?.apiKey;
|
|
359
|
+
if (!apiKey) {
|
|
360
|
+
throw new Error(`No API key provided for provider "${model.provider}"`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const url = new URL(`${model.baseUrl.replace(/\/+$/u, "")}/messages`);
|
|
364
|
+
if (options?.debug) {
|
|
365
|
+
url.searchParams.set("debug", "1");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let payload: unknown = {
|
|
369
|
+
model: model.id,
|
|
370
|
+
context,
|
|
371
|
+
options: {
|
|
372
|
+
temperature: options?.temperature,
|
|
373
|
+
maxTokens: options?.maxTokens,
|
|
374
|
+
reasoning: options?.reasoning,
|
|
375
|
+
cacheRetention: resolveCacheRetention(options?.cacheRetention, options?.env),
|
|
376
|
+
sessionId: options?.sessionId,
|
|
377
|
+
toolChoice: options?.toolChoice,
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
const nextPayload = await options?.onPayload?.(payload, model);
|
|
381
|
+
if (nextPayload !== undefined) {
|
|
382
|
+
payload = nextPayload;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const response = await fetch(url, {
|
|
386
|
+
method: "POST",
|
|
387
|
+
headers: {
|
|
388
|
+
authorization: `Bearer ${apiKey}`,
|
|
389
|
+
accept: "text/event-stream",
|
|
390
|
+
"content-type": "application/json",
|
|
391
|
+
...providerHeadersToRecord(options?.headers),
|
|
392
|
+
},
|
|
393
|
+
body: JSON.stringify(payload),
|
|
394
|
+
signal: options?.signal,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
398
|
+
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
const body = await response.text();
|
|
401
|
+
throw createPiMessagesResponseError(model, url, response, body);
|
|
402
|
+
}
|
|
403
|
+
if (!response.body) {
|
|
404
|
+
throw new Error(`${model.provider} response has no body`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
for await (const piEvent of readPiMessagesEvents(response.body)) {
|
|
408
|
+
const event = convertEvent(piEvent);
|
|
409
|
+
eventStream.push(event);
|
|
410
|
+
if (event.type === "done" || event.type === "error") {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
throw new Error(`${model.provider} stream ended without a terminal event`);
|
|
416
|
+
} catch (error) {
|
|
417
|
+
eventStream.push(createErrorEvent(model, error, options?.signal?.aborted ?? false));
|
|
418
|
+
}
|
|
419
|
+
})();
|
|
420
|
+
|
|
421
|
+
return eventStream;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
export const streamSimple: StreamFunction<"pi-messages", SimpleStreamOptions> = (
|
|
425
|
+
model: Model<"pi-messages">,
|
|
426
|
+
context: Context,
|
|
427
|
+
options?: SimpleStreamOptions,
|
|
428
|
+
): AssistantMessageEventStream => {
|
|
429
|
+
const extra = options as PiMessagesOptions | undefined;
|
|
430
|
+
return stream(model, context, {
|
|
431
|
+
...options,
|
|
432
|
+
reasoning: options?.reasoning,
|
|
433
|
+
toolChoice: extra?.toolChoice,
|
|
434
|
+
debug: extra?.debug,
|
|
435
|
+
});
|
|
436
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AuthContext } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
interface NodeFsModule {
|
|
4
|
+
access(path: string): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface NodeOsModule {
|
|
8
|
+
homedir(): string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Variable specifier so browser bundlers do not try to resolve node builtins.
|
|
12
|
+
const importNodeModule = (specifier: string): Promise<unknown> => import(specifier);
|
|
13
|
+
|
|
14
|
+
function getProcessEnv(): Record<string, string | undefined> | undefined {
|
|
15
|
+
const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;
|
|
16
|
+
return proc?.env;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Default auth context: env vars from `process.env` (undefined in browsers),
|
|
21
|
+
* file existence via node:fs (always false in browsers).
|
|
22
|
+
*/
|
|
23
|
+
export function defaultProviderAuthContext(): AuthContext {
|
|
24
|
+
return {
|
|
25
|
+
async env(name: string): Promise<string | undefined> {
|
|
26
|
+
const value = getProcessEnv()?.[name];
|
|
27
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async fileExists(path: string): Promise<boolean> {
|
|
31
|
+
try {
|
|
32
|
+
const fs = (await importNodeModule("node:fs/promises")) as NodeFsModule;
|
|
33
|
+
let resolved = path;
|
|
34
|
+
if (resolved.startsWith("~")) {
|
|
35
|
+
const os = (await importNodeModule("node:os")) as NodeOsModule;
|
|
36
|
+
resolved = os.homedir() + resolved.slice(1);
|
|
37
|
+
}
|
|
38
|
+
await fs.access(resolved);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Credential, CredentialStore } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default in-memory credential store. Apps inject persistent stores.
|
|
5
|
+
* Keyed by `Provider.id`, one credential per provider; see `CredentialStore`.
|
|
6
|
+
* Writes are serialized per provider through a promise chain.
|
|
7
|
+
*/
|
|
8
|
+
export class InMemoryCredentialStore implements CredentialStore {
|
|
9
|
+
private credentials = new Map<string, Credential>();
|
|
10
|
+
private chains = new Map<string, Promise<unknown>>();
|
|
11
|
+
|
|
12
|
+
/** Serialize tasks per provider id. */
|
|
13
|
+
private enqueue<T>(providerId: string, task: () => Promise<T>): Promise<T> {
|
|
14
|
+
const previous = this.chains.get(providerId) ?? Promise.resolve();
|
|
15
|
+
const next = (async () => {
|
|
16
|
+
await previous.catch(() => {});
|
|
17
|
+
return task();
|
|
18
|
+
})();
|
|
19
|
+
this.chains.set(
|
|
20
|
+
providerId,
|
|
21
|
+
next.catch(() => {}),
|
|
22
|
+
);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async read(providerId: string): Promise<Credential | undefined> {
|
|
27
|
+
return this.credentials.get(providerId);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
modify(
|
|
31
|
+
providerId: string,
|
|
32
|
+
fn: (current: Credential | undefined) => Promise<Credential | undefined>,
|
|
33
|
+
): Promise<Credential | undefined> {
|
|
34
|
+
return this.enqueue(providerId, async () => {
|
|
35
|
+
const current = this.credentials.get(providerId);
|
|
36
|
+
const next = await fn(current);
|
|
37
|
+
if (next !== undefined) this.credentials.set(providerId, next);
|
|
38
|
+
return next ?? current;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
delete(providerId: string): Promise<void> {
|
|
43
|
+
return this.enqueue(providerId, async () => {
|
|
44
|
+
this.credentials.delete(providerId);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ApiKeyAuth, OAuthAuth } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Standard api-key auth: a stored credential key wins, otherwise the first
|
|
5
|
+
* set env var resolves. Includes a `login` that prompts for the key.
|
|
6
|
+
* Providers with non-standard resolution (provider env, ambient files, IAM)
|
|
7
|
+
* write their own `ApiKeyAuth`.
|
|
8
|
+
*/
|
|
9
|
+
export function envApiKeyAuth(name: string, envVars: readonly string[]): ApiKeyAuth {
|
|
10
|
+
return {
|
|
11
|
+
name,
|
|
12
|
+
login: async (callbacks) => {
|
|
13
|
+
const key = await callbacks.prompt({ type: "secret", message: `Enter ${name}` });
|
|
14
|
+
return { type: "api_key", key };
|
|
15
|
+
},
|
|
16
|
+
resolve: async ({ ctx, credential }) => {
|
|
17
|
+
if (credential?.key) return { auth: { apiKey: credential.key }, source: "stored credential" };
|
|
18
|
+
for (const envVar of envVars) {
|
|
19
|
+
const value = await ctx.env(envVar);
|
|
20
|
+
if (value) return { auth: { apiKey: value }, source: envVar };
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Wraps a dynamically imported `OAuthAuth` so provider definitions can
|
|
29
|
+
* advertise OAuth without importing the implementation. The flow loads on
|
|
30
|
+
* first `login`/`refresh`/`toAuth` call; callers keep Node-only flow code out
|
|
31
|
+
* of bundles by loading through a bundler-opaque dynamic import (variable
|
|
32
|
+
* specifier, see the bedrock lazy wrapper).
|
|
33
|
+
*/
|
|
34
|
+
export function lazyOAuth(input: { name: string; load: () => Promise<OAuthAuth> }): OAuthAuth {
|
|
35
|
+
let promise: Promise<OAuthAuth> | undefined;
|
|
36
|
+
const loaded = () => {
|
|
37
|
+
promise ??= input.load();
|
|
38
|
+
return promise;
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
name: input.name,
|
|
42
|
+
login: async (callbacks) => (await loaded()).login(callbacks),
|
|
43
|
+
refresh: async (credential) => (await loaded()).refresh(credential),
|
|
44
|
+
toAuth: async (credential) => (await loaded()).toAuth(credential),
|
|
45
|
+
};
|
|
46
|
+
}
|