expandai 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/README.md +2 -2
- package/build/{esm/Install/Opencode → assets/opencode}/expandai-webfetch.js +22 -4
- package/build/dts/Mcp/RequestHeaders.d.ts +0 -1
- package/build/dts/Mcp/Server.d.ts +1 -2
- package/build/dts/index.d.ts +0 -1
- package/build/esm/index.js +193 -2
- package/build/esm/main.js +691 -200
- package/build/skills/expandai/SKILL.md +100 -8
- package/package.json +20 -12
- package/build/dts/ApiClient.d.ts +0 -165
- package/build/dts/ApiClient.d.ts.map +0 -1
- package/build/dts/Auth/Domain.d.ts +0 -95
- package/build/dts/Auth/Domain.d.ts.map +0 -1
- package/build/dts/Auth/ManualAuthorization.d.ts +0 -107
- package/build/dts/Auth/ManualAuthorization.d.ts.map +0 -1
- package/build/dts/Auth/PendingInit.d.ts +0 -35
- package/build/dts/Auth/PendingInit.d.ts.map +0 -1
- package/build/dts/Auth/Pkce.d.ts +0 -10
- package/build/dts/Auth/Pkce.d.ts.map +0 -1
- package/build/dts/Auth/SetupGrantAuthorization.d.ts +0 -34
- package/build/dts/Auth/SetupGrantAuthorization.d.ts.map +0 -1
- package/build/dts/Authentication.d.ts +0 -24
- package/build/dts/Authentication.d.ts.map +0 -1
- package/build/dts/BrowserOAuth.d.ts +0 -99
- package/build/dts/BrowserOAuth.d.ts.map +0 -1
- package/build/dts/CommandPath.d.ts +0 -2
- package/build/dts/CommandPath.d.ts.map +0 -1
- package/build/dts/Commands/Fetch.d.ts +0 -30
- package/build/dts/Commands/Fetch.d.ts.map +0 -1
- package/build/dts/Commands/Hook.d.ts +0 -17
- package/build/dts/Commands/Hook.d.ts.map +0 -1
- package/build/dts/Commands/Init.d.ts +0 -55
- package/build/dts/Commands/Init.d.ts.map +0 -1
- package/build/dts/Commands/Mcp.d.ts +0 -11
- package/build/dts/Commands/Mcp.d.ts.map +0 -1
- package/build/dts/Commands/McpRun.d.ts +0 -5
- package/build/dts/Commands/McpRun.d.ts.map +0 -1
- package/build/dts/Commands/Search.d.ts +0 -21
- package/build/dts/Commands/Search.d.ts.map +0 -1
- package/build/dts/Commands/Skill.d.ts +0 -12
- package/build/dts/Commands/Skill.d.ts.map +0 -1
- package/build/dts/Commands/Status.d.ts +0 -27
- package/build/dts/Commands/Status.d.ts.map +0 -1
- package/build/dts/Commands/Telemetry.d.ts +0 -7
- package/build/dts/Commands/Telemetry.d.ts.map +0 -1
- package/build/dts/Commands/Uninstall.d.ts +0 -9
- package/build/dts/Commands/Uninstall.d.ts.map +0 -1
- package/build/dts/Config.d.ts +0 -196
- package/build/dts/Config.d.ts.map +0 -1
- package/build/dts/Hook/Claude.d.ts +0 -13
- package/build/dts/Hook/Claude.d.ts.map +0 -1
- package/build/dts/Hook/Errors.d.ts +0 -20
- package/build/dts/Hook/Errors.d.ts.map +0 -1
- package/build/dts/Hook/Opencode.d.ts +0 -13
- package/build/dts/Hook/Opencode.d.ts.map +0 -1
- package/build/dts/Hook/SnapshotNote.d.ts +0 -12
- package/build/dts/Hook/SnapshotNote.d.ts.map +0 -1
- package/build/dts/Init/ClaudeAdapter.d.ts +0 -61
- package/build/dts/Init/ClaudeAdapter.d.ts.map +0 -1
- package/build/dts/Init/ClaudeProfile.d.ts +0 -77
- package/build/dts/Init/ClaudeProfile.d.ts.map +0 -1
- package/build/dts/Init/Domain.d.ts +0 -139
- package/build/dts/Init/Domain.d.ts.map +0 -1
- package/build/dts/Init/HarnessAdapter.d.ts +0 -33
- package/build/dts/Init/HarnessAdapter.d.ts.map +0 -1
- package/build/dts/Init/HarnessDetector.d.ts +0 -19
- package/build/dts/Init/HarnessDetector.d.ts.map +0 -1
- package/build/dts/Init/HarnessRegistry.d.ts +0 -50
- package/build/dts/Init/HarnessRegistry.d.ts.map +0 -1
- package/build/dts/Init/MutationTransaction.d.ts +0 -96
- package/build/dts/Init/MutationTransaction.d.ts.map +0 -1
- package/build/dts/Init/Service.d.ts +0 -72
- package/build/dts/Init/Service.d.ts.map +0 -1
- package/build/dts/Init/StandardAdapters.d.ts +0 -42
- package/build/dts/Init/StandardAdapters.d.ts.map +0 -1
- package/build/dts/Init/Status.d.ts +0 -149
- package/build/dts/Init/Status.d.ts.map +0 -1
- package/build/dts/Install/Claude/Hook.d.ts +0 -88
- package/build/dts/Install/Claude/Hook.d.ts.map +0 -1
- package/build/dts/Install/Claude/Mcp.d.ts +0 -48
- package/build/dts/Install/Claude/Mcp.d.ts.map +0 -1
- package/build/dts/Install/Claude/Skill.d.ts +0 -23
- package/build/dts/Install/Claude/Skill.d.ts.map +0 -1
- package/build/dts/Install/Codex/Mcp.d.ts +0 -45
- package/build/dts/Install/Codex/Mcp.d.ts.map +0 -1
- package/build/dts/Install/Codex/Skill.d.ts +0 -22
- package/build/dts/Install/Codex/Skill.d.ts.map +0 -1
- package/build/dts/Install/Cursor/Mcp.d.ts +0 -45
- package/build/dts/Install/Cursor/Mcp.d.ts.map +0 -1
- package/build/dts/Install/Cursor/Skill.d.ts +0 -29
- package/build/dts/Install/Cursor/Skill.d.ts.map +0 -1
- package/build/dts/Install/Domain.d.ts +0 -103
- package/build/dts/Install/Domain.d.ts.map +0 -1
- package/build/dts/Install/Hook.d.ts +0 -30
- package/build/dts/Install/Hook.d.ts.map +0 -1
- package/build/dts/Install/Mcp.d.ts +0 -28
- package/build/dts/Install/Mcp.d.ts.map +0 -1
- package/build/dts/Install/Opencode/Hook.d.ts +0 -24
- package/build/dts/Install/Opencode/Hook.d.ts.map +0 -1
- package/build/dts/Install/Opencode/Mcp.d.ts +0 -47
- package/build/dts/Install/Opencode/Mcp.d.ts.map +0 -1
- package/build/dts/Install/Opencode/Skill.d.ts +0 -26
- package/build/dts/Install/Opencode/Skill.d.ts.map +0 -1
- package/build/dts/Install/Skill.d.ts +0 -32
- package/build/dts/Install/Skill.d.ts.map +0 -1
- package/build/dts/Install/Uninstall.d.ts +0 -32
- package/build/dts/Install/Uninstall.d.ts.map +0 -1
- package/build/dts/MarkdownAnsi.d.ts +0 -29
- package/build/dts/MarkdownAnsi.d.ts.map +0 -1
- package/build/dts/Mcp/ExpandDocs.d.ts +0 -19
- package/build/dts/Mcp/ExpandDocs.d.ts.map +0 -1
- package/build/dts/Mcp/RequestHeaders.d.ts.map +0 -1
- package/build/dts/Mcp/Server.d.ts.map +0 -1
- package/build/dts/Mcp/Toolkit.d.ts +0 -3
- package/build/dts/Mcp/Toolkit.d.ts.map +0 -1
- package/build/dts/Mcp/tools/Citations.d.ts +0 -266
- package/build/dts/Mcp/tools/Citations.d.ts.map +0 -1
- package/build/dts/Mcp/tools/Fetch.d.ts +0 -168
- package/build/dts/Mcp/tools/Fetch.d.ts.map +0 -1
- package/build/dts/SecureFile.d.ts +0 -23
- package/build/dts/SecureFile.d.ts.map +0 -1
- package/build/dts/Snippets.d.ts +0 -18
- package/build/dts/Snippets.d.ts.map +0 -1
- package/build/dts/Telemetry.d.ts +0 -30
- package/build/dts/Telemetry.d.ts.map +0 -1
- package/build/dts/Version.d.ts +0 -2
- package/build/dts/Version.d.ts.map +0 -1
- package/build/dts/Xdg.d.ts +0 -11
- package/build/dts/Xdg.d.ts.map +0 -1
- package/build/dts/index.d.ts.map +0 -1
- package/build/dts/main.d.ts +0 -3
- package/build/dts/main.d.ts.map +0 -1
- package/build/esm/ApiClient.js +0 -305
- package/build/esm/ApiClient.js.map +0 -1
- package/build/esm/Auth/Domain.js +0 -56
- package/build/esm/Auth/Domain.js.map +0 -1
- package/build/esm/Auth/ManualAuthorization.js +0 -253
- package/build/esm/Auth/ManualAuthorization.js.map +0 -1
- package/build/esm/Auth/PendingInit.js +0 -267
- package/build/esm/Auth/PendingInit.js.map +0 -1
- package/build/esm/Auth/Pkce.js +0 -23
- package/build/esm/Auth/Pkce.js.map +0 -1
- package/build/esm/Auth/SetupGrantAuthorization.js +0 -91
- package/build/esm/Auth/SetupGrantAuthorization.js.map +0 -1
- package/build/esm/Authentication.js +0 -143
- package/build/esm/Authentication.js.map +0 -1
- package/build/esm/BrowserOAuth.js +0 -251
- package/build/esm/BrowserOAuth.js.map +0 -1
- package/build/esm/CommandPath.js +0 -31
- package/build/esm/CommandPath.js.map +0 -1
- package/build/esm/Commands/Fetch.js +0 -119
- package/build/esm/Commands/Fetch.js.map +0 -1
- package/build/esm/Commands/Hook.js +0 -20
- package/build/esm/Commands/Hook.js.map +0 -1
- package/build/esm/Commands/Init.js +0 -371
- package/build/esm/Commands/Init.js.map +0 -1
- package/build/esm/Commands/Mcp.js +0 -19
- package/build/esm/Commands/Mcp.js.map +0 -1
- package/build/esm/Commands/McpRun.js +0 -15
- package/build/esm/Commands/McpRun.js.map +0 -1
- package/build/esm/Commands/Search.js +0 -46
- package/build/esm/Commands/Search.js.map +0 -1
- package/build/esm/Commands/Skill.js +0 -17
- package/build/esm/Commands/Skill.js.map +0 -1
- package/build/esm/Commands/Status.js +0 -186
- package/build/esm/Commands/Status.js.map +0 -1
- package/build/esm/Commands/Telemetry.js +0 -23
- package/build/esm/Commands/Telemetry.js.map +0 -1
- package/build/esm/Commands/Uninstall.js +0 -28
- package/build/esm/Commands/Uninstall.js.map +0 -1
- package/build/esm/Config.js +0 -619
- package/build/esm/Config.js.map +0 -1
- package/build/esm/Hook/Claude.js +0 -74
- package/build/esm/Hook/Claude.js.map +0 -1
- package/build/esm/Hook/Errors.js +0 -18
- package/build/esm/Hook/Errors.js.map +0 -1
- package/build/esm/Hook/Opencode.js +0 -50
- package/build/esm/Hook/Opencode.js.map +0 -1
- package/build/esm/Hook/SnapshotNote.js +0 -65
- package/build/esm/Hook/SnapshotNote.js.map +0 -1
- package/build/esm/Init/ClaudeAdapter.js +0 -137
- package/build/esm/Init/ClaudeAdapter.js.map +0 -1
- package/build/esm/Init/ClaudeProfile.js +0 -344
- package/build/esm/Init/ClaudeProfile.js.map +0 -1
- package/build/esm/Init/Domain.js +0 -118
- package/build/esm/Init/Domain.js.map +0 -1
- package/build/esm/Init/HarnessAdapter.js +0 -21
- package/build/esm/Init/HarnessAdapter.js.map +0 -1
- package/build/esm/Init/HarnessDetector.js +0 -65
- package/build/esm/Init/HarnessDetector.js.map +0 -1
- package/build/esm/Init/HarnessRegistry.js +0 -58
- package/build/esm/Init/HarnessRegistry.js.map +0 -1
- package/build/esm/Init/MutationTransaction.js +0 -494
- package/build/esm/Init/MutationTransaction.js.map +0 -1
- package/build/esm/Init/Service.js +0 -319
- package/build/esm/Init/Service.js.map +0 -1
- package/build/esm/Init/StandardAdapters.js +0 -116
- package/build/esm/Init/StandardAdapters.js.map +0 -1
- package/build/esm/Init/Status.js +0 -215
- package/build/esm/Init/Status.js.map +0 -1
- package/build/esm/Install/Claude/Hook.js +0 -77
- package/build/esm/Install/Claude/Hook.js.map +0 -1
- package/build/esm/Install/Claude/Mcp.js +0 -101
- package/build/esm/Install/Claude/Mcp.js.map +0 -1
- package/build/esm/Install/Claude/Skill.js +0 -56
- package/build/esm/Install/Claude/Skill.js.map +0 -1
- package/build/esm/Install/Codex/Mcp.js +0 -97
- package/build/esm/Install/Codex/Mcp.js.map +0 -1
- package/build/esm/Install/Codex/Skill.js +0 -33
- package/build/esm/Install/Codex/Skill.js.map +0 -1
- package/build/esm/Install/Cursor/Mcp.js +0 -74
- package/build/esm/Install/Cursor/Mcp.js.map +0 -1
- package/build/esm/Install/Cursor/Skill.js +0 -40
- package/build/esm/Install/Cursor/Skill.js.map +0 -1
- package/build/esm/Install/Domain.js +0 -162
- package/build/esm/Install/Domain.js.map +0 -1
- package/build/esm/Install/Hook.js +0 -38
- package/build/esm/Install/Hook.js.map +0 -1
- package/build/esm/Install/Mcp.js +0 -40
- package/build/esm/Install/Mcp.js.map +0 -1
- package/build/esm/Install/Opencode/Hook.js +0 -44
- package/build/esm/Install/Opencode/Hook.js.map +0 -1
- package/build/esm/Install/Opencode/Mcp.js +0 -76
- package/build/esm/Install/Opencode/Mcp.js.map +0 -1
- package/build/esm/Install/Opencode/Skill.js +0 -37
- package/build/esm/Install/Opencode/Skill.js.map +0 -1
- package/build/esm/Install/Skill.js +0 -41
- package/build/esm/Install/Skill.js.map +0 -1
- package/build/esm/Install/Uninstall.js +0 -49
- package/build/esm/Install/Uninstall.js.map +0 -1
- package/build/esm/MarkdownAnsi.js +0 -142
- package/build/esm/MarkdownAnsi.js.map +0 -1
- package/build/esm/Mcp/ExpandDocs.js +0 -56
- package/build/esm/Mcp/ExpandDocs.js.map +0 -1
- package/build/esm/Mcp/RequestHeaders.js +0 -13
- package/build/esm/Mcp/RequestHeaders.js.map +0 -1
- package/build/esm/Mcp/Server.js +0 -12
- package/build/esm/Mcp/Server.js.map +0 -1
- package/build/esm/Mcp/Toolkit.js +0 -16
- package/build/esm/Mcp/Toolkit.js.map +0 -1
- package/build/esm/Mcp/tools/Citations.js +0 -193
- package/build/esm/Mcp/tools/Citations.js.map +0 -1
- package/build/esm/Mcp/tools/Fetch.js +0 -165
- package/build/esm/Mcp/tools/Fetch.js.map +0 -1
- package/build/esm/SecureFile.js +0 -36
- package/build/esm/SecureFile.js.map +0 -1
- package/build/esm/Snippets.js +0 -29
- package/build/esm/Snippets.js.map +0 -1
- package/build/esm/Telemetry.js +0 -157
- package/build/esm/Telemetry.js.map +0 -1
- package/build/esm/Version.js +0 -4
- package/build/esm/Version.js.map +0 -1
- package/build/esm/Xdg.js +0 -15
- package/build/esm/Xdg.js.map +0 -1
- package/build/esm/index.js.map +0 -1
- package/build/esm/main.js.map +0 -1
- package/src/ApiClient.ts +0 -450
- package/src/Auth/Domain.ts +0 -63
- package/src/Auth/ManualAuthorization.ts +0 -296
- package/src/Auth/PendingInit.ts +0 -338
- package/src/Auth/Pkce.ts +0 -26
- package/src/Auth/SetupGrantAuthorization.ts +0 -116
- package/src/Authentication.ts +0 -202
- package/src/BrowserOAuth.ts +0 -359
- package/src/CommandPath.ts +0 -30
- package/src/Commands/Fetch.ts +0 -188
- package/src/Commands/Hook.ts +0 -48
- package/src/Commands/Init.ts +0 -489
- package/src/Commands/Mcp.ts +0 -30
- package/src/Commands/McpRun.ts +0 -25
- package/src/Commands/Search.ts +0 -97
- package/src/Commands/Skill.ts +0 -30
- package/src/Commands/Status.ts +0 -228
- package/src/Commands/Telemetry.ts +0 -42
- package/src/Commands/Uninstall.ts +0 -38
- package/src/Config.ts +0 -840
- package/src/Hook/Claude.ts +0 -81
- package/src/Hook/Errors.ts +0 -19
- package/src/Hook/Opencode.ts +0 -58
- package/src/Hook/SnapshotNote.ts +0 -66
- package/src/Init/ClaudeAdapter.ts +0 -191
- package/src/Init/ClaudeProfile.ts +0 -453
- package/src/Init/Domain.ts +0 -153
- package/src/Init/HarnessAdapter.ts +0 -35
- package/src/Init/HarnessDetector.ts +0 -77
- package/src/Init/HarnessRegistry.ts +0 -87
- package/src/Init/MutationTransaction.ts +0 -747
- package/src/Init/Service.ts +0 -385
- package/src/Init/StandardAdapters.ts +0 -141
- package/src/Init/Status.ts +0 -253
- package/src/Install/Claude/Hook.ts +0 -123
- package/src/Install/Claude/Mcp.ts +0 -129
- package/src/Install/Claude/Skill.ts +0 -77
- package/src/Install/Codex/Mcp.ts +0 -114
- package/src/Install/Codex/Skill.ts +0 -44
- package/src/Install/Cursor/Mcp.ts +0 -85
- package/src/Install/Cursor/Skill.ts +0 -51
- package/src/Install/Domain.ts +0 -221
- package/src/Install/Hook.ts +0 -50
- package/src/Install/Mcp.ts +0 -71
- package/src/Install/Opencode/Hook.ts +0 -55
- package/src/Install/Opencode/Mcp.ts +0 -87
- package/src/Install/Opencode/Skill.ts +0 -48
- package/src/Install/Opencode/expandai-webfetch.d.ts +0 -23
- package/src/Install/Opencode/expandai-webfetch.js +0 -56
- package/src/Install/Skill.ts +0 -57
- package/src/Install/Uninstall.ts +0 -82
- package/src/MarkdownAnsi.ts +0 -162
- package/src/Mcp/ExpandDocs.ts +0 -62
- package/src/Mcp/RequestHeaders.ts +0 -16
- package/src/Mcp/Server.ts +0 -15
- package/src/Mcp/Toolkit.ts +0 -21
- package/src/Mcp/tools/Citations.ts +0 -219
- package/src/Mcp/tools/Fetch.ts +0 -203
- package/src/SecureFile.ts +0 -70
- package/src/Snippets.ts +0 -39
- package/src/Telemetry.ts +0 -186
- package/src/Version.ts +0 -3
- package/src/Xdg.ts +0 -18
- package/src/index.ts +0 -1
- package/src/main.ts +0 -305
package/src/ApiClient.ts
DELETED
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
import { HttpClient, HttpClientRequest, HttpClientResponse } from '@effect/platform'
|
|
2
|
-
import { NodeHttpClient } from '@effect/platform-node'
|
|
3
|
-
import { fetchObjectModeJson, fetchSnapshotSearch } from '@expandai/sdk/FetchJson'
|
|
4
|
-
import * as Generated from '@expandai/sdk/Generated'
|
|
5
|
-
import { decodeError, unexpectedStatus } from '@expandai/sdk/HttpClientHelpers'
|
|
6
|
-
import { Data, DateTime, Duration, Effect, flow, Match, Option, Redacted, Schema } from 'effect'
|
|
7
|
-
import { CLI_AUTHORIZATION_CLIENT_ID } from './Auth/Domain.js'
|
|
8
|
-
import { BrowserOAuthService } from './BrowserOAuth.js'
|
|
9
|
-
import { ConfigManager, OAuthConfig, OAuthTokenExpired } from './Config.js'
|
|
10
|
-
import { CurrentRequestHeaders } from './Mcp/RequestHeaders.js'
|
|
11
|
-
|
|
12
|
-
export class DeviceAuthorizationCreated extends Schema.Class<DeviceAuthorizationCreated>('DeviceAuthorizationCreated')({
|
|
13
|
-
deviceCode: Schema.Redacted(Schema.NonEmptyString),
|
|
14
|
-
userCode: Schema.NonEmptyString,
|
|
15
|
-
// `Schema.URL` decodes to a `URL`; the wire form stays a string. The CLI
|
|
16
|
-
// hands this to the user to open, so a malformed `appUrls.app` should fail
|
|
17
|
-
// here rather than in a browser.
|
|
18
|
-
verificationUri: Schema.URL,
|
|
19
|
-
// Whole seconds. A zero or negative `expiresIn` is a window that has already
|
|
20
|
-
// closed, so it must be positive.
|
|
21
|
-
expiresIn: Schema.Int.pipe(Schema.positive()),
|
|
22
|
-
// `interval` is RFC 8628's *minimum* wait between polls, so zero is
|
|
23
|
-
// meaningful — poll without delay. Only negative is nonsense, and it would
|
|
24
|
-
// make `nextPollAt` drift backwards forever.
|
|
25
|
-
interval: Schema.Int.pipe(Schema.nonNegative()),
|
|
26
|
-
}) {}
|
|
27
|
-
|
|
28
|
-
const ManualAuthorizationTokenResponse = Schema.Struct({
|
|
29
|
-
access_token: Schema.Redacted(Schema.NonEmptyString),
|
|
30
|
-
refresh_token: Schema.Redacted(Schema.NonEmptyString),
|
|
31
|
-
token_type: Schema.NonEmptyString,
|
|
32
|
-
expires_in: Schema.Number.pipe(Schema.positive()),
|
|
33
|
-
scope: Schema.optional(Schema.String),
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
export type ManualAuthorizationTokenResponse = typeof ManualAuthorizationTokenResponse.Type
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* RFC 8628 §3.5 poll outcomes. These are results, not errors: an agent-driven
|
|
40
|
-
* CLI returns immediately on every one of them, and only a transport failure
|
|
41
|
-
* is exceptional.
|
|
42
|
-
*/
|
|
43
|
-
export type DevicePollOutcome =
|
|
44
|
-
| { readonly _tag: 'Tokens'; readonly token: ManualAuthorizationTokenResponse }
|
|
45
|
-
| { readonly _tag: 'Pending' }
|
|
46
|
-
| { readonly _tag: 'SlowDown' }
|
|
47
|
-
| { readonly _tag: 'Denied' }
|
|
48
|
-
| { readonly _tag: 'Expired' }
|
|
49
|
-
| { readonly _tag: 'Invalid' }
|
|
50
|
-
/**
|
|
51
|
-
* The server refused for a reason a fresh code cannot fix — a misconfigured
|
|
52
|
-
* client, resource, or scope. Distinct from `Invalid` because the recovery is
|
|
53
|
-
* opposite: `Invalid` means start over, this means stop and report. Carries
|
|
54
|
-
* the server's own `error` so the message names the actual cause instead of
|
|
55
|
-
* guessing.
|
|
56
|
-
*/
|
|
57
|
-
| { readonly _tag: 'Refused'; readonly error: string; readonly description?: string }
|
|
58
|
-
|
|
59
|
-
const ManualAuthorizationOperation = Schema.Literal('create', 'poll')
|
|
60
|
-
|
|
61
|
-
export class ManualAuthorizationApiError extends Schema.TaggedError<ManualAuthorizationApiError>()(
|
|
62
|
-
'ManualAuthorizationApiError',
|
|
63
|
-
{
|
|
64
|
-
operation: ManualAuthorizationOperation,
|
|
65
|
-
kind: Schema.Literal('request_failed', 'rejected', 'invalid_response'),
|
|
66
|
-
status: Schema.optional(Schema.Number),
|
|
67
|
-
recoverable: Schema.Boolean,
|
|
68
|
-
},
|
|
69
|
-
) {
|
|
70
|
-
get message() {
|
|
71
|
-
return this.kind === 'rejected'
|
|
72
|
-
? `Manual authorization ${this.operation} was rejected. Restart authorization if the problem persists.`
|
|
73
|
-
: `Manual authorization ${this.operation} could not be completed safely. Try again.`
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Single user-facing error for any failure crossing the API boundary. The
|
|
79
|
-
* generated client raises a half-dozen status-specific tagged classes plus
|
|
80
|
-
* transport/decode errors; we collapse them here so the rest of the app sees
|
|
81
|
-
* one tag with a ready-made `.message` and never has to know about
|
|
82
|
-
* `Generated.ts` types.
|
|
83
|
-
*/
|
|
84
|
-
export class ApiRequestFailed extends Schema.TaggedError<ApiRequestFailed>()('ApiRequestFailed', {
|
|
85
|
-
detail: Schema.String,
|
|
86
|
-
}) {
|
|
87
|
-
get message() {
|
|
88
|
-
return this.detail
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Derived rather than hand-written so codegen drift surfaces as a type error.
|
|
93
|
-
type ClientError =
|
|
94
|
-
| Effect.Effect.Error<ReturnType<Generated.Client['fetchFetch']>>
|
|
95
|
-
| Effect.Effect.Error<ReturnType<Generated.Client['fetchFetchJson']>>
|
|
96
|
-
| Effect.Effect.Error<ReturnType<Generated.Client['fetchFetchSearch']>>
|
|
97
|
-
| Effect.Effect.Error<ReturnType<Generated.Client['fetchBatched']>>
|
|
98
|
-
| Effect.Effect.Error<ReturnType<Generated.Client['fetchGetBatched']>>
|
|
99
|
-
type Issue = typeof Generated.Issue.Type
|
|
100
|
-
type OAuthAuthMethod = typeof OAuthConfig.Type
|
|
101
|
-
|
|
102
|
-
const OAUTH_REFRESH_SKEW = Duration.minutes(1)
|
|
103
|
-
|
|
104
|
-
class OAuthAuthMethodChanged extends Data.TaggedError('OAuthAuthMethodChanged')<Record<string, never>> {}
|
|
105
|
-
|
|
106
|
-
function getField(value: unknown, key: string): unknown {
|
|
107
|
-
return typeof value === 'object' && value !== null && key in value ? value[key as keyof typeof value] : undefined
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function getStringField(value: unknown, key: string): string | undefined {
|
|
111
|
-
const field = getField(value, key)
|
|
112
|
-
return typeof field === 'string' ? field : undefined
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function getIssueList(value: unknown): Issue[] {
|
|
116
|
-
const issues = getField(value, 'issues')
|
|
117
|
-
return Array.isArray(issues) ? (issues as Issue[]) : []
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function getUnsupportedFields(value: unknown): string[] {
|
|
121
|
-
const unsupportedFields = getField(value, 'unsupportedFields')
|
|
122
|
-
return Array.isArray(unsupportedFields) ? unsupportedFields.filter((field) => typeof field === 'string') : []
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function formatIssue({ path, message }: Issue): string {
|
|
126
|
-
const prefix = path.map((segment) => (typeof segment === 'object' ? segment.key : String(segment))).join('.')
|
|
127
|
-
return prefix ? `${prefix}: ${message}` : message
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function formatApiFailure(error: ClientError): string {
|
|
131
|
-
switch (getStringField(error, '_tag')) {
|
|
132
|
-
case 'HttpApiDecodeError': {
|
|
133
|
-
const issues = getIssueList(error)
|
|
134
|
-
return issues.length === 0
|
|
135
|
-
? (getStringField(error, 'message') ?? 'Invalid request')
|
|
136
|
-
: issues.map(formatIssue).join('; ')
|
|
137
|
-
}
|
|
138
|
-
case 'AuthFailed':
|
|
139
|
-
return `Authentication failed (${getStringField(error, 'reason') ?? 'unknown'}). Run \`expandai login\` to re-authenticate.`
|
|
140
|
-
case 'AccessBlocked':
|
|
141
|
-
return 'Access blocked. Check your billing status or contact support.'
|
|
142
|
-
case 'BatchedIncludeUnsupported':
|
|
143
|
-
return `Unsupported batched include fields: ${getUnsupportedFields(error).join(', ')}.`
|
|
144
|
-
case 'FetchSnapshotSearchNotFound':
|
|
145
|
-
return 'Fetch snapshot not found.'
|
|
146
|
-
case 'PayloadTooLarge':
|
|
147
|
-
return 'Request payload is too large.'
|
|
148
|
-
case 'TooManyRequests':
|
|
149
|
-
return 'Rate limit exceeded — try again in a moment.'
|
|
150
|
-
case 'InternalError':
|
|
151
|
-
return 'The expand.ai API returned an internal error. Try again or contact support.'
|
|
152
|
-
case 'ServiceUnavailable':
|
|
153
|
-
return 'The expand.ai API is temporarily unavailable. Try again shortly.'
|
|
154
|
-
default:
|
|
155
|
-
return error instanceof Error ? error.message : (getStringField(error, 'message') ?? 'Expand API request failed.')
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const liftApiError = <A, R>(self: Effect.Effect<A, ClientError, R>) =>
|
|
160
|
-
self.pipe(Effect.catchAll((error) => new ApiRequestFailed({ detail: formatApiFailure(error) })))
|
|
161
|
-
|
|
162
|
-
/** Public, unauthenticated HTTP boundary for device-flow CLI authorization. */
|
|
163
|
-
export class ManualAuthorizationApiClient extends Effect.Service<ManualAuthorizationApiClient>()(
|
|
164
|
-
'@expandai/cli/ManualAuthorizationApiClient',
|
|
165
|
-
{
|
|
166
|
-
dependencies: [NodeHttpClient.layer, ConfigManager.Default],
|
|
167
|
-
effect: Effect.gen(function* () {
|
|
168
|
-
const httpClient = yield* HttpClient.HttpClient
|
|
169
|
-
const configManager = yield* ConfigManager
|
|
170
|
-
|
|
171
|
-
const apiError = (
|
|
172
|
-
operation: typeof ManualAuthorizationOperation.Type,
|
|
173
|
-
kind: 'request_failed' | 'rejected' | 'invalid_response',
|
|
174
|
-
status?: number,
|
|
175
|
-
) =>
|
|
176
|
-
new ManualAuthorizationApiError({
|
|
177
|
-
operation,
|
|
178
|
-
kind,
|
|
179
|
-
status,
|
|
180
|
-
recoverable: kind !== 'invalid_response',
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const create = Effect.fn('ManualAuthorizationApiClient.create')(function* (input: {
|
|
184
|
-
readonly harness: 'agent-harnesses'
|
|
185
|
-
readonly scope: 'project' | 'global'
|
|
186
|
-
readonly platform: string
|
|
187
|
-
readonly machineLabel: string
|
|
188
|
-
readonly cliVersion: string
|
|
189
|
-
}) {
|
|
190
|
-
const apiUrl = yield* configManager.getApiUrl()
|
|
191
|
-
const request = yield* HttpClientRequest.post(new URL('/v1/cli-authorization/device', apiUrl)).pipe(
|
|
192
|
-
HttpClientRequest.bodyJson(input),
|
|
193
|
-
Effect.mapError(() => apiError('create', 'request_failed')),
|
|
194
|
-
)
|
|
195
|
-
return yield* httpClient.execute(request).pipe(
|
|
196
|
-
Effect.flatMap((response) =>
|
|
197
|
-
response.status >= 200 && response.status < 300
|
|
198
|
-
? HttpClientResponse.schemaBodyJson(DeviceAuthorizationCreated)(response).pipe(
|
|
199
|
-
Effect.mapError(() => apiError('create', 'invalid_response')),
|
|
200
|
-
)
|
|
201
|
-
: Effect.fail(apiError('create', 'rejected', response.status)),
|
|
202
|
-
),
|
|
203
|
-
Effect.catchAll((error) =>
|
|
204
|
-
error instanceof ManualAuthorizationApiError
|
|
205
|
-
? Effect.fail(error)
|
|
206
|
-
: Effect.fail(apiError('create', 'request_failed')),
|
|
207
|
-
),
|
|
208
|
-
)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
const PollErrorBody = Schema.Struct({
|
|
212
|
-
error: Schema.String,
|
|
213
|
-
error_description: Schema.optional(Schema.String),
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* RFC 6749/8628 errors that describe the *request* rather than the state
|
|
218
|
-
* of this particular code. Re-minting produces a code that fails
|
|
219
|
-
* identically, so treating these as "expired" is what turns a
|
|
220
|
-
* misconfiguration into an endless approve loop.
|
|
221
|
-
*/
|
|
222
|
-
const PERMANENT_REFUSALS = new Set([
|
|
223
|
-
'invalid_target',
|
|
224
|
-
'invalid_client',
|
|
225
|
-
'invalid_scope',
|
|
226
|
-
'unsupported_grant_type',
|
|
227
|
-
'unauthorized_client',
|
|
228
|
-
'invalid_request',
|
|
229
|
-
])
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* One poll of the OAuth token endpoint (RFC 8628). Form-encoded, and
|
|
233
|
-
* always against `/oauth2/token`: `/device/token` is the first-party
|
|
234
|
-
* session path and the server rejects our client there by design.
|
|
235
|
-
*/
|
|
236
|
-
const poll = Effect.fn('ManualAuthorizationApiClient.poll')(function* (deviceCode: Redacted.Redacted<string>) {
|
|
237
|
-
const apiUrl = yield* configManager.getApiUrl()
|
|
238
|
-
const request = HttpClientRequest.post(new URL('/api/auth/oauth2/token', apiUrl)).pipe(
|
|
239
|
-
HttpClientRequest.bodyUrlParams({
|
|
240
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
241
|
-
device_code: Redacted.value(deviceCode),
|
|
242
|
-
client_id: CLI_AUTHORIZATION_CLIENT_ID,
|
|
243
|
-
}),
|
|
244
|
-
)
|
|
245
|
-
return yield* httpClient.execute(request).pipe(
|
|
246
|
-
Effect.flatMap(
|
|
247
|
-
Effect.fnUntraced(function* (response) {
|
|
248
|
-
if (response.status >= 200 && response.status < 300) {
|
|
249
|
-
const token = yield* HttpClientResponse.schemaBodyJson(ManualAuthorizationTokenResponse)(response).pipe(
|
|
250
|
-
Effect.mapError(() => apiError('poll', 'invalid_response')),
|
|
251
|
-
)
|
|
252
|
-
return { _tag: 'Tokens', token } satisfies DevicePollOutcome
|
|
253
|
-
}
|
|
254
|
-
if (response.status >= 500) return yield* apiError('poll', 'rejected', response.status)
|
|
255
|
-
const body = yield* HttpClientResponse.schemaBodyJson(PollErrorBody)(response).pipe(
|
|
256
|
-
Effect.mapError(() => apiError('poll', 'invalid_response')),
|
|
257
|
-
)
|
|
258
|
-
switch (body.error) {
|
|
259
|
-
case 'authorization_pending':
|
|
260
|
-
return { _tag: 'Pending' } satisfies DevicePollOutcome
|
|
261
|
-
case 'slow_down':
|
|
262
|
-
return { _tag: 'SlowDown' } satisfies DevicePollOutcome
|
|
263
|
-
case 'access_denied':
|
|
264
|
-
return { _tag: 'Denied' } satisfies DevicePollOutcome
|
|
265
|
-
case 'expired_token':
|
|
266
|
-
return { _tag: 'Expired' } satisfies DevicePollOutcome
|
|
267
|
-
default:
|
|
268
|
-
return PERMANENT_REFUSALS.has(body.error)
|
|
269
|
-
? ({
|
|
270
|
-
_tag: 'Refused',
|
|
271
|
-
error: body.error,
|
|
272
|
-
...(body.error_description === undefined ? {} : { description: body.error_description }),
|
|
273
|
-
} satisfies DevicePollOutcome)
|
|
274
|
-
: ({ _tag: 'Invalid' } satisfies DevicePollOutcome)
|
|
275
|
-
}
|
|
276
|
-
}),
|
|
277
|
-
),
|
|
278
|
-
Effect.catchAll((error) =>
|
|
279
|
-
error instanceof ManualAuthorizationApiError
|
|
280
|
-
? Effect.fail(error)
|
|
281
|
-
: Effect.fail(apiError('poll', 'request_failed')),
|
|
282
|
-
),
|
|
283
|
-
)
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
return { create, poll } as const
|
|
287
|
-
}),
|
|
288
|
-
},
|
|
289
|
-
) {}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Resolves outgoing API auth per call. When the current request scope carries
|
|
293
|
-
* an `Authorization` header (via `CurrentRequestHeaders`), that value is
|
|
294
|
-
* forwarded; otherwise auth comes from `~/.config/expandai/config.json`.
|
|
295
|
-
*/
|
|
296
|
-
export class ApiClient extends Effect.Service<ApiClient>()('@expandai/cli/ApiClient', {
|
|
297
|
-
dependencies: [NodeHttpClient.layer, ConfigManager.Default, BrowserOAuthService.Default],
|
|
298
|
-
scoped: Effect.gen(function* () {
|
|
299
|
-
const configManager = yield* ConfigManager
|
|
300
|
-
const browserOAuth = yield* BrowserOAuthService
|
|
301
|
-
const baseClient = yield* HttpClient.HttpClient
|
|
302
|
-
const refreshLock = yield* Effect.makeSemaphore(1)
|
|
303
|
-
|
|
304
|
-
const freshOAuthAccessToken = Effect.fn('ApiClient.freshOAuthAccessToken')(function* ({
|
|
305
|
-
accessToken,
|
|
306
|
-
expiresAt,
|
|
307
|
-
refreshToken,
|
|
308
|
-
}: OAuthAuthMethod) {
|
|
309
|
-
const now = yield* DateTime.now
|
|
310
|
-
const refreshThreshold = refreshToken === undefined ? now : DateTime.addDuration(now, OAUTH_REFRESH_SKEW)
|
|
311
|
-
if (DateTime.lessThan(refreshThreshold, expiresAt)) {
|
|
312
|
-
return Option.some(Redacted.value(accessToken))
|
|
313
|
-
}
|
|
314
|
-
return Option.none<string>()
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
const refreshOAuthToken = Effect.fn('ApiClient.refreshOAuthToken')(function* ({
|
|
318
|
-
refreshToken,
|
|
319
|
-
expiresAt,
|
|
320
|
-
clientId,
|
|
321
|
-
}: OAuthAuthMethod) {
|
|
322
|
-
if (refreshToken === undefined) {
|
|
323
|
-
return yield* new OAuthTokenExpired({ expiredAt: expiresAt })
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const now = yield* DateTime.now
|
|
327
|
-
const token = yield* browserOAuth.refreshToken(Redacted.value(refreshToken), clientId)
|
|
328
|
-
if (token === undefined) {
|
|
329
|
-
return yield* new OAuthTokenExpired({ expiredAt: expiresAt })
|
|
330
|
-
}
|
|
331
|
-
const refreshedExpiresAt = DateTime.addDuration(now, token.expires_in)
|
|
332
|
-
const nextRefreshToken = token.refresh_token === undefined ? refreshToken : Redacted.make(token.refresh_token)
|
|
333
|
-
yield* configManager.updateConfig({
|
|
334
|
-
authMethod: OAuthConfig.make({
|
|
335
|
-
accessToken: Redacted.make(token.access_token),
|
|
336
|
-
refreshToken: nextRefreshToken,
|
|
337
|
-
expiresAt: refreshedExpiresAt,
|
|
338
|
-
clientId,
|
|
339
|
-
}),
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
return token.access_token
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
const oauthBearerToken = Effect.fn('ApiClient.oauthBearerToken')(function* (authMethod: OAuthAuthMethod) {
|
|
346
|
-
const alreadyFresh = yield* freshOAuthAccessToken(authMethod)
|
|
347
|
-
if (Option.isSome(alreadyFresh)) {
|
|
348
|
-
return alreadyFresh.value
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return yield* refreshLock.withPermits(1)(
|
|
352
|
-
Effect.gen(function* () {
|
|
353
|
-
const { authMethod: persistedAuthMethod } = yield* configManager.loadConfig()
|
|
354
|
-
if (persistedAuthMethod._tag !== 'OAuth') {
|
|
355
|
-
return yield* new OAuthAuthMethodChanged({})
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const persistedFresh = yield* freshOAuthAccessToken(persistedAuthMethod)
|
|
359
|
-
if (Option.isSome(persistedFresh)) {
|
|
360
|
-
return persistedFresh.value
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return yield* refreshOAuthToken(persistedAuthMethod)
|
|
364
|
-
}),
|
|
365
|
-
)
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
// Resolved per call, not at construction. Effect Layers are globally
|
|
369
|
-
// memoized, so capturing CurrentRequestHeaders here once would freeze
|
|
370
|
-
// auth resolution to whatever was on the first request. Reading per
|
|
371
|
-
// call also keeps the OAuth `expiresAt` check live against `DateTime.now`.
|
|
372
|
-
// `Effect.serviceOption` reads the tag from the outer fiber context
|
|
373
|
-
// without leaking it into our R, so the API's per-request
|
|
374
|
-
// `Context.make(CurrentRequestHeaders, headers)` flows through and the
|
|
375
|
-
// stdio MCP path simply gets `Option.none` → empty headers.
|
|
376
|
-
const buildAuthedClientOnce = () =>
|
|
377
|
-
Effect.gen(function* () {
|
|
378
|
-
const forwardedHeaders = yield* Effect.serviceOption(CurrentRequestHeaders).pipe(
|
|
379
|
-
Effect.map(Option.getOrElse(() => ({}) as Record<string, string>)),
|
|
380
|
-
)
|
|
381
|
-
// header keys are lowercase per WHATWG Headers spec.
|
|
382
|
-
const forwardedAuth = Option.fromNullable(forwardedHeaders['authorization'])
|
|
383
|
-
|
|
384
|
-
if (Option.isSome(forwardedAuth)) {
|
|
385
|
-
// Forwarded path: caller owns the token's lifecycle (their session,
|
|
386
|
-
// their bearer). We don't re-check expiry — if it's stale the API
|
|
387
|
-
// will return 401. `OAuthTokenExpired` is for the *persisted*
|
|
388
|
-
// OAuth in the loadConfig branch only.
|
|
389
|
-
const apiUrl = yield* configManager.getApiUrl()
|
|
390
|
-
const mapRequest = flow(
|
|
391
|
-
HttpClientRequest.prependUrl(apiUrl.toString()),
|
|
392
|
-
HttpClientRequest.setHeader('authorization', forwardedAuth.value),
|
|
393
|
-
)
|
|
394
|
-
return baseClient.pipe(HttpClient.mapRequest(mapRequest))
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const { authMethod, apiUrl } = yield* configManager.loadConfig()
|
|
398
|
-
const applyAuth = yield* Match.value(authMethod).pipe(
|
|
399
|
-
Match.tag('ApiKey', ({ apiKey }) =>
|
|
400
|
-
Effect.succeed(HttpClientRequest.setHeader('x-expand-api-key', Redacted.value(apiKey))),
|
|
401
|
-
),
|
|
402
|
-
Match.tag('OAuth', (authMethod) =>
|
|
403
|
-
oauthBearerToken(authMethod).pipe(Effect.map((accessToken) => HttpClientRequest.bearerToken(accessToken))),
|
|
404
|
-
),
|
|
405
|
-
Match.exhaustive,
|
|
406
|
-
)
|
|
407
|
-
const mapRequest = flow(HttpClientRequest.prependUrl(apiUrl.toString()), applyAuth)
|
|
408
|
-
return baseClient.pipe(HttpClient.mapRequest(mapRequest))
|
|
409
|
-
})
|
|
410
|
-
const buildAuthedClient = () =>
|
|
411
|
-
buildAuthedClientOnce().pipe(Effect.catchTag('OAuthAuthMethodChanged', () => buildAuthedClientOnce()))
|
|
412
|
-
const buildAuthed = () => buildAuthedClient().pipe(Effect.map(Generated.make))
|
|
413
|
-
const fetchMarkdown = (client: HttpClient.HttpClient, options: Parameters<Generated.Client['fetchFetch']>[0]) =>
|
|
414
|
-
HttpClientRequest.make('POST')('/v1/fetch').pipe(
|
|
415
|
-
HttpClientRequest.setUrlParams(options.params.include !== undefined ? { include: options.params.include } : {}),
|
|
416
|
-
(req) => Effect.orDie(HttpClientRequest.bodyJson(req, options.payload)),
|
|
417
|
-
Effect.flatMap((request) =>
|
|
418
|
-
Effect.flatMap(
|
|
419
|
-
client.execute(request),
|
|
420
|
-
HttpClientResponse.matchStatus({
|
|
421
|
-
'200': (response) => response.text,
|
|
422
|
-
'400': (response) => decodeError(response, Generated.FetchFetch400),
|
|
423
|
-
'401': (response) => decodeError(response, Generated.AuthFailed),
|
|
424
|
-
'403': (response) => decodeError(response, Generated.FetchFetch403),
|
|
425
|
-
'413': (response) => decodeError(response, Generated.PayloadTooLarge),
|
|
426
|
-
'429': (response) => decodeError(response, Generated.TooManyRequests),
|
|
427
|
-
'500': (response) => decodeError(response, Generated.InternalError),
|
|
428
|
-
'503': (response) => decodeError(response, Generated.ServiceUnavailable),
|
|
429
|
-
orElse: (response) => unexpectedStatus(request, response),
|
|
430
|
-
}),
|
|
431
|
-
),
|
|
432
|
-
),
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
return {
|
|
436
|
-
fetchFetch: (...args: Parameters<Generated.Client['fetchFetch']>) =>
|
|
437
|
-
buildAuthedClient().pipe(Effect.flatMap((client) => fetchMarkdown(client, ...args).pipe(liftApiError))),
|
|
438
|
-
fetchFetchResult: (...args: Parameters<Generated.Client['fetchFetchJson']>) =>
|
|
439
|
-
buildAuthedClient().pipe(Effect.flatMap((client) => fetchObjectModeJson(client, ...args).pipe(liftApiError))),
|
|
440
|
-
fetchFetchJson: (...args: Parameters<Generated.Client['fetchFetchJson']>) =>
|
|
441
|
-
buildAuthedClient().pipe(Effect.flatMap((client) => fetchObjectModeJson(client, ...args).pipe(liftApiError))),
|
|
442
|
-
fetchFetchSearch: (...args: Parameters<Generated.Client['fetchFetchSearch']>) =>
|
|
443
|
-
buildAuthedClient().pipe(Effect.flatMap((client) => fetchSnapshotSearch(client, ...args).pipe(liftApiError))),
|
|
444
|
-
fetchBatched: (...args: Parameters<Generated.Client['fetchBatched']>) =>
|
|
445
|
-
buildAuthed().pipe(Effect.flatMap((client) => client.fetchBatched(...args).pipe(liftApiError))),
|
|
446
|
-
fetchGetBatched: (...args: Parameters<Generated.Client['fetchGetBatched']>) =>
|
|
447
|
-
buildAuthed().pipe(Effect.flatMap((client) => client.fetchGetBatched(...args).pipe(liftApiError))),
|
|
448
|
-
} as const
|
|
449
|
-
}),
|
|
450
|
-
}) {}
|
package/src/Auth/Domain.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { Schema } from 'effect'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The OAuth client for browser-approved CLI setup. Distinct from the
|
|
5
|
-
* `expandai-cli` client that `expandai login` uses: that one keeps loopback
|
|
6
|
-
* redirect URIs and must keep showing a consent screen, because Better Auth
|
|
7
|
-
* matches loopback URIs without comparing the port.
|
|
8
|
-
*/
|
|
9
|
-
export const CLI_AUTHORIZATION_CLIENT_ID = 'expandai-cli-browser'
|
|
10
|
-
|
|
11
|
-
export const PendingInitKey = Schema.Struct({
|
|
12
|
-
apiOrigin: Schema.String,
|
|
13
|
-
clientId: Schema.Literal(CLI_AUTHORIZATION_CLIENT_ID),
|
|
14
|
-
harness: Schema.Literal('agent-harnesses'),
|
|
15
|
-
scope: Schema.Literal('project', 'global'),
|
|
16
|
-
canonicalScopeRoot: Schema.String,
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
export type PendingInitKey = typeof PendingInitKey.Type
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* One pending RFC 8628 device authorization. The `deviceCode` is the whole
|
|
23
|
-
* credential — better-auth binds it to our client id server-side and it never
|
|
24
|
-
* leaves this machine — so the record keeps the same 0600/no-symlink storage
|
|
25
|
-
* discipline the PKCE verifier had.
|
|
26
|
-
*/
|
|
27
|
-
export class PendingInitRecord extends Schema.Class<PendingInitRecord>('PendingInitRecord')({
|
|
28
|
-
key: PendingInitKey,
|
|
29
|
-
deviceCode: Schema.Redacted(Schema.String),
|
|
30
|
-
userCode: Schema.String,
|
|
31
|
-
verificationUri: Schema.String,
|
|
32
|
-
// RFC 8628 §3.5: current polling interval in seconds. `slow_down` adds five,
|
|
33
|
-
// permanently, so the backoff has to be persisted — each `init` invocation
|
|
34
|
-
// polls at most once and exits.
|
|
35
|
-
intervalSeconds: Schema.Number,
|
|
36
|
-
// Earliest wall-clock time the next poll may hit the network. An agent
|
|
37
|
-
// re-running `init` faster than the interval gets a local "still pending"
|
|
38
|
-
// instead of a request the server would answer with `slow_down`.
|
|
39
|
-
nextPollAt: Schema.DateTimeUtc,
|
|
40
|
-
approvalExpiresAt: Schema.DateTimeUtc,
|
|
41
|
-
}) {}
|
|
42
|
-
|
|
43
|
-
export class PendingInitCorrupt extends Schema.TaggedError<PendingInitCorrupt>()('PendingInitCorrupt', {
|
|
44
|
-
path: Schema.String,
|
|
45
|
-
error: Schema.Defect,
|
|
46
|
-
}) {
|
|
47
|
-
readonly recoverable = true
|
|
48
|
-
|
|
49
|
-
get message() {
|
|
50
|
-
return `Pending authorization state at ${this.path} is corrupt and was left unchanged.`
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class PendingInitStorageError extends Schema.TaggedError<PendingInitStorageError>()('PendingInitStorageError', {
|
|
55
|
-
path: Schema.String,
|
|
56
|
-
error: Schema.Defect,
|
|
57
|
-
}) {
|
|
58
|
-
readonly recoverable = true
|
|
59
|
-
|
|
60
|
-
get message() {
|
|
61
|
-
return `Could not access pending authorization state at ${this.path}.`
|
|
62
|
-
}
|
|
63
|
-
}
|