@andreprado/agentkit 0.1.0-alpha.3 → 0.1.0-alpha.5

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.
@@ -30,55 +30,65 @@ The capsule root is the runtime boundary. Run AgentKit commands from the directo
30
30
 
31
31
  ## Current Command Surface
32
32
 
33
- Implemented local commands:
33
+ Current commands:
34
34
 
35
35
  ```sh
36
- agentkit new <name> --template blank
37
- agentkit new <name> --template support
38
- agentkit new <name> --template blank --no-install
39
- agentkit chat --message "hello"
40
- agentkit tool <name> --input <path-or-json>
36
+ agentkit new <name> [--template blank|support|dentista] [--no-install]
37
+ agentkit dev [--port <number>]
38
+ agentkit open
39
+ agentkit chat-ui --deploy
40
+ agentkit chat-ui --deploy [--port <number>] [--token-file <path>]
41
+ agentkit chat --message <text> [--conversation-id <id>]
42
+ agentkit tool <name> [--input <path-or-json>]
43
+ agentkit db migrate
44
+ agentkit db reset --yes
45
+ agentkit db shell
46
+ agentkit db seed [--file <path>]
41
47
  agentkit eval run
42
48
  agentkit conversations list
43
49
  agentkit conversations show <conversation-id>
44
50
  agentkit channels list
45
- agentkit channels add website website-chat
46
- agentkit channels add telegram support-telegram
47
- agentkit channels add whatsapp support-whatsapp --provider zapster
48
- agentkit channels setup <name>
49
- agentkit channels setup <name> --apply
50
- agentkit channels status <name>
51
- agentkit channels test <name> --message "hello"
52
- agentkit channels test <name> --fixture ./fixtures/provider-event.json
53
- agentkit channels deliveries list <name>
54
- agentkit channels deliveries show <delivery-id>
51
+ agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
52
+ agentkit channels setup <name> [--apply] [--api <url>]
53
+ agentkit channels status <name> [--api <url>]
54
+ agentkit channels test <name> [--message <text>] [--fixture <path>] [--api <url>]
55
+ agentkit channels deliveries list <name> [--api <url>]
56
+ agentkit channels deliveries show <delivery-id> [--api <url>]
55
57
  agentkit inspect
56
- agentkit env set <NAME> <VALUE>
57
- agentkit env list
58
- agentkit env unset <NAME>
59
- agentkit docs path
60
- agentkit docs llms
61
- agentkit docs full
62
- agentkit handoff codex [goal]
63
- agentkit handoff claude [goal]
64
- agentkit dev
65
- agentkit dev --port 4124
66
- agentkit build
67
- agentkit deploy
68
- agentkit deploy --dry-run
58
+ agentkit build [--target cloudflare|container]
69
59
  agentkit login --token <token>
70
60
  agentkit logout
61
+ agentkit deploy [--target cloudflare|vps] [--host <host>] [--api <url>] [--dry-run] [--anonymous] [--local-wrangler] [--smoke <message>]
62
+ agentkit deploy doctor [--api <url>] [--anonymous]
63
+ agentkit deploy smoke [--message <text>] [--api <url>]
71
64
  agentkit deploy status
72
65
  agentkit deploy pause
73
66
  agentkit deploy resume
74
67
  agentkit secret set <NAME> <VALUE>
68
+ agentkit secret set <NAME> --stdin
69
+ agentkit secret set <NAME> --from-env [ENV_NAME]
70
+ agentkit secret set <NAME> --from-local-env
71
+ agentkit secret sync --from-local
75
72
  agentkit secret list
76
73
  agentkit secret unset <NAME>
77
- agentkit access token create <name>
74
+ agentkit access token create <name> [--api <url>] [--out <path>]
78
75
  agentkit access token list
79
76
  agentkit access token revoke <token-id>
77
+ agentkit env set <NAME> <VALUE>
78
+ agentkit env set <NAME> --stdin
79
+ agentkit env set <NAME> --from-env [ENV_NAME]
80
+ agentkit env list
81
+ agentkit env unset <NAME>
82
+ agentkit docs path
83
+ agentkit docs llms
84
+ agentkit docs full
85
+ agentkit handoff codex [goal]
86
+ agentkit handoff claude [goal]
87
+ agentkit help commands
80
88
  ```
81
89
 
90
+ Prefer `env set --stdin` or `--from-env` for local secret values, and prefer `secret set --stdin`, `--from-env`, or `--from-local-env` for hosted secrets. Inline `<VALUE>` forms exist for simple non-sensitive values, but agents should avoid putting secrets in shell history.
91
+
82
92
  Planned commands described by the contract but not implemented yet:
83
93
 
84
94
  ```sh
@@ -125,7 +135,7 @@ Then open the folder in Codex, Claude Code, or another coding agent and ask dire
125
135
  Develop an appointment and intake agent for an ophthalmology office.
126
136
  ```
127
137
 
128
- The coding agent should infer the first useful version, edit `prompts/instructions.md`, `agentkit.config.ts`, and `tools/`, then run the verification commands before finishing.
138
+ The coding agent should infer the first useful version, edit `prompts/instructions.md`, `agentkit.config.ts`, `schema.sql`, `tools/`, and `evals/`, then run the verification commands before finishing. Do not wait for a wizard or recipe. AgentKit provides the scaffold and contract; the coding agent implements directly in the capsule.
129
139
 
130
140
  Optional handoff shortcut:
131
141
 
@@ -136,6 +146,8 @@ npm run agentkit -- handoff claude "Develop an appointment and intake agent for
136
146
 
137
147
  The command prints a ready-to-paste prompt that points the coding agent at `AGENTKIT.md` and the packaged `llms-full.txt` contract.
138
148
 
149
+ The generated docs and handoff prompt must make UI testing explicit. For local UI testing, run `npm run dev`, open the printed `Chat:` URL, and tell the owner the exact URL. For hosted UI testing after deploy, run `npm run agentkit -- chat-ui --deploy`, open the printed `Chat:` URL, and tell the owner it is connected to the hosted deploy.
150
+
139
151
  ## Agent Config
140
152
 
141
153
  `agentkit.config.ts` is the source of truth:
@@ -202,7 +214,11 @@ provider: {
202
214
  secrets: [],
203
215
  ```
204
216
 
205
- Use OpenAI locally:
217
+ `test/fake` is deterministic. It is useful for scaffold checks, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality.
218
+
219
+ Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them. After the owner chooses, update `agentkit.config.ts`, `.env.schema`, local secrets, hosted secrets if deploying, then rerun chat/UI checks.
220
+
221
+ OpenAI example:
206
222
 
207
223
  ```ts
208
224
  provider: {
@@ -539,6 +555,8 @@ Run:
539
555
  agentkit dev
540
556
  ```
541
557
 
558
+ If the default local port is occupied, `agentkit dev` chooses the next available port and prints the actual URLs. If the occupied port is another AgentKit server, the output names the agent using it.
559
+
542
560
  Expected output:
543
561
 
544
562
  ```txt
@@ -550,6 +568,12 @@ Inspect: http://localhost:4123/_agentkit
550
568
  Storage: .agentkit/agentkit.db
551
569
  ```
552
570
 
571
+ To reopen the local UI for the current capsule after the server is already running:
572
+
573
+ ```sh
574
+ agentkit open
575
+ ```
576
+
553
577
  Endpoints:
554
578
 
555
579
  ```txt
@@ -567,6 +591,8 @@ curl -X POST http://localhost:4123/v1/chat \
567
591
  -d '{"message":{"role":"user","content":"hello"}}'
568
592
  ```
569
593
 
594
+ The chat response includes `conversationId`. Send the same `conversationId` on the next `POST /v1/chat` request, or pass it to `agentkit chat --conversation-id <id>`, to continue with the persisted message history.
595
+
570
596
  ## Conversations
571
597
 
572
598
  After chat:
@@ -610,6 +636,8 @@ persisted_tool_call
610
636
 
611
637
  `persisted_tool_call` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `status`, and/or `visibility`. `tool_call` remains accepted as a backwards-compatible alias.
612
638
 
639
+ Evals run the normal capsule tools. If a tool would write externally, delete, charge money, send email, or call a real customer system, make its `execute` implementation branch on `ctx.runtime.environment === "eval"` and return deterministic non-destructive output for eval runs. Do not invent an eval-only mock API; keep the behavior inside the registered tool contract unless AgentKit adds a first-class mock facility later.
640
+
613
641
  ## Security Rules
614
642
 
615
643
  Never commit:
@@ -633,7 +661,7 @@ prompts/
633
661
  docs/
634
662
  ```
635
663
 
636
- Local `.env` is development only. Use `.env.schema` as the committed secret-name contract; local AgentKit commands load `.env` directly. Hosted alpha deploys use `agentkit login --token ...` and managed secrets through `agentkit secret set/list/unset`.
664
+ Local `.env` is development only. Use `.env.schema` as the committed secret-name contract; local AgentKit commands load `.env` directly. Hosted alpha deploys use `agentkit login --token ...` and managed secrets through `agentkit secret set/list/unset` or `agentkit secret sync --from-local`. Prefer `--stdin`, `--from-env`, `--from-local-env`, or sync from local `.env` so secret values do not appear in shell history. Inline `<VALUE>` forms exist only for compatibility and simple non-sensitive values.
637
665
 
638
666
  Tools are a security boundary. A tool must declare every secret it needs. The runtime injects only tool-declared secrets.
639
667
 
@@ -647,18 +675,21 @@ Current flow:
647
675
  agentkit deploy --dry-run
648
676
  agentkit login --token agk_user_...
649
677
  agentkit deploy doctor
650
- agentkit deploy
678
+ agentkit deploy --smoke "hello"
651
679
  agentkit deploy status
680
+ agentkit deploy smoke --message "hello"
681
+ agentkit chat-ui --deploy
652
682
  ```
653
683
 
654
- `agentkit deploy doctor` checks AgentKit Cloud login, `cloudflare_deploy_alpha`, hosted secrets, local `.env` names that still need `agentkit secret set`, and private-access runtime token handling. `agentkit deploy` sends the capsule to AgentKit Cloud, runs the same readiness check automatically before building and uploading, and writes the local chat/UI deploy access token to `.agentkit/chat-access-token.json` for private hosted deploys. Production alpha deploys require an account with `cloudflare_deploy_alpha`; local commands and dry-run builds do not require login. AgentKit owns infrastructure selection, backend migration, managed secrets, and public URL creation.
684
+ `agentkit deploy doctor` checks AgentKit Cloud login, `cloudflare_deploy_alpha`, online deploy capacity, hosted secrets, local `.env` names that still need `agentkit secret set`, and private-access runtime token handling. `agentkit deploy` sends the capsule to AgentKit Cloud, runs the same readiness check automatically before building and uploading, and writes the local chat/UI deploy access token to `.agentkit/chat-access-token.json` for private hosted deploys. `agentkit deploy --smoke "hello"` deploys and then tests `/v1/chat` with the deploy access token. `agentkit deploy smoke --message "hello"` repeats that smoke against the last local deploy. `agentkit chat-ui --deploy` serves a local UI pointed at the hosted deploy using that token without exposing it to browser code. Production alpha deploys require an account with `cloudflare_deploy_alpha`; local commands and dry-run builds do not require login. AgentKit owns infrastructure selection, backend migration, managed secrets, and public URL creation.
655
685
 
656
686
  The CLI defaults to the hosted AgentKit Cloud API at `https://agentkit-cloud.aibuilders.com.br`. Use `AGENTKIT_CLOUD_API_URL` or `agentkit deploy --api <url>` only for local or alternate control-plane tests.
657
687
 
658
688
  Account/access flow:
659
689
 
660
690
  ```sh
661
- agentkit secret set OPENAI_API_KEY
691
+ agentkit secret set OPENAI_API_KEY --from-local-env
692
+ agentkit secret sync --from-local
662
693
  agentkit secret list
663
694
  agentkit access token create website-chat --out .agentkit/website-chat-access-token.json
664
695
  agentkit access token list
package/docs/llms.txt CHANGED
@@ -24,9 +24,10 @@ Task guides:
24
24
  Current local commands:
25
25
 
26
26
  ```sh
27
- agentkit new <name> --template blank|support
27
+ agentkit new <name> --template blank|support|dentista
28
28
  agentkit new <name> --template blank --no-install
29
29
  agentkit docs full
30
+ agentkit env set <NAME> --stdin
30
31
  agentkit env list
31
32
  agentkit env unset <NAME>
32
33
  agentkit handoff codex|claude [goal]
@@ -43,16 +44,25 @@ agentkit channels test support-telegram --message "hello"
43
44
  agentkit channels deliveries list support-telegram
44
45
  agentkit login --token agk_user_...
45
46
  agentkit deploy doctor
46
- agentkit deploy
47
+ agentkit deploy --smoke "hello"
47
48
  agentkit deploy status
49
+ agentkit deploy smoke --message "hello"
50
+ agentkit chat-ui --deploy
51
+ agentkit secret set <NAME> --from-local-env
52
+ agentkit secret sync --from-local
48
53
  agentkit secret list
49
54
  agentkit access token create website-chat --out .agentkit/website-chat-access-token.json
50
55
  agentkit access token list
51
56
  agentkit inspect
52
57
  agentkit dev
58
+ agentkit open
53
59
  ```
54
60
 
55
- Generated capsules include `AGENTKIT.md` and `AGENTS.md` so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace.
61
+ Generated capsules include `AGENTKIT.md` and `AGENTS.md` so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold and contract.
62
+
63
+ UI testing is part of the handoff. For local UI testing, run `agentkit dev`, open the printed `Chat:` URL, and tell the owner the exact URL. After hosted deploy, run `agentkit chat-ui --deploy`, open the printed `Chat:` URL, and tell the owner it is connected to the deploy.
64
+
65
+ `test/fake` is deterministic and validates scaffold, direct tool calls, and fake-provider evals. It does not validate natural conversation quality. Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
56
66
 
57
67
  Current local endpoints from `agentkit dev`:
58
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andreprado/agentkit",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,36 @@
1
+ export type ParsedArgs = {
2
+ command?: string;
3
+ positional: string[];
4
+ flags: Record<string, string | boolean>;
5
+ };
6
+
7
+ export function parseArgs(argv: string[]): ParsedArgs {
8
+ const [command, ...rest] = argv;
9
+ const positional: string[] = [];
10
+ const flags: Record<string, string | boolean> = {};
11
+
12
+ for (let index = 0; index < rest.length; index += 1) {
13
+ const value = rest[index];
14
+
15
+ if (!value.startsWith("--")) {
16
+ positional.push(value);
17
+ continue;
18
+ }
19
+
20
+ const name = value.slice(2);
21
+ const next = rest[index + 1];
22
+
23
+ if (next && !next.startsWith("--")) {
24
+ flags[name] = next;
25
+ index += 1;
26
+ } else {
27
+ flags[name] = true;
28
+ }
29
+ }
30
+
31
+ return { command, positional, flags };
32
+ }
33
+
34
+ export function isHelpRequested(args: ParsedArgs): boolean {
35
+ return args.flags.help === true || args.flags.h === true || args.positional.includes("--help") || args.positional.includes("-h");
36
+ }
@@ -0,0 +1,257 @@
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ import { AgentKitError } from "../runtime/errors";
6
+ import { defaultAgentKitCloudApiUrl } from "./constants";
7
+ import { parseCloudApiUrl } from "./flags";
8
+
9
+ export type CloudAuthConfig = {
10
+ apiUrl: string;
11
+ token: string;
12
+ source?: "environment" | "file";
13
+ };
14
+
15
+ export type CloudMeResponse = {
16
+ account?: {
17
+ email?: string;
18
+ status?: string;
19
+ };
20
+ };
21
+
22
+ export type CloudCapabilitiesResponse = {
23
+ capabilities?: Array<{
24
+ name?: string;
25
+ status?: string;
26
+ }>;
27
+ };
28
+
29
+ export type CloudLimitsResponse = {
30
+ online_agents?: {
31
+ account?: {
32
+ used?: number | null;
33
+ limit?: number | null;
34
+ };
35
+ global?: {
36
+ used?: number | null;
37
+ limit?: number | null;
38
+ };
39
+ };
40
+ };
41
+
42
+ export type DeployAccessTokenCreateResponse = {
43
+ access_token?: {
44
+ id?: string;
45
+ deploy_id?: string;
46
+ name?: string;
47
+ token?: string;
48
+ };
49
+ };
50
+
51
+ export type DeployAccessTokenListResponse = {
52
+ access_tokens?: Array<{
53
+ id?: string;
54
+ deploy_id?: string;
55
+ name?: string;
56
+ }>;
57
+ };
58
+
59
+ export type CloudSecretsResponse = {
60
+ secrets?: Array<{
61
+ name?: string;
62
+ status?: string;
63
+ updated_at?: string;
64
+ }>;
65
+ };
66
+
67
+ export function cloudAuthPath(): string {
68
+ return join(homedir(), ".agentkit", "cloud.json");
69
+ }
70
+
71
+ export async function readCloudAuth(): Promise<CloudAuthConfig | null> {
72
+ if (process.env.AGENTKIT_CLOUD_API_TOKEN) {
73
+ return {
74
+ apiUrl: process.env.AGENTKIT_CLOUD_API_URL ?? defaultAgentKitCloudApiUrl,
75
+ token: process.env.AGENTKIT_CLOUD_API_TOKEN,
76
+ source: "environment",
77
+ };
78
+ }
79
+
80
+ try {
81
+ const parsed = JSON.parse(await readFile(cloudAuthPath(), "utf8"));
82
+
83
+ if (parsed && typeof parsed === "object" && typeof parsed.token === "string") {
84
+ return {
85
+ apiUrl: typeof parsed.apiUrl === "string" ? parsed.apiUrl : defaultAgentKitCloudApiUrl,
86
+ token: parsed.token,
87
+ source: "file",
88
+ };
89
+ }
90
+ } catch {
91
+ return null;
92
+ }
93
+
94
+ return null;
95
+ }
96
+
97
+ export async function writeCloudAuth(config: CloudAuthConfig): Promise<void> {
98
+ await mkdir(join(homedir(), ".agentkit"), { recursive: true });
99
+ await writeFile(cloudAuthPath(), `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
100
+ }
101
+
102
+ export async function clearCloudAuth(): Promise<void> {
103
+ await rm(cloudAuthPath(), { force: true });
104
+ }
105
+
106
+ export async function resolveCloudApiUrl(flag: string | boolean | undefined): Promise<string> {
107
+ if (flag !== undefined) {
108
+ return parseCloudApiUrl(flag);
109
+ }
110
+
111
+ const auth = await readCloudAuth();
112
+ return auth?.apiUrl ?? parseCloudApiUrl(undefined);
113
+ }
114
+
115
+ export async function verifyCloudLogin(
116
+ apiUrl: string,
117
+ token: string,
118
+ ): Promise<{ me: CloudMeResponse; capabilities: CloudCapabilitiesResponse }> {
119
+ const me = await cloudApiRequestWithToken<CloudMeResponse>(apiUrl, "/v1/me", token, { method: "GET" });
120
+ const capabilities = await cloudApiRequestWithToken<CloudCapabilitiesResponse>(
121
+ apiUrl,
122
+ "/v1/me/capabilities",
123
+ token,
124
+ { method: "GET" },
125
+ );
126
+
127
+ return { me, capabilities };
128
+ }
129
+
130
+ export async function cloudGet<T>(apiUrl: string, path: string): Promise<T> {
131
+ const response = await cloudFetch(apiUrl, path);
132
+ const payload = await response.json();
133
+
134
+ if (!response.ok) {
135
+ throw new Error(readCloudError(payload, response.status));
136
+ }
137
+
138
+ return payload as T;
139
+ }
140
+
141
+ export async function cloudPost<T>(apiUrl: string, path: string, body: unknown): Promise<T> {
142
+ const response = await cloudFetch(apiUrl, path, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify(body),
146
+ });
147
+ const payload = await response.json();
148
+
149
+ if (!response.ok) {
150
+ throw new Error(readCloudError(payload, response.status));
151
+ }
152
+
153
+ return payload as T;
154
+ }
155
+
156
+ export function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
157
+ const headers = new Headers(init.headers);
158
+ const apiToken = process.env.AGENTKIT_CLOUD_API_TOKEN;
159
+
160
+ if (apiToken && !headers.has("Authorization")) {
161
+ headers.set("Authorization", `Bearer ${apiToken}`);
162
+ }
163
+
164
+ return fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
165
+ ...init,
166
+ headers,
167
+ });
168
+ }
169
+
170
+ export async function cloudApiRequest(apiUrl: string, path: string, init: RequestInit): Promise<unknown> {
171
+ const auth = await readCloudAuth();
172
+ const headers = new Headers(init.headers);
173
+
174
+ if (!headers.has("Content-Type") && init.body !== undefined) {
175
+ headers.set("Content-Type", "application/json");
176
+ }
177
+
178
+ if (auth?.token && !headers.has("Authorization")) {
179
+ headers.set("Authorization", `Bearer ${auth.token}`);
180
+ }
181
+
182
+ const response = await fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
183
+ ...init,
184
+ headers,
185
+ });
186
+ const payload = await response.json().catch(() => null);
187
+
188
+ if (!response.ok) {
189
+ const code =
190
+ payload && typeof payload === "object" && "error" in payload
191
+ ? String((payload as { error?: { code?: unknown } }).error?.code ?? "cloud_request_failed")
192
+ : "cloud_request_failed";
193
+ let message = readCloudError(payload, response.status);
194
+
195
+ if (code === "auth_token_invalid") {
196
+ message = `${message} Token source: ${auth ? formatCloudAuthSource(auth) : "none"}. Run: agentkit login --token <token>`;
197
+ }
198
+
199
+ throw new AgentKitError(code, message);
200
+ }
201
+
202
+ return payload;
203
+ }
204
+
205
+ export async function cloudApiRequestWithToken<T>(
206
+ apiUrl: string,
207
+ path: string,
208
+ token: string,
209
+ init: RequestInit,
210
+ ): Promise<T> {
211
+ const headers = new Headers(init.headers);
212
+
213
+ if (!headers.has("Content-Type") && init.body !== undefined) {
214
+ headers.set("Content-Type", "application/json");
215
+ }
216
+
217
+ headers.set("Authorization", `Bearer ${token}`);
218
+
219
+ const response = await fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
220
+ ...init,
221
+ headers,
222
+ });
223
+ const payload = await response.json().catch(() => null);
224
+
225
+ if (!response.ok) {
226
+ const code =
227
+ payload && typeof payload === "object" && "error" in payload
228
+ ? String((payload as { error?: { code?: unknown } }).error?.code ?? "cloud_request_failed")
229
+ : "cloud_request_failed";
230
+ throw new AgentKitError(code, readCloudError(payload, response.status));
231
+ }
232
+
233
+ return payload as T;
234
+ }
235
+
236
+ export function readCloudError(payload: unknown, status: number): string {
237
+ if (payload && typeof payload === "object" && "error" in payload) {
238
+ const error = (payload as { error?: { message?: unknown } }).error;
239
+ if (typeof error?.message === "string") {
240
+ return error.message;
241
+ }
242
+ }
243
+
244
+ return `AgentKit Cloud request failed with HTTP ${status}.`;
245
+ }
246
+
247
+ export function formatCloudAuthSource(auth: CloudAuthConfig): string {
248
+ if (auth.source === "environment") {
249
+ return "AGENTKIT_CLOUD_API_TOKEN";
250
+ }
251
+
252
+ if (auth.source === "file") {
253
+ return cloudAuthPath();
254
+ }
255
+
256
+ return "AgentKit Cloud auth";
257
+ }