@fastagent-sh/fastagent 0.21.1 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -12
- package/dist/agent.d.ts +24 -37
- package/dist/agent.js +7 -15
- package/dist/atomic-write.d.ts +13 -17
- package/dist/atomic-write.js +18 -29
- package/dist/bind.d.ts +12 -22
- package/dist/bind.js +18 -27
- package/dist/channel.d.ts +11 -33
- package/dist/channels/agentcore-limits.d.ts +2 -13
- package/dist/channels/agentcore-limits.js +2 -13
- package/dist/channels/agentcore-protocol.d.ts +67 -58
- package/dist/channels/agentcore-protocol.js +23 -15
- package/dist/channels/agentcore-service.d.ts +19 -33
- package/dist/channels/agentcore-service.js +176 -59
- package/dist/channels/agentcore.d.ts +39 -33
- package/dist/channels/agentcore.js +90 -224
- package/dist/channels/body.d.ts +15 -2
- package/dist/channels/body.js +24 -2
- package/dist/channels/busy.d.ts +1 -21
- package/dist/channels/busy.js +2 -39
- package/dist/channels/control.d.ts +17 -62
- package/dist/channels/control.js +74 -182
- package/dist/channels/define-channel.d.ts +39 -0
- package/dist/channels/define-channel.js +9 -0
- package/dist/channels/discover.d.ts +10 -31
- package/dist/channels/discover.js +37 -31
- package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
- package/dist/channels/feishu/bootstrap-token.js +7 -23
- package/dist/channels/feishu/card.d.ts +14 -28
- package/dist/channels/feishu/card.js +16 -30
- package/dist/channels/feishu/cloud.d.ts +1 -5
- package/dist/channels/feishu/context-buffer.d.ts +4 -23
- package/dist/channels/feishu/context-buffer.js +8 -31
- package/dist/channels/feishu/crypto.d.ts +10 -5
- package/dist/channels/feishu/crypto.js +12 -19
- package/dist/channels/feishu/feishu-api.d.ts +2 -1
- package/dist/channels/feishu/feishu-api.js +5 -4
- package/dist/channels/feishu/feishu.d.ts +4 -10
- package/dist/channels/feishu/feishu.js +113 -227
- package/dist/channels/feishu/invoke-turn.d.ts +5 -4
- package/dist/channels/feishu/invoke-turn.js +36 -54
- package/dist/channels/feishu/model.d.ts +4 -21
- package/dist/channels/feishu/model.js +1 -8
- package/dist/channels/feishu/normalize.d.ts +2 -10
- package/dist/channels/feishu/normalize.js +7 -42
- package/dist/channels/feishu/parse.d.ts +9 -29
- package/dist/channels/feishu/parse.js +8 -26
- package/dist/channels/feishu/preview.d.ts +12 -14
- package/dist/channels/feishu/preview.js +108 -207
- package/dist/channels/feishu/register-app.d.ts +8 -23
- package/dist/channels/feishu/register-app.js +7 -20
- package/dist/channels/feishu/register-webhook.d.ts +2 -9
- package/dist/channels/feishu/register-webhook.js +9 -33
- package/dist/channels/feishu/scaffold/channel.ts +27 -19
- package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/feishu/setup-mode.d.ts +11 -36
- package/dist/channels/feishu/setup-mode.js +7 -26
- package/dist/channels/feishu/ws-ingress.d.ts +1 -2
- package/dist/channels/feishu/ws-ingress.js +6 -8
- package/dist/channels/http.d.ts +15 -15
- package/dist/channels/http.js +19 -9
- package/dist/channels/kit/attachment-path.d.ts +4 -5
- package/dist/channels/kit/attachment-path.js +10 -34
- package/dist/channels/kit/context-buffer.d.ts +14 -21
- package/dist/channels/kit/context-buffer.js +25 -38
- package/dist/channels/kit/delivery.d.ts +35 -0
- package/dist/channels/kit/delivery.js +129 -0
- package/dist/channels/kit/event-stream.d.ts +5 -0
- package/dist/channels/kit/event-stream.js +29 -0
- package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
- package/dist/channels/kit/invoke-turn-kit.js +89 -32
- package/dist/channels/kit/preview-kit.d.ts +18 -72
- package/dist/channels/kit/preview-kit.js +22 -88
- package/dist/channels/kit/signature.d.ts +3 -9
- package/dist/channels/kit/signature.js +3 -9
- package/dist/channels/kit/state.d.ts +1 -2
- package/dist/channels/kit/state.js +5 -14
- package/dist/channels/kit/stop-command.d.ts +1 -2
- package/dist/channels/kit/stop-command.js +6 -9
- package/dist/channels/kit/tasks.d.ts +1 -1
- package/dist/channels/kit/tasks.js +8 -21
- package/dist/channels/kit/text.d.ts +2 -16
- package/dist/channels/kit/text.js +23 -31
- package/dist/channels/kit/thread-participants.d.ts +4 -17
- package/dist/channels/kit/thread-participants.js +8 -55
- package/dist/channels/kit/transport.d.ts +26 -0
- package/dist/channels/kit/transport.js +20 -0
- package/dist/channels/kit/turn-queue.d.ts +7 -11
- package/dist/channels/kit/turn-queue.js +21 -39
- package/dist/channels/kit/turn-runner.d.ts +32 -18
- package/dist/channels/kit/turn-runner.js +98 -53
- package/dist/channels/kit/turn-store.d.ts +32 -32
- package/dist/channels/kit/turn-store.js +45 -88
- package/dist/channels/lark/lark.d.ts +1 -5
- package/dist/channels/lark/onboard.d.ts +3 -12
- package/dist/channels/lark/onboard.js +3 -13
- package/dist/channels/lark/scaffold/channel.ts +27 -19
- package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/registration.d.ts +9 -35
- package/dist/channels/registration.js +6 -37
- package/dist/channels/secret.js +2 -5
- package/dist/channels/serve.d.ts +47 -44
- package/dist/channels/serve.js +165 -101
- package/dist/channels/slack/config-api.d.ts +4 -27
- package/dist/channels/slack/config-api.js +5 -34
- package/dist/channels/slack/context-buffer.d.ts +2 -3
- package/dist/channels/slack/context-buffer.js +5 -5
- package/dist/channels/slack/invoke-turn.d.ts +3 -1
- package/dist/channels/slack/invoke-turn.js +25 -34
- package/dist/channels/slack/manifest.js +1 -3
- package/dist/channels/slack/model.d.ts +1 -2
- package/dist/channels/slack/model.js +1 -1
- package/dist/channels/slack/onboard.d.ts +4 -3
- package/dist/channels/slack/onboard.js +8 -11
- package/dist/channels/slack/onboarding-state.d.ts +3 -5
- package/dist/channels/slack/onboarding-state.js +9 -6
- package/dist/channels/slack/parse.d.ts +4 -13
- package/dist/channels/slack/parse.js +7 -28
- package/dist/channels/slack/preview.d.ts +12 -2
- package/dist/channels/slack/preview.js +292 -269
- package/dist/channels/slack/reaction.d.ts +11 -3
- package/dist/channels/slack/reaction.js +19 -8
- package/dist/channels/slack/register-webhook.js +4 -7
- package/dist/channels/slack/scaffold/channel.ts +20 -13
- package/dist/channels/slack/scaffold/slack-send.ts +2 -2
- package/dist/channels/slack/setup-server.d.ts +1 -5
- package/dist/channels/slack/setup-server.js +1 -5
- package/dist/channels/slack/shared-api.d.ts +1 -4
- package/dist/channels/slack/shared-api.js +2 -13
- package/dist/channels/slack/slack-api.d.ts +2 -1
- package/dist/channels/slack/slack-api.js +6 -4
- package/dist/channels/slack/slack.d.ts +11 -14
- package/dist/channels/slack/slack.js +121 -122
- package/dist/channels/sse.d.ts +1 -1
- package/dist/channels/sse.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +7 -22
- package/dist/channels/telegram/context-buffer.js +6 -23
- package/dist/channels/telegram/invoke-turn.d.ts +7 -7
- package/dist/channels/telegram/invoke-turn.js +24 -43
- package/dist/channels/telegram/parse.d.ts +24 -51
- package/dist/channels/telegram/parse.js +32 -84
- package/dist/channels/telegram/preview.d.ts +10 -1
- package/dist/channels/telegram/preview.js +79 -100
- package/dist/channels/telegram/register-webhook.js +5 -23
- package/dist/channels/telegram/scaffold/channel.ts +25 -18
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
- package/dist/channels/telegram/telegram-api.d.ts +4 -3
- package/dist/channels/telegram/telegram-api.js +9 -7
- package/dist/channels/telegram/telegram.d.ts +4 -18
- package/dist/channels/telegram/telegram.js +38 -94
- package/dist/channels/telegram/turn-store.d.ts +11 -14
- package/dist/channels/telegram/turn-store.js +8 -6
- package/dist/channels/wait-health.d.ts +4 -6
- package/dist/channels/wait-health.js +10 -14
- package/dist/cli/add-feishu.d.ts +2 -12
- package/dist/cli/add-feishu.js +24 -71
- package/dist/cli/add-slack.d.ts +5 -3
- package/dist/cli/add-slack.js +53 -76
- package/dist/cli/auth-view.d.ts +2 -16
- package/dist/cli/auth-view.js +2 -16
- package/dist/cli/commands/add.js +25 -40
- package/dist/cli/commands/chat.d.ts +0 -1
- package/dist/cli/commands/chat.js +5 -6
- package/dist/cli/commands/deploy/agentcore.js +60 -65
- package/dist/cli/commands/deploy/docker.js +50 -23
- package/dist/cli/commands/deploy/fly.js +23 -61
- package/dist/cli/commands/deploy/railway.js +23 -18
- package/dist/cli/commands/deploy/shared.d.ts +38 -57
- package/dist/cli/commands/deploy/shared.js +40 -59
- package/dist/cli/commands/deploy.d.ts +10 -27
- package/dist/cli/commands/deploy.js +38 -60
- package/dist/cli/commands/destroy.d.ts +4 -0
- package/dist/cli/commands/destroy.js +56 -0
- package/dist/cli/commands/dev.d.ts +2 -1
- package/dist/cli/commands/dev.js +20 -23
- package/dist/cli/commands/info.d.ts +0 -2
- package/dist/cli/commands/info.js +83 -49
- package/dist/cli/commands/init.d.ts +1 -2
- package/dist/cli/commands/init.js +16 -55
- package/dist/cli/commands/invoke.d.ts +0 -1
- package/dist/cli/commands/invoke.js +5 -7
- package/dist/cli/commands/login.d.ts +2 -1
- package/dist/cli/commands/login.js +33 -38
- package/dist/cli/commands/logs.js +3 -4
- package/dist/cli/commands/models.js +1 -5
- package/dist/cli/commands/routine-run.d.ts +6 -0
- package/dist/cli/commands/routine-run.js +47 -0
- package/dist/cli/commands/routine.d.ts +25 -0
- package/dist/cli/commands/routine.js +122 -0
- package/dist/cli/commands/start.d.ts +31 -3
- package/dist/cli/commands/start.js +131 -81
- package/dist/cli/commands/tool.js +26 -12
- package/dist/cli/fail.d.ts +13 -23
- package/dist/cli/fail.js +20 -23
- package/dist/cli/invoke-stream.d.ts +1 -8
- package/dist/cli/kernel.d.ts +6 -38
- package/dist/cli/kernel.js +14 -77
- package/dist/cli/models-view.d.ts +1 -9
- package/dist/cli/models-view.js +11 -22
- package/dist/cli/program.d.ts +5 -6
- package/dist/cli/program.js +120 -157
- package/dist/cli/serve.d.ts +57 -41
- package/dist/cli/serve.js +94 -121
- package/dist/cli/shared.d.ts +15 -61
- package/dist/cli/shared.js +55 -181
- package/dist/cli.js +1 -8
- package/dist/collect.d.ts +6 -0
- package/dist/collect.js +6 -0
- package/dist/core.d.ts +4 -1
- package/dist/core.js +9 -7
- package/dist/declared-secrets.d.ts +55 -0
- package/dist/declared-secrets.js +88 -0
- package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
- package/dist/deploy/agentcore/aws-cli.js +64 -0
- package/dist/deploy/agentcore/destroy.d.ts +51 -0
- package/dist/deploy/agentcore/destroy.js +273 -0
- package/dist/deploy/agentcore/forwarder.js +28 -79
- package/dist/deploy/agentcore/logs.d.ts +2 -18
- package/dist/deploy/agentcore/logs.js +27 -51
- package/dist/deploy/agentcore/plan.d.ts +85 -105
- package/dist/deploy/agentcore/plan.js +152 -314
- package/dist/deploy/agentcore/run.d.ts +28 -28
- package/dist/deploy/agentcore/run.js +227 -306
- package/dist/deploy/agentcore/zip.d.ts +2 -15
- package/dist/deploy/agentcore/zip.js +3 -17
- package/dist/deploy/build-context.d.ts +21 -0
- package/dist/deploy/build-context.js +137 -0
- package/dist/deploy/channel-ingress.d.ts +26 -39
- package/dist/deploy/channel-ingress.js +57 -28
- package/dist/deploy/container.d.ts +20 -34
- package/dist/deploy/container.js +49 -100
- package/dist/deploy/docker/plan.d.ts +12 -11
- package/dist/deploy/docker/plan.js +43 -24
- package/dist/deploy/docker/run.d.ts +22 -25
- package/dist/deploy/docker/run.js +39 -37
- package/dist/deploy/fly/plan.d.ts +15 -44
- package/dist/deploy/fly/plan.js +41 -73
- package/dist/deploy/fly/run.d.ts +16 -51
- package/dist/deploy/fly/run.js +42 -95
- package/dist/deploy/hosts.d.ts +24 -3
- package/dist/deploy/hosts.js +24 -3
- package/dist/deploy/preflight.d.ts +47 -41
- package/dist/deploy/preflight.js +282 -383
- package/dist/deploy/railway/plan.d.ts +16 -42
- package/dist/deploy/railway/plan.js +42 -60
- package/dist/deploy/railway/run.d.ts +30 -68
- package/dist/deploy/railway/run.js +86 -100
- package/dist/deploy/registration-gate.d.ts +6 -13
- package/dist/deploy/residency.d.ts +37 -0
- package/dist/deploy/residency.js +24 -0
- package/dist/deploy/runner.d.ts +10 -9
- package/dist/deploy/runner.js +7 -18
- package/dist/deploy/secrets.d.ts +40 -41
- package/dist/deploy/secrets.js +117 -103
- package/dist/deploy/workspace.d.ts +50 -0
- package/dist/deploy/workspace.js +197 -0
- package/dist/dev-supervisor.d.ts +1 -9
- package/dist/dev-supervisor.js +21 -64
- package/dist/effect-port.d.ts +50 -0
- package/dist/effect-port.js +77 -0
- package/dist/engines/pi/agent-session-factory.d.ts +50 -69
- package/dist/engines/pi/agent-session-factory.js +249 -146
- package/dist/engines/pi/auth.d.ts +15 -11
- package/dist/engines/pi/auth.js +96 -99
- package/dist/engines/pi/chat.d.ts +1 -5
- package/dist/engines/pi/chat.js +2 -14
- package/dist/engines/pi/config.d.ts +53 -51
- package/dist/engines/pi/config.js +83 -92
- package/dist/engines/pi/create.d.ts +60 -75
- package/dist/engines/pi/create.js +108 -141
- package/dist/engines/pi/definition.d.ts +14 -58
- package/dist/engines/pi/definition.js +21 -85
- package/dist/engines/pi/invoke-session.d.ts +5 -25
- package/dist/engines/pi/invoke-session.js +233 -244
- package/dist/engines/pi/login.d.ts +95 -35
- package/dist/engines/pi/login.js +259 -107
- package/dist/engines/pi/machine.d.ts +31 -0
- package/dist/engines/pi/machine.js +106 -0
- package/dist/engines/pi/models.d.ts +70 -89
- package/dist/engines/pi/models.js +188 -81
- package/dist/engines/pi/open.d.ts +65 -53
- package/dist/engines/pi/open.js +129 -69
- package/dist/engines/pi/report.d.ts +3 -12
- package/dist/engines/pi/report.js +2 -16
- package/dist/engines/pi/search-tools.d.ts +1 -9
- package/dist/engines/pi/search-tools.js +26 -57
- package/dist/engines/pi/service.d.ts +2 -16
- package/dist/engines/pi/service.js +2 -16
- package/dist/engines/pi/session-builder.d.ts +5 -9
- package/dist/engines/pi/session-builder.js +44 -82
- package/dist/engines/pi/session-control.js +286 -269
- package/dist/engines/pi/session-effects.d.ts +14 -0
- package/dist/engines/pi/session-effects.js +30 -0
- package/dist/engines/pi/session-inheritance.d.ts +9 -18
- package/dist/engines/pi/session-inheritance.js +103 -160
- package/dist/engines/pi/session-markers.d.ts +31 -30
- package/dist/engines/pi/session-markers.js +26 -25
- package/dist/engines/pi/session-settings.d.ts +6 -22
- package/dist/engines/pi/session-settings.js +10 -21
- package/dist/engines/pi/session-store.d.ts +26 -92
- package/dist/engines/pi/session-store.js +96 -310
- package/dist/engines/pi/tool-context.d.ts +17 -45
- package/dist/engines/pi/tool-context.js +7 -26
- package/dist/engines/pi/tool.d.ts +45 -36
- package/dist/engines/pi/tool.js +43 -57
- package/dist/engines/pi/turn-kit.d.ts +14 -52
- package/dist/engines/pi/turn-kit.js +18 -67
- package/dist/engines/pi/wake-tool.d.ts +2 -10
- package/dist/engines/pi/wake-tool.js +7 -27
- package/dist/env.d.ts +27 -28
- package/dist/env.js +45 -50
- package/dist/index.js +0 -2
- package/dist/lark.d.ts +1 -2
- package/dist/lark.js +1 -2
- package/dist/loader.d.ts +14 -55
- package/dist/loader.js +24 -43
- package/dist/log.d.ts +2 -12
- package/dist/log.js +3 -24
- package/dist/node.d.ts +2 -15
- package/dist/node.js +2 -18
- package/dist/observe.d.ts +2 -8
- package/dist/open-url.d.ts +1 -1
- package/dist/open-url.js +1 -1
- package/dist/paths.d.ts +70 -134
- package/dist/paths.js +165 -232
- package/dist/pi.d.ts +4 -6
- package/dist/pi.js +3 -3
- package/dist/proxy.d.ts +16 -6
- package/dist/proxy.js +52 -16
- package/dist/runtime.d.ts +18 -12
- package/dist/runtime.js +18 -11
- package/dist/scaffold/add-channel.d.ts +14 -31
- package/dist/scaffold/add-channel.js +48 -112
- package/dist/scaffold/init.d.ts +9 -31
- package/dist/scaffold/init.js +45 -138
- package/dist/scaffold/templates/env.example +6 -4
- package/dist/scaffold/templates/fastagent.config.ts +36 -0
- package/dist/scaffold/templates.d.ts +3 -3
- package/dist/scaffold/templates.js +5 -8
- package/dist/scaffold/vendor-skill.d.ts +2 -5
- package/dist/scaffold/vendor-skill.js +19 -29
- package/dist/schedule/cron.d.ts +9 -5
- package/dist/schedule/cron.js +9 -11
- package/dist/schedule/discover.d.ts +12 -12
- package/dist/schedule/discover.js +43 -34
- package/dist/schedule/routine.d.ts +43 -0
- package/dist/schedule/routine.js +32 -0
- package/dist/schedule/run.d.ts +55 -0
- package/dist/schedule/run.js +146 -0
- package/dist/schedule/scheduler.d.ts +44 -55
- package/dist/schedule/scheduler.js +291 -265
- package/dist/schedule/state.d.ts +105 -10
- package/dist/schedule/state.js +293 -19
- package/dist/schedule/wake-alarm.d.ts +10 -23
- package/dist/schedule/wake-alarm.js +91 -121
- package/dist/schedule/wakeups.d.ts +23 -39
- package/dist/schedule/wakeups.js +33 -75
- package/dist/secrets-gate.d.ts +32 -0
- package/dist/secrets-gate.js +33 -0
- package/dist/service.d.ts +137 -98
- package/dist/service.js +250 -252
- package/dist/session-remote.d.ts +33 -44
- package/dist/session-remote.js +203 -192
- package/dist/session.d.ts +166 -229
- package/dist/session.js +33 -55
- package/dist/tunnel.d.ts +8 -36
- package/dist/tunnel.js +24 -65
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +10 -16
- package/dist/channels/agentcore-state.d.ts +0 -77
- package/dist/channels/agentcore-state.js +0 -276
- package/dist/channels/github/github.d.ts +0 -31
- package/dist/channels/github/github.js +0 -89
- package/dist/channels/github/scaffold/channel.ts +0 -25
- package/dist/cli/commands/attach.d.ts +0 -108
- package/dist/cli/commands/attach.js +0 -637
- package/dist/cli/commands/fire.d.ts +0 -7
- package/dist/cli/commands/fire.js +0 -38
- package/dist/cli/commands/schedule.d.ts +0 -12
- package/dist/cli/commands/schedule.js +0 -87
- package/dist/github.d.ts +0 -3
- package/dist/github.js +0 -2
- package/dist/scaffold/templates/fastagent.config.mjs +0 -23
- package/dist/schedule/audit.d.ts +0 -18
- package/dist/schedule/audit.js +0 -57
- package/dist/schedule/schedule.d.ts +0 -35
- package/dist/schedule/schedule.js +0 -23
|
@@ -1,28 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Signing in to a MODEL PROVIDER, for any front end: {@link login} runs one provider's flow over pi-ai's own
|
|
3
|
+
* `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
|
|
4
|
+
* (one writer, one lock/corruption semantics); {@link loginOptions} is the list a picker offers. `fastagent login` is
|
|
5
|
+
* one front end ({@link loginFlow}: terminal menus, then {@link login}); a GUI client is another.
|
|
6
|
+
*/
|
|
7
|
+
import { type AuthInteraction, type Provider } from "@earendil-works/pi-ai";
|
|
8
|
+
export type LoginMethod = "oauth" | "api_key";
|
|
9
|
+
/** The user backed out of a prompt/menu, or the flow was aborted — a decision, not a failure. */
|
|
10
|
+
export declare class LoginCancelled extends Error {
|
|
11
|
+
}
|
|
12
|
+
/** One provider sign-in a picker can offer. */
|
|
13
|
+
export interface LoginOption {
|
|
14
|
+
provider: string;
|
|
15
|
+
method: LoginMethod;
|
|
16
|
+
/** The method's own name, e.g. "Anthropic (Claude Pro/Max)" or "Anthropic API key". */
|
|
17
|
+
label: string;
|
|
18
|
+
/** An OAuth login backed by a provider subscription. */
|
|
19
|
+
subscription: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* What the file holds for this provider now. It keeps ONE credential per provider, so signing in with the other
|
|
22
|
+
* method replaces it — worth a warning in a client.
|
|
23
|
+
*/
|
|
24
|
+
stored?: LoginMethod;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Every interactive sign-in of pi's built-in providers, with what `authPath` already holds for each. A provider whose
|
|
28
|
+
* key can only come from its env var offers none and is left out.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loginOptions(authPath: string): Promise<LoginOption[]>;
|
|
31
|
+
/** {@link loginOptions} over pi's built-ins plus `providers` (a same id replaces one). Not public: a test seam. */
|
|
32
|
+
export declare function loginOptionsOver(authPath: string, providers?: readonly Provider[]): Promise<LoginOption[]>;
|
|
33
|
+
export interface LoginRequest {
|
|
34
|
+
provider: string;
|
|
35
|
+
method: LoginMethod;
|
|
36
|
+
/** The credentials file the sign-in is written to. */
|
|
37
|
+
authPath: string;
|
|
38
|
+
/** pi-ai's own interaction: prompts (`text`, `secret`, `select`, `manual_code`) and events (`auth_url`, …). */
|
|
39
|
+
interaction: AuthInteraction;
|
|
40
|
+
}
|
|
41
|
+
export interface LoginResult {
|
|
42
|
+
provider: string;
|
|
43
|
+
method: LoginMethod;
|
|
44
|
+
/** An API key checked with one minimal request: accepted, or not decidable (kept). OAuth needs no check. */
|
|
45
|
+
verified: "ok" | "unknown" | "n/a";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sign in to one of pi's built-in providers and persist the credential to `authPath`.
|
|
5
49
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
50
|
+
* The file is checked FIRST (a no-op write runs the refuse-corrupt and writability checks), so a flow never runs
|
|
51
|
+
* toward a credential that could not be saved. An entered API key is verified with one minimal request to pi's
|
|
52
|
+
* default model for the provider (its first model when pi names none), at pi's built-in endpoint, BEFORE it is
|
|
53
|
+
* written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
|
|
54
|
+
* the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
|
|
55
|
+
* again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
|
|
56
|
+
* A provider an agent's models.json points elsewhere is not this call's: store that key with `fastagentCredentialStore`.
|
|
57
|
+
*
|
|
58
|
+
* Aborting `interaction.signal` at any point, the verification included, rejects with {@link LoginCancelled} and
|
|
59
|
+
* writes nothing. Every prompt carries a signal that aborts on it, on the prompt's own signal (a callback server
|
|
60
|
+
* beating a `manual_code` prompt), and when the flow settles with a prompt still pending.
|
|
61
|
+
*/
|
|
62
|
+
export declare function login(request: LoginRequest): Promise<LoginResult>;
|
|
63
|
+
/** Not public: what the CLI and the tests add to {@link login}. */
|
|
64
|
+
export interface LoginInternals {
|
|
65
|
+
/** Providers added to pi's built-ins (a same id replaces one): a test seam. */
|
|
66
|
+
providers?: readonly Provider[];
|
|
67
|
+
/**
|
|
68
|
+
* The agent the key is for. Verification then runs on THAT agent's registry (its models.json and the machine's over
|
|
69
|
+
* the built-ins), so the check reaches the endpoint its turns will: a models.json can point a built-in provider at a
|
|
70
|
+
* gateway, where the provider's own endpoint would refuse the gateway's key and loop the person through re-entry.
|
|
71
|
+
*/
|
|
72
|
+
agentDir?: string;
|
|
73
|
+
/**
|
|
74
|
+
* A model spec to verify an entered key with, when the registry has it as one of this provider's models: the
|
|
75
|
+
* first-run picker's choice, the request the agent is about to make. Otherwise pi's default model for the provider
|
|
76
|
+
* is used, then its first. On one registry, any of a provider's models answers the 401 question.
|
|
77
|
+
*/
|
|
78
|
+
verifyWith?: string;
|
|
79
|
+
}
|
|
80
|
+
/** {@link login}, plus {@link LoginInternals}. */
|
|
81
|
+
export declare function loginOver(request: LoginRequest, internals?: LoginInternals): Promise<LoginResult>;
|
|
82
|
+
/**
|
|
83
|
+
* pi's default model per provider (`defaultModelPerProvider` in pi-coding-agent's model-resolver): the general-purpose
|
|
84
|
+
* model pi itself starts on, so the likeliest to answer a minimal request. COPIED, because pi does not export it;
|
|
85
|
+
* `test/login.test.ts` compares this against pi's own table, so a pi release that changes a default fails there.
|
|
10
86
|
*/
|
|
11
|
-
|
|
87
|
+
export declare const PI_DEFAULT_MODELS: Readonly<Record<string, string>>;
|
|
12
88
|
/** One picker option: a stable `value`, a human `label`, and an optional `hint` (e.g. configured status). */
|
|
13
89
|
export interface IoOption {
|
|
14
90
|
value: string;
|
|
15
91
|
label: string;
|
|
16
92
|
hint?: string;
|
|
17
93
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Terminal interaction, injectable for tests (no real stdin/stdout or browser). The CLI implements it
|
|
20
|
-
* with `@clack/prompts` (a searchable list for many options, a hidden prompt for keys).
|
|
21
|
-
*/
|
|
94
|
+
/** Terminal interaction, injectable for tests (no real stdin/stdout or browser). */
|
|
22
95
|
export interface LoginIO {
|
|
23
|
-
/** Single-choice picker.
|
|
96
|
+
/** Single-choice picker. */
|
|
24
97
|
select(message: string, options: IoOption[]): Promise<string | undefined>;
|
|
25
|
-
/** Free-text or hidden input.
|
|
98
|
+
/** Free-text or hidden input. */
|
|
26
99
|
prompt(message: string, opts?: {
|
|
27
100
|
hidden?: boolean;
|
|
28
101
|
signal?: AbortSignal;
|
|
@@ -32,31 +105,18 @@ export interface LoginIO {
|
|
|
32
105
|
/** Best-effort open a URL in the browser (printed regardless). */
|
|
33
106
|
openUrl(url: string): void;
|
|
34
107
|
}
|
|
35
|
-
export type LoginMethod = "oauth" | "api_key";
|
|
36
|
-
/** The user backed out of a prompt/menu — a decision, not a failure. Callers (the first-run picker,
|
|
37
|
-
* the login command) match on this to report neutrally instead of as a login "failure". */
|
|
38
|
-
export declare class LoginCancelled extends Error {
|
|
39
|
-
}
|
|
40
|
-
/** What `loginFlow` can offer a provider interactively: an OAuth flow, an API-key ENTRY prompt, or
|
|
41
|
-
* nothing ("none" — the key must come from the provider's env var). */
|
|
42
|
-
export type InteractiveLoginKind = LoginMethod | "none";
|
|
43
|
-
/** The provider's {@link InteractiveLoginKind}. OAuth wins when both exist (methodForProvider still
|
|
44
|
-
* asks at login time); the first-run picker annotates with this so the hint predicts what picking
|
|
45
|
-
* actually does — a browser login ("oauth"), a key prompt ("api_key"), or neither. */
|
|
46
|
-
export declare function interactiveLoginKind(p: Provider): InteractiveLoginKind;
|
|
47
|
-
export interface LoginResult {
|
|
48
|
-
provider: string;
|
|
49
|
-
method: LoginMethod;
|
|
50
|
-
}
|
|
51
108
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
109
|
+
* `fastagent login`: ask only what was not given (method, then provider, from {@link loginOptions} with each one's
|
|
110
|
+
* stored credential), then {@link login}.
|
|
54
111
|
*/
|
|
55
|
-
export declare function loginFlow(io: LoginIO, options
|
|
112
|
+
export declare function loginFlow(io: LoginIO, options: {
|
|
113
|
+
authPath: string;
|
|
56
114
|
provider?: string;
|
|
57
115
|
method?: LoginMethod;
|
|
58
|
-
authPath?: string;
|
|
59
|
-
store?: CredentialStore;
|
|
60
116
|
providers?: Provider[];
|
|
61
117
|
signal?: AbortSignal;
|
|
118
|
+
/** {@link LoginInternals.verifyWith}. */
|
|
119
|
+
verifyWith?: string;
|
|
120
|
+
/** {@link LoginInternals.agentDir}. */
|
|
121
|
+
agentDir?: string;
|
|
62
122
|
}): Promise<LoginResult>;
|
package/dist/engines/pi/login.js
CHANGED
|
@@ -1,16 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* Signing in to a MODEL PROVIDER, for any front end: {@link login} runs one provider's flow over pi-ai's own
|
|
3
|
+
* `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
|
|
4
|
+
* (one writer, one lock/corruption semantics); {@link loginOptions} is the list a picker offers. `fastagent login` is
|
|
5
|
+
* one front end ({@link loginFlow}: terminal menus, then {@link login}); a GUI client is another.
|
|
6
|
+
*/
|
|
7
|
+
import { InMemoryCredentialStore, } from "@earendil-works/pi-ai";
|
|
8
|
+
import { fastagentCredentialStore } from "./auth.js";
|
|
9
|
+
import { createPiModelRuntime, interactiveAuth, loginProviders, piModelsOver, probeApiKey } from "./models.js";
|
|
10
|
+
/** The user backed out of a prompt/menu, or the flow was aborted — a decision, not a failure. */
|
|
5
11
|
export class LoginCancelled extends Error {
|
|
6
12
|
}
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Every interactive sign-in of pi's built-in providers, with what `authPath` already holds for each. A provider whose
|
|
15
|
+
* key can only come from its env var offers none and is left out.
|
|
16
|
+
*/
|
|
17
|
+
export function loginOptions(authPath) {
|
|
18
|
+
return loginOptionsOver(authPath);
|
|
19
|
+
}
|
|
20
|
+
/** {@link loginOptions} over pi's built-ins plus `providers` (a same id replaces one). Not public: a test seam. */
|
|
21
|
+
export async function loginOptionsOver(authPath, providers) {
|
|
22
|
+
// ONE read of the file: `list` is metadata only, and reading per provider would repeat a corrupt file's warning for
|
|
23
|
+
// every provider.
|
|
24
|
+
const held = new Map((await fastagentCredentialStore(authPath).list()).map((info) => [info.providerId, info.type]));
|
|
25
|
+
const offered = [];
|
|
26
|
+
for (const provider of loginProviders(providers)) {
|
|
27
|
+
const methods = ["oauth", "api_key"].filter((method) => interactiveAuth(provider, method));
|
|
28
|
+
if (methods.length === 0)
|
|
29
|
+
continue;
|
|
30
|
+
const stored = held.get(provider.id);
|
|
31
|
+
for (const method of methods) {
|
|
32
|
+
offered.push({
|
|
33
|
+
provider: provider.id,
|
|
34
|
+
method,
|
|
35
|
+
label: interactiveAuth(provider, method)?.name ?? provider.name,
|
|
36
|
+
subscription: method === "oauth" && provider.auth.oauth?.isSubscription === true,
|
|
37
|
+
...(stored ? { stored } : {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return offered;
|
|
14
42
|
}
|
|
15
43
|
/** Combine present abort signals into one (no-op when none/one). */
|
|
16
44
|
function anySignal(...signals) {
|
|
@@ -18,37 +46,193 @@ function anySignal(...signals) {
|
|
|
18
46
|
return present.length === 0 ? undefined : present.length === 1 ? present[0] : AbortSignal.any(present);
|
|
19
47
|
}
|
|
20
48
|
/**
|
|
21
|
-
*
|
|
22
|
-
* the flow resolves, cancelling a prompt the provider left pending (a manual-code paste racing a
|
|
23
|
-
* callback server it just won) so the one-shot CLI exits instead of hanging on stdin.
|
|
49
|
+
* Sign in to one of pi's built-in providers and persist the credential to `authPath`.
|
|
24
50
|
*
|
|
25
|
-
* The
|
|
26
|
-
*
|
|
51
|
+
* The file is checked FIRST (a no-op write runs the refuse-corrupt and writability checks), so a flow never runs
|
|
52
|
+
* toward a credential that could not be saved. An entered API key is verified with one minimal request to pi's
|
|
53
|
+
* default model for the provider (its first model when pi names none), at pi's built-in endpoint, BEFORE it is
|
|
54
|
+
* written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
|
|
55
|
+
* the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
|
|
56
|
+
* again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
|
|
57
|
+
* A provider an agent's models.json points elsewhere is not this call's: store that key with `fastagentCredentialStore`.
|
|
58
|
+
*
|
|
59
|
+
* Aborting `interaction.signal` at any point, the verification included, rejects with {@link LoginCancelled} and
|
|
60
|
+
* writes nothing. Every prompt carries a signal that aborts on it, on the prompt's own signal (a callback server
|
|
61
|
+
* beating a `manual_code` prompt), and when the flow settles with a prompt still pending.
|
|
27
62
|
*/
|
|
28
|
-
function
|
|
63
|
+
export function login(request) {
|
|
64
|
+
return loginOver(request);
|
|
65
|
+
}
|
|
66
|
+
/** {@link login}, plus {@link LoginInternals}. */
|
|
67
|
+
export async function loginOver(request, internals = {}) {
|
|
68
|
+
const { providers, verifyWith, agentDir } = internals;
|
|
69
|
+
const { method, authPath, interaction } = request;
|
|
70
|
+
const provider = loginProviders(providers).find((p) => p.id === request.provider);
|
|
71
|
+
if (!provider)
|
|
72
|
+
throw new Error(`unknown provider "${request.provider}"`);
|
|
73
|
+
const auth = interactiveAuth(provider, method);
|
|
74
|
+
if (!auth?.login)
|
|
75
|
+
throw new Error(`provider "${provider.id}" has no interactive ${method} login`);
|
|
76
|
+
// The key under test lives here, not in the file, until it passes.
|
|
77
|
+
const trial = new InMemoryCredentialStore();
|
|
78
|
+
// Only a key needs the registry, and building an agent's fails on a broken models.json: an OAuth sign-in must not.
|
|
79
|
+
// For a key it is built BEFORE the flow, so that failure comes before the person types anything.
|
|
80
|
+
const models = method !== "api_key"
|
|
81
|
+
? undefined
|
|
82
|
+
: agentDir
|
|
83
|
+
? await createPiModelRuntime({
|
|
84
|
+
agentDir,
|
|
85
|
+
credentials: trial,
|
|
86
|
+
...(providers ? { providers: [...providers] } : {}),
|
|
87
|
+
})
|
|
88
|
+
: piModelsOver(trial, providers);
|
|
89
|
+
const store = fastagentCredentialStore(authPath);
|
|
90
|
+
await store.modify(provider.id, async () => undefined);
|
|
91
|
+
const signal = interaction.signal ?? new AbortController().signal;
|
|
92
|
+
const notify = (event) => interaction.notify(event);
|
|
93
|
+
for (;;) {
|
|
94
|
+
const credential = await runFlow(auth.login.bind(auth), interaction, signal);
|
|
95
|
+
let verified = "n/a";
|
|
96
|
+
if (models) {
|
|
97
|
+
await trial.modify(provider.id, async () => credential);
|
|
98
|
+
const verdict = await verifyApiKey(models, provider.id, verifyWith, notify, signal);
|
|
99
|
+
if (signal.aborted)
|
|
100
|
+
throw new LoginCancelled("cancelled");
|
|
101
|
+
if (verdict === "rejected")
|
|
102
|
+
continue;
|
|
103
|
+
verified = verdict;
|
|
104
|
+
}
|
|
105
|
+
await store.modify(provider.id, async () => credential);
|
|
106
|
+
return { provider: provider.id, method, verified };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function runFlow(flow, interaction, signal) {
|
|
110
|
+
if (signal.aborted)
|
|
111
|
+
throw new LoginCancelled("cancelled");
|
|
112
|
+
const done = new AbortController();
|
|
113
|
+
let credential;
|
|
114
|
+
try {
|
|
115
|
+
credential = await flow({
|
|
116
|
+
signal,
|
|
117
|
+
prompt: (prompt) => interaction.prompt({ ...prompt, signal: anySignal(prompt.signal, signal, done.signal) }),
|
|
118
|
+
notify: (event) => interaction.notify(event),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// Whatever the provider made of the abort (its own error, a rejected prompt), it was the caller's decision.
|
|
123
|
+
if (signal.aborted)
|
|
124
|
+
throw new LoginCancelled("cancelled");
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
done.abort();
|
|
129
|
+
}
|
|
130
|
+
// A provider that ignored the abort and finished anyway: the caller still cancelled.
|
|
131
|
+
if (signal.aborted)
|
|
132
|
+
throw new LoginCancelled("cancelled");
|
|
133
|
+
return credential;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* pi's default model per provider (`defaultModelPerProvider` in pi-coding-agent's model-resolver): the general-purpose
|
|
137
|
+
* model pi itself starts on, so the likeliest to answer a minimal request. COPIED, because pi does not export it;
|
|
138
|
+
* `test/login.test.ts` compares this against pi's own table, so a pi release that changes a default fails there.
|
|
139
|
+
*/
|
|
140
|
+
export const PI_DEFAULT_MODELS = {
|
|
141
|
+
"amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
|
|
142
|
+
"ant-ling": "Ring-2.6-1T",
|
|
143
|
+
anthropic: "claude-opus-4-8",
|
|
144
|
+
openai: "gpt-5.5",
|
|
145
|
+
"azure-openai-responses": "gpt-5.4",
|
|
146
|
+
"openai-codex": "gpt-5.5",
|
|
147
|
+
radius: "balanced",
|
|
148
|
+
nvidia: "nvidia/nemotron-3-super-120b-a12b",
|
|
149
|
+
deepseek: "deepseek-v4-pro",
|
|
150
|
+
google: "gemini-3.1-pro-preview",
|
|
151
|
+
"google-vertex": "gemini-3.1-pro-preview",
|
|
152
|
+
"github-copilot": "gpt-5.4",
|
|
153
|
+
openrouter: "moonshotai/kimi-k2.6",
|
|
154
|
+
"vercel-ai-gateway": "zai/glm-5.1",
|
|
155
|
+
xai: "grok-4.7",
|
|
156
|
+
groq: "openai/gpt-oss-120b",
|
|
157
|
+
cerebras: "gpt-oss-120b",
|
|
158
|
+
zai: "glm-5.3",
|
|
159
|
+
"zai-coding-cn": "glm-5.3",
|
|
160
|
+
mistral: "devstral-medium-latest",
|
|
161
|
+
minimax: "MiniMax-M2.7",
|
|
162
|
+
"minimax-cn": "MiniMax-M2.7",
|
|
163
|
+
moonshotai: "kimi-k2.6",
|
|
164
|
+
"moonshotai-cn": "kimi-k2.6",
|
|
165
|
+
huggingface: "moonshotai/Kimi-K2.6",
|
|
166
|
+
fireworks: "accounts/fireworks/models/kimi-k2p6",
|
|
167
|
+
together: "moonshotai/Kimi-K2.6",
|
|
168
|
+
baseten: "zai-org/GLM-5.2",
|
|
169
|
+
opencode: "kimi-k2.6",
|
|
170
|
+
"opencode-go": "kimi-k2.6",
|
|
171
|
+
"kimi-coding": "kimi-for-coding",
|
|
172
|
+
meta: "muse-spark-1.3",
|
|
173
|
+
"cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
|
|
174
|
+
"cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
|
|
175
|
+
"qwen-token-plan": "qwen3.7-max",
|
|
176
|
+
"qwen-token-plan-cn": "qwen3.7-max",
|
|
177
|
+
"qwen-token-plan-individual": "qwen3.8-max",
|
|
178
|
+
xiaomi: "mimo-v2.5-pro",
|
|
179
|
+
"xiaomi-token-plan-cn": "mimo-v2.5-pro",
|
|
180
|
+
"xiaomi-token-plan-ams": "mimo-v2.5-pro",
|
|
181
|
+
"xiaomi-token-plan-sgp": "mimo-v2.5-pro",
|
|
182
|
+
};
|
|
183
|
+
/** One minimal request with the key under test. */
|
|
184
|
+
async function verifyApiKey(models, providerId, verifyWith, notify, signal) {
|
|
185
|
+
const chosen = verifyWith?.startsWith(`${providerId}/`)
|
|
186
|
+
? models.getModel(providerId, verifyWith.slice(providerId.length + 1))
|
|
187
|
+
: undefined;
|
|
188
|
+
const piDefault = PI_DEFAULT_MODELS[providerId];
|
|
189
|
+
// The provider's first model is the last resort only: catalogs are alphabetical, and a first model can be a
|
|
190
|
+
// special-purpose one that rejects a minimal request (which still answers the 401 question).
|
|
191
|
+
const model = chosen ??
|
|
192
|
+
(piDefault ? models.getModel(providerId, piDefault) : undefined) ??
|
|
193
|
+
models.getProvider(providerId)?.getModels()[0];
|
|
194
|
+
if (!model) {
|
|
195
|
+
notify({
|
|
196
|
+
type: "info",
|
|
197
|
+
message: `cannot verify the key: provider "${providerId}" lists no models — kept as entered`,
|
|
198
|
+
});
|
|
199
|
+
return "unknown";
|
|
200
|
+
}
|
|
201
|
+
const label = `${model.provider}/${model.id}`;
|
|
202
|
+
notify({ type: "progress", message: `verifying the key with ${label}…` });
|
|
203
|
+
const probe = await probeApiKey(models, model, signal);
|
|
204
|
+
if (signal.aborted)
|
|
205
|
+
return "unknown"; // the caller reports the cancel, not a verdict on the key
|
|
206
|
+
if (probe.state === "ok") {
|
|
207
|
+
notify({ type: "info", message: `key verified — ${label} responded` });
|
|
208
|
+
}
|
|
209
|
+
else if (probe.state === "rejected") {
|
|
210
|
+
notify({
|
|
211
|
+
type: "info",
|
|
212
|
+
message: `${providerId} rejected the API key (HTTP 401): ${probe.message} — enter it again (or cancel)`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
notify({
|
|
217
|
+
type: "info",
|
|
218
|
+
message: `could not verify the key with ${label}: ${probe.message} — kept; invokes surface the provider's error`,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return probe.state;
|
|
222
|
+
}
|
|
223
|
+
/** {@link LoginIO} as pi-ai's `AuthInteraction`: what {@link login} drives for the terminal. */
|
|
224
|
+
function terminalInteraction(io, signal) {
|
|
29
225
|
return {
|
|
30
|
-
signal
|
|
226
|
+
...(signal ? { signal } : {}),
|
|
31
227
|
prompt: async (p) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// `auth.login()` RETURNS, which it cannot do while blocked on this await.
|
|
41
|
-
//
|
|
42
|
-
// That is a property of the providers, not a guarantee, and nothing here holds it. A
|
|
43
|
-
// provider that ever issues a `select` after starting its callback server shows up as the
|
|
44
|
-
// CLI parked on stdin — widen `LoginIO.select` with a signal then.
|
|
45
|
-
const v = await io.select(p.message, p.options.map((o) => ({ value: o.id, label: o.label, hint: o.description })));
|
|
46
|
-
if (v === undefined)
|
|
47
|
-
throw new LoginCancelled("cancelled");
|
|
48
|
-
return v;
|
|
49
|
-
}
|
|
50
|
-
const signal = anySignal(p.signal, userSignal, doneSignal);
|
|
51
|
-
const v = await io.prompt(p.message, { hidden: p.type === "secret", signal });
|
|
228
|
+
// A select is UNCANCELLABLE here: `LoginIO.select` takes no signal.
|
|
229
|
+
const v = p.type === "select"
|
|
230
|
+
? await io.select(p.message, p.options.map((o) => ({
|
|
231
|
+
value: o.id,
|
|
232
|
+
label: o.label,
|
|
233
|
+
...(o.description ? { hint: o.description } : {}),
|
|
234
|
+
})))
|
|
235
|
+
: await io.prompt(p.message, { hidden: p.type === "secret", ...(p.signal ? { signal: p.signal } : {}) });
|
|
52
236
|
if (v === undefined)
|
|
53
237
|
throw new LoginCancelled("cancelled");
|
|
54
238
|
return v;
|
|
@@ -70,10 +254,6 @@ function authCallbacks(io, userSignal, doneSignal) {
|
|
|
70
254
|
},
|
|
71
255
|
};
|
|
72
256
|
}
|
|
73
|
-
/** Providers offering the given method as an INTERACTIVE login (so `login` can actually run it). */
|
|
74
|
-
function candidatesFor(providers, method) {
|
|
75
|
-
return providers.filter((p) => (method === "oauth" ? p.auth.oauth : p.auth.apiKey?.login));
|
|
76
|
-
}
|
|
77
257
|
async function selectMethod(io) {
|
|
78
258
|
const v = await io.select("Authentication method", [
|
|
79
259
|
{ value: "oauth", label: "Use a subscription (OAuth)" },
|
|
@@ -83,74 +263,46 @@ async function selectMethod(io) {
|
|
|
83
263
|
throw new LoginCancelled("no authentication method selected");
|
|
84
264
|
return v;
|
|
85
265
|
}
|
|
86
|
-
/** Given a provider arg, pick the method it supports (asking only when it offers both). */
|
|
87
|
-
async function methodForProvider(io, p) {
|
|
88
|
-
const hasOauth = !!p.auth.oauth;
|
|
89
|
-
const hasKeyLogin = !!p.auth.apiKey?.login;
|
|
90
|
-
if (hasOauth && hasKeyLogin)
|
|
91
|
-
return selectMethod(io);
|
|
92
|
-
if (hasOauth)
|
|
93
|
-
return "oauth";
|
|
94
|
-
if (hasKeyLogin)
|
|
95
|
-
return "api_key";
|
|
96
|
-
throw new Error(`provider "${p.id}" has no interactive login — set its API key via the provider's env var`);
|
|
97
|
-
}
|
|
98
|
-
/** List providers for the method with their configured status, and let the user pick one. */
|
|
99
|
-
async function selectProvider(io, providers, method, store) {
|
|
100
|
-
const candidates = candidatesFor(providers, method);
|
|
101
|
-
if (candidates.length === 0)
|
|
102
|
-
throw new Error(`no provider supports ${method} login`);
|
|
103
|
-
const options = await Promise.all(candidates.map(async (p) => {
|
|
104
|
-
const cred = await store.read(p.id);
|
|
105
|
-
const auth = method === "oauth" ? p.auth.oauth : p.auth.apiKey;
|
|
106
|
-
return { value: p.id, label: auth?.name ?? p.name, hint: cred ? `configured (${cred.type})` : undefined };
|
|
107
|
-
}));
|
|
108
|
-
const id = await io.select("Select a provider", options);
|
|
109
|
-
// Answers the PROVIDER, not its id: the caller needs the object, and resolving it here means the
|
|
110
|
-
// one place that can fail to is the one that just offered the list. Cancel and an id that was
|
|
111
|
-
// never offered are the same answer — nothing was chosen — and `find` gives both, since a
|
|
112
|
-
// provider id is always a string and `undefined` matches none of them.
|
|
113
|
-
const chosen = candidates.find((p) => p.id === id);
|
|
114
|
-
if (!chosen)
|
|
115
|
-
throw new LoginCancelled("no provider selected");
|
|
116
|
-
return chosen;
|
|
117
|
-
}
|
|
118
266
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
267
|
+
* `fastagent login`: ask only what was not given (method, then provider, from {@link loginOptions} with each one's
|
|
268
|
+
* stored credential), then {@link login}.
|
|
121
269
|
*/
|
|
122
|
-
export async function loginFlow(io, options
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
let method;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
throw new Error(
|
|
131
|
-
|
|
132
|
-
|
|
270
|
+
export async function loginFlow(io, options) {
|
|
271
|
+
const offered = await loginOptionsOver(options.authPath, options.providers);
|
|
272
|
+
let provider = options.provider;
|
|
273
|
+
let method = options.method;
|
|
274
|
+
if (provider) {
|
|
275
|
+
const methods = offered.filter((o) => o.provider === provider).map((o) => o.method);
|
|
276
|
+
if (methods.length === 0) {
|
|
277
|
+
const known = loginProviders(options.providers).some((p) => p.id === provider);
|
|
278
|
+
throw new Error(known
|
|
279
|
+
? `provider "${provider}" has no interactive login — set its API key via the provider's env var`
|
|
280
|
+
: `unknown provider "${provider}"`);
|
|
281
|
+
}
|
|
282
|
+
method ??= methods.length > 1 ? await selectMethod(io) : methods[0];
|
|
133
283
|
}
|
|
134
284
|
else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
let credential;
|
|
148
|
-
try {
|
|
149
|
-
credential = await auth.login(authCallbacks(io, options.signal, done.signal));
|
|
150
|
-
}
|
|
151
|
-
finally {
|
|
152
|
-
done.abort();
|
|
285
|
+
const chosenMethod = method ?? (await selectMethod(io));
|
|
286
|
+
method = chosenMethod;
|
|
287
|
+
const candidates = offered.filter((o) => o.method === chosenMethod);
|
|
288
|
+
if (candidates.length === 0)
|
|
289
|
+
throw new Error(`no provider supports ${chosenMethod} login`);
|
|
290
|
+
provider = await io.select("Select a provider", candidates.map((o) => ({
|
|
291
|
+
value: o.provider,
|
|
292
|
+
label: o.label,
|
|
293
|
+
...(o.stored ? { hint: `configured (${o.stored})` } : {}),
|
|
294
|
+
})));
|
|
295
|
+
if (!candidates.some((o) => o.provider === provider))
|
|
296
|
+
throw new LoginCancelled("no provider selected");
|
|
153
297
|
}
|
|
154
|
-
|
|
155
|
-
|
|
298
|
+
return loginOver({
|
|
299
|
+
provider: provider,
|
|
300
|
+
method: method,
|
|
301
|
+
authPath: options.authPath,
|
|
302
|
+
interaction: terminalInteraction(io, options.signal),
|
|
303
|
+
}, {
|
|
304
|
+
...(options.providers ? { providers: options.providers } : {}),
|
|
305
|
+
...(options.verifyWith ? { verifyWith: options.verifyWith } : {}),
|
|
306
|
+
...(options.agentDir ? { agentDir: options.agentDir } : {}),
|
|
307
|
+
});
|
|
156
308
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE MACHINE an agent runs on, as fastagent inherits it: the skills and prompt templates this box provides — found
|
|
3
|
+
* by pi's own Agent Skills discovery, installed pi packages included — and pi's engine settings.
|
|
4
|
+
*
|
|
5
|
+
* An agent inherits its machine the way it already inherits the `PATH`: `bash` runs whatever is installed, and a
|
|
6
|
+
* skill in `~/.pi/agent/skills` is available the same way. Deploying ships the project scope — the workspace — and
|
|
7
|
+
* nothing here is compared against a deployment, the same way nobody is told their local `ffmpeg` is not in the image.
|
|
8
|
+
*
|
|
9
|
+
* READ ONCE per process, like the environment it is. The definition is what stays live (`dev` re-reads it per turn,
|
|
10
|
+
* because it is what an author edits); a skill installed after boot arrives on the next start.
|
|
11
|
+
*/
|
|
12
|
+
import { DefaultResourceLoader, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
/** pi's own shapes, taken from its loader rather than re-declared. */
|
|
14
|
+
export type MachineSkill = ReturnType<DefaultResourceLoader["getSkills"]>["skills"][number];
|
|
15
|
+
type MachinePrompt = ReturnType<DefaultResourceLoader["getPrompts"]>["prompts"][number];
|
|
16
|
+
/** What this box lends an agent. */
|
|
17
|
+
export interface Machine {
|
|
18
|
+
skills: MachineSkill[];
|
|
19
|
+
prompts: MachinePrompt[];
|
|
20
|
+
/** pi's engine settings (retry, compaction, cache warming, …) as read at boot — a fresh manager per caller. */
|
|
21
|
+
settingsManager(): SettingsManager;
|
|
22
|
+
}
|
|
23
|
+
/** This process's one read of the machine, shared by the run plane and `commands()` so the two cannot disagree. */
|
|
24
|
+
export declare function readMachine(workspace: string): Promise<Machine>;
|
|
25
|
+
/** The definition's names win: vendoring a skill into `skills/` is how an author overrides the machine's. */
|
|
26
|
+
export declare function withMachine<Own extends {
|
|
27
|
+
name: string;
|
|
28
|
+
}, Lent extends {
|
|
29
|
+
name: string;
|
|
30
|
+
}>(own: readonly Own[], lent: readonly Lent[]): (Own | Lent)[];
|
|
31
|
+
export {};
|