antpath 0.2.1 → 0.3.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 +55 -40
- package/dist/_shared/cleanup-policy.d.ts +8 -0
- package/dist/_shared/cleanup-policy.js +24 -0
- package/dist/_shared/config.d.ts +47 -0
- package/dist/_shared/config.js +150 -0
- package/dist/_shared/dev-stack.d.ts +19 -0
- package/dist/_shared/dev-stack.js +105 -0
- package/dist/_shared/errors.d.ts +8 -0
- package/dist/_shared/errors.js +18 -0
- package/dist/_shared/http.d.ts +20 -0
- package/dist/_shared/http.js +94 -0
- package/dist/_shared/index.d.ts +17 -0
- package/dist/_shared/index.js +20 -0
- package/dist/{providers → _shared}/known-events.d.ts +10 -10
- package/dist/{providers → _shared}/known-events.js +9 -9
- package/dist/_shared/operations.d.ts +19 -0
- package/dist/_shared/operations.js +42 -0
- package/dist/_shared/proxy-protocol.d.ts +148 -0
- package/dist/_shared/proxy-protocol.js +113 -0
- package/dist/_shared/proxy-validation.d.ts +19 -0
- package/dist/_shared/proxy-validation.js +51 -0
- package/dist/_shared/runtime-types.d.ts +90 -0
- package/dist/_shared/runtime-types.js +2 -0
- package/dist/{errors.d.ts → _shared/sdk-errors.d.ts} +10 -1
- package/dist/{errors.js → _shared/sdk-errors.js} +15 -2
- package/dist/{utils/secrets.js → _shared/sdk-secrets.js} +1 -1
- package/dist/_shared/secrets.d.ts +7 -0
- package/dist/_shared/secrets.js +20 -0
- package/dist/_shared/status.d.ts +8 -0
- package/dist/_shared/status.js +46 -0
- package/dist/_shared/submission.d.ts +142 -0
- package/dist/_shared/submission.js +681 -0
- package/dist/{template → _shared/template}/compiler.js +3 -3
- package/dist/{template/index.d.ts → _shared/template/helpers.d.ts} +0 -2
- package/dist/{template/index.js → _shared/template/helpers.js} +1 -2
- package/dist/_shared/template/index.d.ts +4 -0
- package/dist/_shared/template/index.js +4 -0
- package/dist/_shared/template/mapper.d.ts +11 -0
- package/dist/_shared/template/mapper.js +70 -0
- package/dist/cli.mjs +1234 -64
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +93 -8
- package/dist/client.js +192 -30
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +16 -10
- package/dist/index.js +16 -7
- package/dist/index.js.map +1 -1
- package/docs/cleanup.md +7 -4
- package/docs/credentials.md +12 -12
- package/docs/events.md +19 -82
- package/docs/outputs.md +15 -4
- package/docs/quickstart.md +42 -5
- package/docs/skills.md +1 -1
- package/docs/templates.md +1 -1
- package/docs/testing.md +11 -8
- package/examples/mcp-static-bearer.ts +14 -9
- package/examples/quickstart.ts +8 -6
- package/package.json +4 -5
- package/references/implementation-plan.md +1 -1
- package/dist/credentials.d.ts +0 -3
- package/dist/credentials.js +0 -56
- package/dist/credentials.js.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/files/downloader.d.ts +0 -3
- package/dist/files/downloader.js +0 -43
- package/dist/files/downloader.js.map +0 -1
- package/dist/platform/client.d.ts +0 -204
- package/dist/platform/client.js +0 -203
- package/dist/platform/client.js.map +0 -1
- package/dist/platform/index.d.ts +0 -1
- package/dist/platform/index.js +0 -2
- package/dist/platform/index.js.map +0 -1
- package/dist/providers/anthropic/provider.d.ts +0 -36
- package/dist/providers/anthropic/provider.js +0 -380
- package/dist/providers/anthropic/provider.js.map +0 -1
- package/dist/providers/known-events.js.map +0 -1
- package/dist/providers/types.d.ts +0 -42
- package/dist/providers/types.js.map +0 -1
- package/dist/run/controller.d.ts +0 -30
- package/dist/run/controller.js +0 -314
- package/dist/run/controller.js.map +0 -1
- package/dist/skills/packager.d.ts +0 -11
- package/dist/skills/packager.js +0 -76
- package/dist/skills/packager.js.map +0 -1
- package/dist/template/compiler.js.map +0 -1
- package/dist/template/index.js.map +0 -1
- package/dist/template/types.js +0 -2
- package/dist/template/types.js.map +0 -1
- package/dist/types.d.ts +0 -149
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/events.d.ts +0 -27
- package/dist/utils/events.js +0 -120
- package/dist/utils/events.js.map +0 -1
- package/dist/utils/paths.d.ts +0 -3
- package/dist/utils/paths.js +0 -27
- package/dist/utils/paths.js.map +0 -1
- package/dist/utils/secrets.js.map +0 -1
- package/dist/utils/stable.js.map +0 -1
- /package/dist/{utils/secrets.d.ts → _shared/sdk-secrets.d.ts} +0 -0
- /package/dist/{utils → _shared}/stable.d.ts +0 -0
- /package/dist/{utils → _shared}/stable.js +0 -0
- /package/dist/{template → _shared/template}/compiler.d.ts +0 -0
- /package/dist/{template → _shared/template}/types.d.ts +0 -0
- /package/dist/{providers → _shared/template}/types.js +0 -0
package/README.md
CHANGED
|
@@ -4,41 +4,48 @@ title: antpath
|
|
|
4
4
|
|
|
5
5
|
# antpath
|
|
6
6
|
|
|
7
|
-
antpath is a TypeScript-first SDK for running autonomous Claude Managed Agents sessions
|
|
8
|
-
|
|
9
|
-
Everything ships from a single import path:
|
|
7
|
+
antpath is a TypeScript-first SDK + CLI for running autonomous Claude Managed Agents sessions through a managed antpath dashboard. Everything an agent or human needs is reachable through **one** import and **one** binary.
|
|
10
8
|
|
|
11
9
|
```ts
|
|
12
10
|
import {
|
|
13
|
-
AntpathClient,
|
|
14
|
-
AntpathPlatformClient, // submit durable runs to an antpath dashboard
|
|
11
|
+
AntpathClient, // the only public class — submits durable runs to a dashboard
|
|
15
12
|
defineTemplate,
|
|
16
13
|
string,
|
|
17
|
-
validateProxyAuth
|
|
14
|
+
validateProxyAuth // helper for per-run proxy endpoint auth
|
|
18
15
|
} from "antpath";
|
|
19
16
|
```
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
```bash
|
|
19
|
+
antpath run ./template.json --api-token ant_… --workspace … --dashboard-url … \
|
|
20
|
+
--anthropic-api-key sk-ant-… --follow
|
|
21
|
+
antpath status <run-id> --api-token … --workspace … --dashboard-url …
|
|
22
|
+
antpath events <run-id> --api-token … --workspace … --dashboard-url … --follow
|
|
23
|
+
antpath outputs <run-id> --api-token … --workspace … --dashboard-url …
|
|
24
|
+
antpath download <run-id> <output-id> --out ./local --api-token … --workspace … --dashboard-url …
|
|
25
|
+
antpath cancel <run-id> --api-token … --workspace … --dashboard-url …
|
|
26
|
+
antpath delete <run-id> --api-token … --workspace … --dashboard-url …
|
|
27
|
+
antpath whoami --api-token … --dashboard-url …
|
|
28
|
+
```
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
- **Platform (`AntpathPlatformClient`)** — submits durable runs to an antpath dashboard. Every submission carries an inline `secrets` bundle (Anthropic key, optional MCP credentials, optional skill references, and optional per-run proxy endpoint auth); the dashboard vaults the bundle for the lifetime of one run and deletes it at cleanup. Per-run named secrets accessed through the managed HTTP proxy are declared via the `proxyEndpoints` submission field and authenticated via `secrets.proxyEndpointAuth` — see [Credentials](docs/credentials.md). The same npm package also ships the `antpath` CLI as its `bin` entry; the worker mounts that CLI at `/antpath/antpath` in every run for skills to invoke (`node /antpath/antpath proxy …`).
|
|
30
|
+
The SDK class and the CLI are backed by the same `@antpath/shared` operations module — any read or write you can do through one, you can do through the other, against the same durable run records. The same npm package also ships the in-container `antpath` CLI as its `bin` entry; the worker mounts that CLI at `/antpath/antpath` inside every run so skills can call `antpath proxy …` against the per-run manifest. See [Agent-first surface design](../../references/development-principles.md#agent-first-surface-design).
|
|
27
31
|
|
|
28
|
-
## MVP boundaries
|
|
32
|
+
## MVP boundaries
|
|
29
33
|
|
|
30
34
|
- Claude Managed Agents only.
|
|
31
|
-
-
|
|
35
|
+
- BYO Anthropic key + MCP credentials + skill references — passed inline on every submission, vaulted for the lifetime of a single run, destroyed at cleanup.
|
|
36
|
+
- Workspace is the tenant boundary. Every operation is scoped to the workspace bound at client construction.
|
|
32
37
|
- No SDK-side storage of provider keys, MCP credentials, or output file contents.
|
|
33
|
-
-
|
|
38
|
+
- Cleanup runs by default; opt into retention with `cleanup.session: "retain"`.
|
|
34
39
|
|
|
35
|
-
## Quickstart
|
|
40
|
+
## Quickstart (SDK)
|
|
36
41
|
|
|
37
42
|
```ts
|
|
38
43
|
import { AntpathClient, defineTemplate, string } from "antpath";
|
|
39
44
|
|
|
40
45
|
const client = new AntpathClient({
|
|
41
|
-
|
|
46
|
+
baseUrl: "https://antpath.example.com",
|
|
47
|
+
apiToken: process.env.ANTPATH_API_TOKEN!,
|
|
48
|
+
workspaceId: process.env.ANTPATH_WORKSPACE_ID!
|
|
42
49
|
});
|
|
43
50
|
|
|
44
51
|
const template = defineTemplate({
|
|
@@ -46,47 +53,54 @@ const template = defineTemplate({
|
|
|
46
53
|
model: "claude-haiku-4-5",
|
|
47
54
|
system: "You are a concise automation agent.",
|
|
48
55
|
messages: ["Write a short answer about {{topic}}."],
|
|
49
|
-
variables: {
|
|
50
|
-
topic: string()
|
|
51
|
-
}
|
|
56
|
+
variables: { topic: string() }
|
|
52
57
|
});
|
|
53
58
|
|
|
54
|
-
const
|
|
55
|
-
variables: { topic: "
|
|
59
|
+
const ref = await client.submitRun(template, {
|
|
60
|
+
variables: { topic: "agent-first SDK design" },
|
|
61
|
+
secrets: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! } }
|
|
56
62
|
});
|
|
57
63
|
|
|
58
|
-
const
|
|
59
|
-
console.log(
|
|
60
|
-
|
|
61
|
-
await
|
|
64
|
+
const run = await ref.wait();
|
|
65
|
+
console.log(run.status);
|
|
66
|
+
|
|
67
|
+
for (const output of await ref.outputs()) {
|
|
68
|
+
console.log(output.id, output.path);
|
|
69
|
+
}
|
|
62
70
|
```
|
|
63
71
|
|
|
64
|
-
|
|
72
|
+
Stream events live with `ref.stream()`:
|
|
65
73
|
|
|
66
74
|
```ts
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// event.event is a ProviderEvent (raw type stays `unknown`).
|
|
71
|
-
}
|
|
75
|
+
for await (const event of ref.stream()) {
|
|
76
|
+
if (event.type === "agent.message") {
|
|
77
|
+
// typed event helpers live under `antpath`'s event guard exports.
|
|
72
78
|
}
|
|
73
|
-
}
|
|
74
|
-
await handle.wait();
|
|
79
|
+
}
|
|
75
80
|
```
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
The same flow from the CLI:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
antpath run ./template.json \
|
|
86
|
+
--api-token "$ANTPATH_API_TOKEN" \
|
|
87
|
+
--workspace "$ANTPATH_WORKSPACE_ID" \
|
|
88
|
+
--dashboard-url https://antpath.example.com \
|
|
89
|
+
--anthropic-api-key "$ANTHROPIC_API_KEY" \
|
|
90
|
+
--var topic="agent-first SDK design" \
|
|
91
|
+
--follow
|
|
92
|
+
```
|
|
80
93
|
|
|
81
94
|
## Test commands
|
|
82
95
|
|
|
83
96
|
```text
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
pnpm test # unit (deterministic; uses fakes/snapshots)
|
|
98
|
+
pnpm test:e2e:live # full top-to-bottom against a real dashboard + Anthropic
|
|
99
|
+
pnpm test:user # offline; exercises the published package via npm install
|
|
100
|
+
pnpm test:user:live # live; same but hits the real platform
|
|
87
101
|
```
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
`pnpm test:user*` requires `ANTPATH_USER_TEST_TARBALL`/`ANTPATH_USER_TEST_VERSION` (+ live target vars for `:live`). Missing required env is a hard error — there is no silent skip.
|
|
90
104
|
|
|
91
105
|
## Guides
|
|
92
106
|
|
|
@@ -100,3 +114,4 @@ Unit tests are deterministic and may use fakes or sanitized recorded snapshots.
|
|
|
100
114
|
- [Cleanup](docs/cleanup.md)
|
|
101
115
|
- [Testing](docs/testing.md)
|
|
102
116
|
- [Release](docs/release.md)
|
|
117
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral cleanup-policy resolution.
|
|
3
|
+
*
|
|
4
|
+
* Reads `cleanup.session` (canonical) and falls back to legacy `cleanup.claudeSession`.
|
|
5
|
+
* Returns the worker default for any malformed shape — never throws.
|
|
6
|
+
*/
|
|
7
|
+
export type SessionCleanupPolicy = "retain" | "delete";
|
|
8
|
+
export declare function resolveCleanupPolicyFromSnapshot(snapshot: unknown, fallback: SessionCleanupPolicy): SessionCleanupPolicy;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral cleanup-policy resolution.
|
|
3
|
+
*
|
|
4
|
+
* Reads `cleanup.session` (canonical) and falls back to legacy `cleanup.claudeSession`.
|
|
5
|
+
* Returns the worker default for any malformed shape — never throws.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveCleanupPolicyFromSnapshot(snapshot, fallback) {
|
|
8
|
+
if (snapshot === null || snapshot === undefined || typeof snapshot !== "object" || Array.isArray(snapshot)) {
|
|
9
|
+
return fallback;
|
|
10
|
+
}
|
|
11
|
+
const cleanup = snapshot.cleanup;
|
|
12
|
+
if (!cleanup || typeof cleanup !== "object" || Array.isArray(cleanup)) {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
const policy = cleanup;
|
|
16
|
+
if (policy.session === "retain" || policy.session === "delete") {
|
|
17
|
+
return policy.session;
|
|
18
|
+
}
|
|
19
|
+
if (policy.claudeSession === "retain" || policy.claudeSession === "delete") {
|
|
20
|
+
return policy.claudeSession;
|
|
21
|
+
}
|
|
22
|
+
return fallback;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=cleanup-policy.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface PlatformRequiredConfig {
|
|
2
|
+
readonly databaseUrl: string;
|
|
3
|
+
readonly supabaseUrl: string;
|
|
4
|
+
readonly supabaseServiceRoleKey: string;
|
|
5
|
+
readonly supabaseStorageBucket: string;
|
|
6
|
+
readonly supabaseVaultSchema: string;
|
|
7
|
+
readonly authSecret: string;
|
|
8
|
+
readonly sdkTokenPepper: string;
|
|
9
|
+
readonly workerId: string;
|
|
10
|
+
readonly providerApiBaseUrl: string;
|
|
11
|
+
readonly providerApiVersion: string;
|
|
12
|
+
readonly proxyPublicBaseUrl: string;
|
|
13
|
+
readonly proxyTokenPepper: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PlatformProxyConfig {
|
|
16
|
+
readonly rateLimitPerRunPerMinute: number;
|
|
17
|
+
readonly defaultResponseByteBudget: number;
|
|
18
|
+
readonly sweeperIntervalMs: number;
|
|
19
|
+
}
|
|
20
|
+
export interface PlatformCaps {
|
|
21
|
+
readonly maxRunDurationMs: number;
|
|
22
|
+
readonly maxActiveRunsPerWorkspace: number;
|
|
23
|
+
readonly maxActiveRunsPerUserOrToken: number;
|
|
24
|
+
readonly pollingBaseIntervalMs: number;
|
|
25
|
+
readonly pollingMaxIntervalMs: number;
|
|
26
|
+
readonly pollingJitterRatio: number;
|
|
27
|
+
readonly providerCreateTokensPerMinute: number;
|
|
28
|
+
readonly providerDeleteTokensPerMinute: number;
|
|
29
|
+
readonly providerPollTokensPerMinute: number;
|
|
30
|
+
readonly providerRetryBackoffMs: number;
|
|
31
|
+
readonly leaseDurationMs: number;
|
|
32
|
+
readonly leaseRenewalThresholdMs: number;
|
|
33
|
+
readonly maxProviderAttempts: number;
|
|
34
|
+
readonly cleanupRetryCount: number;
|
|
35
|
+
readonly cleanupRetryBackoffMs: number;
|
|
36
|
+
readonly outputDownloadSafetyCapBytes: number;
|
|
37
|
+
readonly workspaceStorageCapBytes: number;
|
|
38
|
+
readonly signedUrlTtlSeconds: number;
|
|
39
|
+
}
|
|
40
|
+
export interface PlatformConfig extends PlatformRequiredConfig {
|
|
41
|
+
readonly caps: PlatformCaps;
|
|
42
|
+
readonly proxy: PlatformProxyConfig;
|
|
43
|
+
}
|
|
44
|
+
type Env = Record<string, string | undefined>;
|
|
45
|
+
export declare function parsePlatformConfig(env: Env): PlatformConfig;
|
|
46
|
+
export declare function snapshotRunCaps(config: PlatformConfig): PlatformCaps;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const DEFAULT_CAPS = {
|
|
2
|
+
maxRunDurationMs: 5 * 60 * 1000,
|
|
3
|
+
maxActiveRunsPerWorkspace: 1,
|
|
4
|
+
maxActiveRunsPerUserOrToken: 1,
|
|
5
|
+
pollingBaseIntervalMs: 5_000,
|
|
6
|
+
pollingMaxIntervalMs: 60_000,
|
|
7
|
+
pollingJitterRatio: 0.2,
|
|
8
|
+
providerCreateTokensPerMinute: 20,
|
|
9
|
+
providerDeleteTokensPerMinute: 30,
|
|
10
|
+
providerPollTokensPerMinute: 60,
|
|
11
|
+
providerRetryBackoffMs: 5_000,
|
|
12
|
+
leaseDurationMs: 60_000,
|
|
13
|
+
leaseRenewalThresholdMs: 20_000,
|
|
14
|
+
maxProviderAttempts: 1,
|
|
15
|
+
cleanupRetryCount: 3,
|
|
16
|
+
cleanupRetryBackoffMs: 10_000,
|
|
17
|
+
outputDownloadSafetyCapBytes: 1024 * 1024 * 1024,
|
|
18
|
+
workspaceStorageCapBytes: 5 * 1024 * 1024 * 1024,
|
|
19
|
+
signedUrlTtlSeconds: 300
|
|
20
|
+
};
|
|
21
|
+
const requiredEnvNames = [
|
|
22
|
+
"ANTPATH_DATABASE_URL",
|
|
23
|
+
"ANTPATH_SUPABASE_URL",
|
|
24
|
+
"ANTPATH_SUPABASE_SERVICE_ROLE_KEY",
|
|
25
|
+
"ANTPATH_SUPABASE_STORAGE_BUCKET",
|
|
26
|
+
"ANTPATH_SUPABASE_VAULT_SCHEMA",
|
|
27
|
+
"ANTPATH_AUTH_SECRET",
|
|
28
|
+
"ANTPATH_SDK_TOKEN_PEPPER",
|
|
29
|
+
"ANTPATH_WORKER_ID",
|
|
30
|
+
"ANTPATH_PROVIDER_API_BASE_URL",
|
|
31
|
+
"ANTPATH_PROVIDER_API_VERSION",
|
|
32
|
+
"ANTPATH_PROXY_PUBLIC_BASE_URL",
|
|
33
|
+
"ANTPATH_PROXY_TOKEN_PEPPER"
|
|
34
|
+
];
|
|
35
|
+
const DEFAULT_PROXY_RATE_LIMIT_PER_RUN_PER_MINUTE = 60;
|
|
36
|
+
const DEFAULT_PROXY_RESPONSE_BYTE_BUDGET = 1_048_576;
|
|
37
|
+
const DEFAULT_PROXY_SWEEPER_INTERVAL_MS = 60_000;
|
|
38
|
+
export function parsePlatformConfig(env) {
|
|
39
|
+
const missing = requiredEnvNames.filter((name) => !env[name]);
|
|
40
|
+
if (missing.length > 0) {
|
|
41
|
+
throw new Error(`Missing required environment variables: ${missing.join(", ")}`);
|
|
42
|
+
}
|
|
43
|
+
const caps = {
|
|
44
|
+
maxRunDurationMs: optionalPositiveInt(env, "ANTPATH_MAX_RUN_DURATION_MS", DEFAULT_CAPS.maxRunDurationMs),
|
|
45
|
+
maxActiveRunsPerWorkspace: optionalPositiveInt(env, "ANTPATH_MAX_ACTIVE_RUNS_PER_WORKSPACE", DEFAULT_CAPS.maxActiveRunsPerWorkspace),
|
|
46
|
+
maxActiveRunsPerUserOrToken: optionalPositiveInt(env, "ANTPATH_MAX_ACTIVE_RUNS_PER_USER_OR_TOKEN", DEFAULT_CAPS.maxActiveRunsPerUserOrToken),
|
|
47
|
+
pollingBaseIntervalMs: optionalPositiveInt(env, "ANTPATH_POLLING_BASE_INTERVAL_MS", DEFAULT_CAPS.pollingBaseIntervalMs),
|
|
48
|
+
pollingMaxIntervalMs: optionalPositiveInt(env, "ANTPATH_POLLING_MAX_INTERVAL_MS", DEFAULT_CAPS.pollingMaxIntervalMs),
|
|
49
|
+
pollingJitterRatio: optionalRatio(env, "ANTPATH_POLLING_JITTER_RATIO", DEFAULT_CAPS.pollingJitterRatio),
|
|
50
|
+
providerCreateTokensPerMinute: optionalPositiveInt(env, "ANTPATH_PROVIDER_CREATE_TOKENS_PER_MINUTE", DEFAULT_CAPS.providerCreateTokensPerMinute),
|
|
51
|
+
providerDeleteTokensPerMinute: optionalPositiveInt(env, "ANTPATH_PROVIDER_DELETE_TOKENS_PER_MINUTE", DEFAULT_CAPS.providerDeleteTokensPerMinute),
|
|
52
|
+
providerPollTokensPerMinute: optionalPositiveInt(env, "ANTPATH_PROVIDER_POLL_TOKENS_PER_MINUTE", DEFAULT_CAPS.providerPollTokensPerMinute),
|
|
53
|
+
providerRetryBackoffMs: optionalPositiveInt(env, "ANTPATH_PROVIDER_RETRY_BACKOFF_MS", DEFAULT_CAPS.providerRetryBackoffMs),
|
|
54
|
+
leaseDurationMs: optionalPositiveInt(env, "ANTPATH_LEASE_DURATION_MS", DEFAULT_CAPS.leaseDurationMs),
|
|
55
|
+
leaseRenewalThresholdMs: optionalPositiveInt(env, "ANTPATH_LEASE_RENEWAL_THRESHOLD_MS", DEFAULT_CAPS.leaseRenewalThresholdMs),
|
|
56
|
+
maxProviderAttempts: optionalPositiveInt(env, "ANTPATH_MAX_PROVIDER_ATTEMPTS", DEFAULT_CAPS.maxProviderAttempts),
|
|
57
|
+
cleanupRetryCount: optionalNonNegativeInt(env, "ANTPATH_CLEANUP_RETRY_COUNT", DEFAULT_CAPS.cleanupRetryCount),
|
|
58
|
+
cleanupRetryBackoffMs: optionalPositiveInt(env, "ANTPATH_CLEANUP_RETRY_BACKOFF_MS", DEFAULT_CAPS.cleanupRetryBackoffMs),
|
|
59
|
+
outputDownloadSafetyCapBytes: optionalPositiveInt(env, "ANTPATH_OUTPUT_DOWNLOAD_SAFETY_CAP_BYTES", DEFAULT_CAPS.outputDownloadSafetyCapBytes),
|
|
60
|
+
workspaceStorageCapBytes: optionalPositiveInt(env, "ANTPATH_WORKSPACE_STORAGE_CAP_BYTES", DEFAULT_CAPS.workspaceStorageCapBytes),
|
|
61
|
+
signedUrlTtlSeconds: optionalPositiveInt(env, "ANTPATH_SIGNED_URL_TTL_SECONDS", DEFAULT_CAPS.signedUrlTtlSeconds)
|
|
62
|
+
};
|
|
63
|
+
validateCaps(caps);
|
|
64
|
+
const proxy = {
|
|
65
|
+
rateLimitPerRunPerMinute: optionalPositiveInt(env, "ANTPATH_PROXY_RATE_LIMIT_PER_RUN_PER_MINUTE", DEFAULT_PROXY_RATE_LIMIT_PER_RUN_PER_MINUTE),
|
|
66
|
+
defaultResponseByteBudget: optionalPositiveInt(env, "ANTPATH_PROXY_DEFAULT_RESPONSE_BYTE_BUDGET", DEFAULT_PROXY_RESPONSE_BYTE_BUDGET),
|
|
67
|
+
sweeperIntervalMs: optionalPositiveInt(env, "ANTPATH_PROXY_SWEEPER_INTERVAL_MS", DEFAULT_PROXY_SWEEPER_INTERVAL_MS)
|
|
68
|
+
};
|
|
69
|
+
const proxyPublicBaseUrl = requireProxyBaseUrl(env, "ANTPATH_PROXY_PUBLIC_BASE_URL");
|
|
70
|
+
return {
|
|
71
|
+
databaseUrl: requireEnv(env, "ANTPATH_DATABASE_URL"),
|
|
72
|
+
supabaseUrl: requireEnv(env, "ANTPATH_SUPABASE_URL"),
|
|
73
|
+
supabaseServiceRoleKey: requireEnv(env, "ANTPATH_SUPABASE_SERVICE_ROLE_KEY"),
|
|
74
|
+
supabaseStorageBucket: requireEnv(env, "ANTPATH_SUPABASE_STORAGE_BUCKET"),
|
|
75
|
+
supabaseVaultSchema: requireEnv(env, "ANTPATH_SUPABASE_VAULT_SCHEMA"),
|
|
76
|
+
authSecret: requireEnv(env, "ANTPATH_AUTH_SECRET"),
|
|
77
|
+
sdkTokenPepper: requireEnv(env, "ANTPATH_SDK_TOKEN_PEPPER"),
|
|
78
|
+
workerId: requireEnv(env, "ANTPATH_WORKER_ID"),
|
|
79
|
+
providerApiBaseUrl: requireEnv(env, "ANTPATH_PROVIDER_API_BASE_URL"),
|
|
80
|
+
providerApiVersion: requireEnv(env, "ANTPATH_PROVIDER_API_VERSION"),
|
|
81
|
+
proxyPublicBaseUrl,
|
|
82
|
+
proxyTokenPepper: requireEnv(env, "ANTPATH_PROXY_TOKEN_PEPPER"),
|
|
83
|
+
caps,
|
|
84
|
+
proxy
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function snapshotRunCaps(config) {
|
|
88
|
+
return { ...config.caps };
|
|
89
|
+
}
|
|
90
|
+
function requireEnv(env, name) {
|
|
91
|
+
const value = env[name];
|
|
92
|
+
if (!value) {
|
|
93
|
+
throw new Error(`Missing required environment variable: ${name}`);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function optionalPositiveInt(env, name, fallback) {
|
|
98
|
+
return optionalInt(env, name, fallback, 1);
|
|
99
|
+
}
|
|
100
|
+
function optionalNonNegativeInt(env, name, fallback) {
|
|
101
|
+
return optionalInt(env, name, fallback, 0);
|
|
102
|
+
}
|
|
103
|
+
function optionalInt(env, name, fallback, minimum) {
|
|
104
|
+
const raw = env[name];
|
|
105
|
+
if (raw === undefined || raw === "") {
|
|
106
|
+
return fallback;
|
|
107
|
+
}
|
|
108
|
+
const value = Number(raw);
|
|
109
|
+
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
110
|
+
throw new Error(`${name} must be a safe integer greater than or equal to ${minimum}`);
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function optionalRatio(env, name, fallback) {
|
|
115
|
+
const raw = env[name];
|
|
116
|
+
if (raw === undefined || raw === "") {
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
const value = Number(raw);
|
|
120
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
121
|
+
throw new Error(`${name} must be a number between 0 and 1`);
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function validateCaps(caps) {
|
|
126
|
+
if (caps.leaseRenewalThresholdMs >= caps.leaseDurationMs) {
|
|
127
|
+
throw new Error("ANTPATH_LEASE_RENEWAL_THRESHOLD_MS must be less than ANTPATH_LEASE_DURATION_MS");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function requireProxyBaseUrl(env, name) {
|
|
131
|
+
const raw = requireEnv(env, name);
|
|
132
|
+
let parsed;
|
|
133
|
+
try {
|
|
134
|
+
parsed = new URL(raw);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new Error(`${name} must be a valid absolute URL`);
|
|
138
|
+
}
|
|
139
|
+
if (parsed.protocol !== "https:") {
|
|
140
|
+
throw new Error(`${name} must use https://`);
|
|
141
|
+
}
|
|
142
|
+
if (parsed.username || parsed.password) {
|
|
143
|
+
throw new Error(`${name} must not embed credentials`);
|
|
144
|
+
}
|
|
145
|
+
if (parsed.search || parsed.hash) {
|
|
146
|
+
throw new Error(`${name} must not include a query string or fragment`);
|
|
147
|
+
}
|
|
148
|
+
return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DevStackConfig {
|
|
2
|
+
readonly supabaseUrl: string;
|
|
3
|
+
readonly supabaseDbUrl: string;
|
|
4
|
+
readonly supabaseServiceRoleKey: string;
|
|
5
|
+
readonly authSecret: string;
|
|
6
|
+
readonly sdkTokenPepper: string;
|
|
7
|
+
readonly storageBucket: string;
|
|
8
|
+
readonly authProvider: "email" | "google" | "none";
|
|
9
|
+
readonly dashboardPort: number;
|
|
10
|
+
readonly workerPort: number;
|
|
11
|
+
readonly workerId: string;
|
|
12
|
+
}
|
|
13
|
+
export type AntpathRole = "worker" | "dashboard";
|
|
14
|
+
type Env = Record<string, string | undefined>;
|
|
15
|
+
export declare function collectRoleEnvErrors(env: Env, role: AntpathRole): readonly string[];
|
|
16
|
+
export declare function assertRoleEnv(env: Env, role: AntpathRole): void;
|
|
17
|
+
export declare function parseDevStackConfig(env: Env): DevStackConfig;
|
|
18
|
+
export declare function collectDevStackConfigErrors(env: Env): readonly string[];
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const ROLE_REQUIREMENTS = {
|
|
2
|
+
worker: [
|
|
3
|
+
{ names: ["SUPABASE_DB_URL", "DATABASE_URL", "DB_URL"], label: "Supabase database URL" },
|
|
4
|
+
{ names: ["ANTPATH_WORKER_ID"], label: "worker ID" },
|
|
5
|
+
{ names: ["ANTPATH_PROXY_PUBLIC_BASE_URL"], label: "antpath proxy public base URL" }
|
|
6
|
+
],
|
|
7
|
+
dashboard: [
|
|
8
|
+
{ names: ["SUPABASE_DB_URL", "DATABASE_URL", "DB_URL"], label: "Supabase database URL" },
|
|
9
|
+
{ names: ["SUPABASE_URL", "API_URL"], label: "Supabase API URL" },
|
|
10
|
+
{ names: ["SUPABASE_SECRET_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SERVICE_ROLE_KEY", "SECRET_KEY"], label: "Supabase secret key" },
|
|
11
|
+
{ names: ["AUTH_SECRET"], label: "Auth.js secret" },
|
|
12
|
+
{ names: ["ANTPATH_SDK_TOKEN_PEPPER"], label: "antpath SDK token pepper" },
|
|
13
|
+
{ names: ["ANTPATH_PROXY_PUBLIC_BASE_URL"], label: "antpath proxy public base URL" },
|
|
14
|
+
{ names: ["ANTPATH_PROXY_TOKEN_PEPPER"], label: "antpath proxy token pepper" }
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
export function collectRoleEnvErrors(env, role) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
for (const requirement of ROLE_REQUIREMENTS[role]) {
|
|
20
|
+
if (!requirement.names.some((name) => Boolean(env[name]))) {
|
|
21
|
+
errors.push(`missing ${requirement.label}: set one of ${requirement.names.join(", ")}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return errors;
|
|
25
|
+
}
|
|
26
|
+
export function assertRoleEnv(env, role) {
|
|
27
|
+
const errors = collectRoleEnvErrors(env, role);
|
|
28
|
+
if (errors.length > 0) {
|
|
29
|
+
throw new Error(`Missing required environment variables for antpath ${role}:\n${errors.map((error) => ` - ${error}`).join("\n")}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function parseDevStackConfig(env) {
|
|
33
|
+
const errors = collectDevStackConfigErrors(env);
|
|
34
|
+
if (errors.length > 0) {
|
|
35
|
+
throw new Error(`Invalid dev stack configuration:\n${errors.map((error) => `- ${error}`).join("\n")}`);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
supabaseUrl: requireOne(env, ["SUPABASE_URL", "API_URL"]),
|
|
39
|
+
supabaseDbUrl: requireOne(env, ["SUPABASE_DB_URL", "DATABASE_URL", "DB_URL"]),
|
|
40
|
+
supabaseServiceRoleKey: requireOne(env, ["SUPABASE_SECRET_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SERVICE_ROLE_KEY", "SECRET_KEY"]),
|
|
41
|
+
authSecret: requireOne(env, ["AUTH_SECRET"]),
|
|
42
|
+
sdkTokenPepper: requireOne(env, ["ANTPATH_SDK_TOKEN_PEPPER"]),
|
|
43
|
+
storageBucket: env.ANTPATH_SUPABASE_STORAGE_BUCKET ?? "antpath-outputs",
|
|
44
|
+
authProvider: authProviderFromEnv(env),
|
|
45
|
+
dashboardPort: optionalPort(env, "ANTPATH_DASHBOARD_PORT", 3000),
|
|
46
|
+
workerPort: optionalPort(env, "ANTPATH_WORKER_PORT", optionalPort(env, "PORT", 8787)),
|
|
47
|
+
workerId: env.ANTPATH_WORKER_ID ?? "local-worker"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function collectDevStackConfigErrors(env) {
|
|
51
|
+
const errors = [];
|
|
52
|
+
requireAny(errors, env, ["SUPABASE_URL", "API_URL"], "local Supabase API URL");
|
|
53
|
+
requireAny(errors, env, ["SUPABASE_DB_URL", "DATABASE_URL", "DB_URL"], "local Supabase database URL");
|
|
54
|
+
requireAny(errors, env, ["SUPABASE_SECRET_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SERVICE_ROLE_KEY", "SECRET_KEY"], "local Supabase secret key");
|
|
55
|
+
requireAny(errors, env, ["AUTH_SECRET"], "Auth.js secret");
|
|
56
|
+
requireAny(errors, env, ["ANTPATH_SDK_TOKEN_PEPPER"], "SDK token pepper");
|
|
57
|
+
if (env.ANTPATH_DEV_STACK_REQUIRE_AUTH_PROVIDER === "1" && authProviderFromEnv(env) === "none") {
|
|
58
|
+
errors.push("configure either AUTH_GOOGLE_ID/AUTH_GOOGLE_SECRET or AUTH_POSTMARK_KEY/AUTH_EMAIL_FROM");
|
|
59
|
+
}
|
|
60
|
+
validatePort(errors, env, "ANTPATH_DASHBOARD_PORT");
|
|
61
|
+
validatePort(errors, env, "ANTPATH_WORKER_PORT");
|
|
62
|
+
validatePort(errors, env, "PORT");
|
|
63
|
+
return errors;
|
|
64
|
+
}
|
|
65
|
+
function requireAny(errors, env, names, label) {
|
|
66
|
+
if (!names.some((name) => Boolean(env[name]))) {
|
|
67
|
+
errors.push(`missing ${label}: set one of ${names.join(", ")}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function requireOne(env, names) {
|
|
71
|
+
for (const name of names) {
|
|
72
|
+
const value = env[name];
|
|
73
|
+
if (value) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Missing required environment variable: ${names.join(" or ")}`);
|
|
78
|
+
}
|
|
79
|
+
function hasAll(env, names) {
|
|
80
|
+
return names.every((name) => Boolean(env[name]));
|
|
81
|
+
}
|
|
82
|
+
function authProviderFromEnv(env) {
|
|
83
|
+
if (hasAll(env, ["AUTH_GOOGLE_ID", "AUTH_GOOGLE_SECRET"])) {
|
|
84
|
+
return "google";
|
|
85
|
+
}
|
|
86
|
+
if (hasAll(env, ["AUTH_POSTMARK_KEY", "AUTH_EMAIL_FROM"])) {
|
|
87
|
+
return "email";
|
|
88
|
+
}
|
|
89
|
+
return "none";
|
|
90
|
+
}
|
|
91
|
+
function optionalPort(env, name, fallback) {
|
|
92
|
+
const value = env[name];
|
|
93
|
+
return value ? Number(value) : fallback;
|
|
94
|
+
}
|
|
95
|
+
function validatePort(errors, env, name) {
|
|
96
|
+
const raw = env[name];
|
|
97
|
+
if (!raw) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const value = Number(raw);
|
|
101
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 65_535) {
|
|
102
|
+
errors.push(`${name} must be an integer port between 1 and 65535`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=dev-stack.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const ERROR_CLASSES: readonly ["transient_provider", "provider_permanent", "tenant_permanent", "antpath_bug", "cancelled_by_user"];
|
|
2
|
+
export type ErrorClass = typeof ERROR_CLASSES[number];
|
|
3
|
+
export interface ErrorPolicy {
|
|
4
|
+
readonly retryable: boolean;
|
|
5
|
+
readonly terminal: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const ERROR_POLICIES: Record<ErrorClass, ErrorPolicy>;
|
|
8
|
+
export declare function isRetryableErrorClass(errorClass: ErrorClass): boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const ERROR_CLASSES = [
|
|
2
|
+
"transient_provider",
|
|
3
|
+
"provider_permanent",
|
|
4
|
+
"tenant_permanent",
|
|
5
|
+
"antpath_bug",
|
|
6
|
+
"cancelled_by_user"
|
|
7
|
+
];
|
|
8
|
+
export const ERROR_POLICIES = {
|
|
9
|
+
transient_provider: { retryable: true, terminal: false },
|
|
10
|
+
provider_permanent: { retryable: false, terminal: true },
|
|
11
|
+
tenant_permanent: { retryable: false, terminal: true },
|
|
12
|
+
antpath_bug: { retryable: false, terminal: true },
|
|
13
|
+
cancelled_by_user: { retryable: false, terminal: true }
|
|
14
|
+
};
|
|
15
|
+
export function isRetryableErrorClass(errorClass) {
|
|
16
|
+
return ERROR_POLICIES[errorClass].retryable;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
2
|
+
export interface HttpClientOptions {
|
|
3
|
+
readonly baseUrl: string;
|
|
4
|
+
readonly apiToken: string;
|
|
5
|
+
readonly fetch?: FetchLike;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Thin transport used by every BFF-bound operation. The SDK class and
|
|
9
|
+
* the CLI subcommands BOTH build an `HttpClient` and pass it to the
|
|
10
|
+
* operations module — so they cannot drift in how they auth, encode
|
|
11
|
+
* query parameters, or decode error responses.
|
|
12
|
+
*/
|
|
13
|
+
export declare class HttpClient {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(options: HttpClientOptions);
|
|
16
|
+
request<T>(path: string, init?: RequestInit, query?: Record<string, string>): Promise<T>;
|
|
17
|
+
download(path: string, init?: RequestInit, query?: Record<string, string>): Promise<{
|
|
18
|
+
readonly response: Response;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AntpathApiError } from "./sdk-errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Thin transport used by every BFF-bound operation. The SDK class and
|
|
4
|
+
* the CLI subcommands BOTH build an `HttpClient` and pass it to the
|
|
5
|
+
* operations module — so they cannot drift in how they auth, encode
|
|
6
|
+
* query parameters, or decode error responses.
|
|
7
|
+
*/
|
|
8
|
+
export class HttpClient {
|
|
9
|
+
#baseUrl;
|
|
10
|
+
#apiToken;
|
|
11
|
+
#fetch;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
if (!options.baseUrl) {
|
|
14
|
+
throw new Error("HttpClient: baseUrl is required");
|
|
15
|
+
}
|
|
16
|
+
if (!options.apiToken) {
|
|
17
|
+
throw new Error("HttpClient: apiToken is required");
|
|
18
|
+
}
|
|
19
|
+
const normalized = options.baseUrl.endsWith("/") ? options.baseUrl : `${options.baseUrl}/`;
|
|
20
|
+
this.#baseUrl = new URL(normalized);
|
|
21
|
+
this.#apiToken = options.apiToken;
|
|
22
|
+
this.#fetch = options.fetch ?? fetch;
|
|
23
|
+
}
|
|
24
|
+
async request(path, init = {}, query = {}) {
|
|
25
|
+
const url = new URL(path.replace(/^\//, ""), this.#baseUrl);
|
|
26
|
+
for (const [key, value] of Object.entries(query)) {
|
|
27
|
+
url.searchParams.set(key, value);
|
|
28
|
+
}
|
|
29
|
+
const headers = {
|
|
30
|
+
accept: "application/json",
|
|
31
|
+
authorization: `Bearer ${this.#apiToken}`,
|
|
32
|
+
...normalizeHeaders(init.headers)
|
|
33
|
+
};
|
|
34
|
+
if (init.body !== undefined && init.body !== null && !headers["content-type"]) {
|
|
35
|
+
headers["content-type"] = "application/json";
|
|
36
|
+
}
|
|
37
|
+
const response = await this.#fetch(url, { ...init, headers });
|
|
38
|
+
const body = await readJson(response);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new AntpathApiError(response.status, extractErrorMessage(body), body);
|
|
41
|
+
}
|
|
42
|
+
return body;
|
|
43
|
+
}
|
|
44
|
+
async download(path, init = {}, query = {}) {
|
|
45
|
+
const url = new URL(path.replace(/^\//, ""), this.#baseUrl);
|
|
46
|
+
for (const [key, value] of Object.entries(query)) {
|
|
47
|
+
url.searchParams.set(key, value);
|
|
48
|
+
}
|
|
49
|
+
const headers = {
|
|
50
|
+
authorization: `Bearer ${this.#apiToken}`,
|
|
51
|
+
...normalizeHeaders(init.headers)
|
|
52
|
+
};
|
|
53
|
+
const response = await this.#fetch(url, { ...init, headers });
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const body = await readJson(response);
|
|
56
|
+
throw new AntpathApiError(response.status, extractErrorMessage(body), body);
|
|
57
|
+
}
|
|
58
|
+
return { response };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function normalizeHeaders(headers) {
|
|
62
|
+
if (!headers)
|
|
63
|
+
return {};
|
|
64
|
+
if (headers instanceof Headers)
|
|
65
|
+
return Object.fromEntries(headers.entries());
|
|
66
|
+
if (Array.isArray(headers))
|
|
67
|
+
return Object.fromEntries(headers);
|
|
68
|
+
return headers;
|
|
69
|
+
}
|
|
70
|
+
async function readJson(response) {
|
|
71
|
+
const text = await response.text();
|
|
72
|
+
if (text.length === 0)
|
|
73
|
+
return {};
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(text);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return { raw: text };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function extractErrorMessage(body) {
|
|
82
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
83
|
+
const error = body.error;
|
|
84
|
+
if (typeof error === "string")
|
|
85
|
+
return error;
|
|
86
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
87
|
+
const message = error.message;
|
|
88
|
+
if (typeof message === "string")
|
|
89
|
+
return message;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return "antpath API request failed";
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=http.js.map
|