@andreprado/agentkit 0.1.0-alpha.10
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 +69 -0
- package/bin/agentkit.mjs +23 -0
- package/docs/guides/add-channel.md +114 -0
- package/docs/guides/add-knowledge.md +134 -0
- package/docs/guides/add-tool.md +342 -0
- package/docs/guides/agentkit-skills-architecture.md +471 -0
- package/docs/guides/channel-security.md +81 -0
- package/docs/guides/channels-implementation-map.md +243 -0
- package/docs/guides/channels-production-handoff.md +102 -0
- package/docs/guides/connect-telegram.md +110 -0
- package/docs/guides/connect-whatsapp-zapster.md +119 -0
- package/docs/guides/create-agent.md +220 -0
- package/docs/guides/prepare-deploy.md +209 -0
- package/docs/guides/run-evals.md +179 -0
- package/docs/guides/security-rules.md +156 -0
- package/docs/guides/use-provider.md +140 -0
- package/docs/llms-full.txt +876 -0
- package/docs/llms.txt +83 -0
- package/docs/portable-deploy-release-checklist.md +41 -0
- package/package.json +47 -0
- package/src/cli/args.ts +36 -0
- package/src/cli/cloud-client.ts +265 -0
- package/src/cli/commands/channels.ts +810 -0
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/constants.ts +4 -0
- package/src/cli/deploy-chat-ui.ts +392 -0
- package/src/cli/deploy-readiness.ts +348 -0
- package/src/cli/flags.ts +162 -0
- package/src/cli/help.ts +184 -0
- package/src/cli/index.ts +1276 -0
- package/src/cli/process.ts +31 -0
- package/src/cloud/artifact.ts +139 -0
- package/src/cloud/client.ts +79 -0
- package/src/cloud/contracts.ts +63 -0
- package/src/cloud/index.ts +3 -0
- package/src/create-project.ts +177 -0
- package/src/index.ts +408 -0
- package/src/providers/index.ts +25 -0
- package/src/providers/pi.ts +286 -0
- package/src/providers/test.ts +133 -0
- package/src/providers/types.ts +34 -0
- package/src/runtime/build.ts +43 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channel-test-harness.ts +112 -0
- package/src/runtime/channels/telegram.ts +360 -0
- package/src/runtime/channels/website.ts +132 -0
- package/src/runtime/channels/whatsapp-meta.ts +71 -0
- package/src/runtime/channels/whatsapp-zapster.ts +278 -0
- package/src/runtime/channels.ts +138 -0
- package/src/runtime/chat.ts +218 -0
- package/src/runtime/config.ts +684 -0
- package/src/runtime/conversations.ts +38 -0
- package/src/runtime/core/deploy-state.ts +54 -0
- package/src/runtime/core/manifest.ts +213 -0
- package/src/runtime/core/targets.ts +133 -0
- package/src/runtime/database.ts +256 -0
- package/src/runtime/db-commands.ts +167 -0
- package/src/runtime/deploy-readiness.ts +105 -0
- package/src/runtime/deploy.ts +1 -0
- package/src/runtime/dev-server.ts +1247 -0
- package/src/runtime/docs.ts +36 -0
- package/src/runtime/env.ts +152 -0
- package/src/runtime/errors.ts +13 -0
- package/src/runtime/evals.ts +509 -0
- package/src/runtime/inspect.ts +203 -0
- package/src/runtime/knowledge/chunk.ts +333 -0
- package/src/runtime/knowledge/config.ts +135 -0
- package/src/runtime/knowledge/embeddings.ts +133 -0
- package/src/runtime/knowledge/ingest.ts +521 -0
- package/src/runtime/knowledge/prompt-policy.ts +30 -0
- package/src/runtime/knowledge/retrieve.ts +283 -0
- package/src/runtime/knowledge/schema.ts +56 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/runtime-contract.ts +93 -0
- package/src/runtime/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +2517 -0
- package/src/runtime/targets/container/build.ts +146 -0
- package/src/runtime/targets/container/server.ts +33 -0
- package/src/runtime/targets/vps/deploy.ts +206 -0
- package/src/runtime/tool-runner.ts +65 -0
- package/src/runtime/tools.ts +470 -0
- package/src/runtime/traces.ts +41 -0
- package/src/storage/sqlite.ts +1118 -0
- package/src/templates/blank.ts +394 -0
- package/src/templates/dentista.ts +1003 -0
- package/src/templates/index.ts +33 -0
- package/src/templates/skills/agentkit-build-agent/SKILL.md +51 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
- package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
- package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
- package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +41 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +38 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +44 -0
- package/src/templates/skills/agentkit-database/SKILL.md +45 -0
- package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
- package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +60 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +22 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
- package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
- package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
- package/src/templates/skills/agentkit-security/SKILL.md +55 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
- package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
- package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
- package/src/templates/support.ts +401 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,1276 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { createProject } from "../create-project";
|
|
7
|
+
import { buildAgentCapsule } from "../runtime/build";
|
|
8
|
+
import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
|
|
9
|
+
import { readLocalDeployStateIfExists, type LocalDeployState } from "../runtime/core/deploy-state";
|
|
10
|
+
import { resolveBuildTarget, resolveDeployTarget } from "../runtime/core/targets";
|
|
11
|
+
import { getConversationFromCwd, listConversationsFromCwd } from "../runtime/conversations";
|
|
12
|
+
import { getConversationTraceFromCwd } from "../runtime/traces";
|
|
13
|
+
import { runAgentMessageFromCwd } from "../runtime/chat";
|
|
14
|
+
import { deployBuildToAgentKitCloud, writeLocalDeployState } from "../runtime/deploy";
|
|
15
|
+
import {
|
|
16
|
+
migrateLocalDatabase,
|
|
17
|
+
openLocalDatabaseShell,
|
|
18
|
+
resetLocalDatabase,
|
|
19
|
+
seedLocalDatabase,
|
|
20
|
+
} from "../runtime/db-commands";
|
|
21
|
+
import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
|
|
22
|
+
import { findAgentKitDocs } from "../runtime/docs";
|
|
23
|
+
import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
|
|
24
|
+
import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
|
|
25
|
+
import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
|
|
26
|
+
import { runToolFromCwd } from "../runtime/tool-runner";
|
|
27
|
+
import { startAgentDevServer } from "../runtime/dev-server";
|
|
28
|
+
import { inspectAgentCapsule } from "../runtime/inspect";
|
|
29
|
+
import { AgentKitError, isAgentKitError } from "../runtime/errors";
|
|
30
|
+
import { prepareVpsDeploy } from "../runtime/targets/vps/deploy";
|
|
31
|
+
import { parseArgs, isHelpRequested, type ParsedArgs } from "./args";
|
|
32
|
+
import {
|
|
33
|
+
defaultAgentKitDevPort,
|
|
34
|
+
defaultDeployChatAccessTokenName,
|
|
35
|
+
defaultDeployChatAccessTokenPath,
|
|
36
|
+
} from "./constants";
|
|
37
|
+
import {
|
|
38
|
+
clearCloudAuth,
|
|
39
|
+
cloudApiRequest,
|
|
40
|
+
readCloudAuth,
|
|
41
|
+
readCloudError,
|
|
42
|
+
resolveCloudApiUrl,
|
|
43
|
+
verifyCloudLogin,
|
|
44
|
+
writeCloudAuth,
|
|
45
|
+
type CloudMeResponse,
|
|
46
|
+
type DeployAccessTokenCreateResponse,
|
|
47
|
+
type DeployAccessTokenListResponse,
|
|
48
|
+
} from "./cloud-client";
|
|
49
|
+
import {
|
|
50
|
+
checkDeployReadiness,
|
|
51
|
+
formatReadinessError,
|
|
52
|
+
printDeployReadinessReport,
|
|
53
|
+
resolveProjectIdForSecretCommand,
|
|
54
|
+
syncHostedSecretsFromLocal,
|
|
55
|
+
} from "./deploy-readiness";
|
|
56
|
+
import { readDeployAccessTokenForUi, startDeployChatUiServer } from "./deploy-chat-ui";
|
|
57
|
+
import {
|
|
58
|
+
parseCloudApiUrl,
|
|
59
|
+
parseOptionalStringFlag,
|
|
60
|
+
parsePort,
|
|
61
|
+
parseRequiredStringFlag,
|
|
62
|
+
parseTargetFlag,
|
|
63
|
+
resolveSecretInputValue,
|
|
64
|
+
} from "./flags";
|
|
65
|
+
import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
|
|
66
|
+
import { runInheritedCommand, waitForShutdown } from "./process";
|
|
67
|
+
import { handleChannelsCommand } from "./commands/channels";
|
|
68
|
+
import { handleKnowledgeCommand } from "./commands/knowledge";
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
const args = parseArgs(process.argv.slice(2));
|
|
72
|
+
|
|
73
|
+
if (!args.command || args.command === "--help" || args.command === "-h") {
|
|
74
|
+
console.log(renderHelp());
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isHelpRequested(args)) {
|
|
79
|
+
console.log(renderCommandHelp(args.command));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (args.command === "help") {
|
|
84
|
+
const [topic] = args.positional;
|
|
85
|
+
|
|
86
|
+
if (!topic) {
|
|
87
|
+
console.log(renderHelp());
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (topic === "commands") {
|
|
92
|
+
console.log(renderCommandReference());
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new Error("Usage: agentkit help [commands]");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (args.command === "new") {
|
|
100
|
+
const [name] = args.positional;
|
|
101
|
+
const template = String(args.flags.template ?? "blank");
|
|
102
|
+
const shouldInstall = args.flags["no-install"] !== true;
|
|
103
|
+
|
|
104
|
+
if (!name) {
|
|
105
|
+
throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support|dentista] [--no-install]");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = await createProject({ name, template, cwd: process.cwd() });
|
|
109
|
+
console.log(`Created ${result.name} at ${result.path}`);
|
|
110
|
+
|
|
111
|
+
if (shouldInstall) {
|
|
112
|
+
console.log("");
|
|
113
|
+
console.log("Installing dependencies...");
|
|
114
|
+
await installProjectDependencies(result.path);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log("");
|
|
118
|
+
console.log("Next:");
|
|
119
|
+
if (resolve(result.path) !== resolve(process.cwd())) {
|
|
120
|
+
console.log(` cd ${result.name}`);
|
|
121
|
+
}
|
|
122
|
+
if (!shouldInstall) {
|
|
123
|
+
console.log(" npm install");
|
|
124
|
+
}
|
|
125
|
+
console.log(" open this folder in Codex or Claude Code");
|
|
126
|
+
console.log(" coding agents start at skills/agentkit-capsule/SKILL.md");
|
|
127
|
+
console.log(' ask: "Develop an appointment and intake agent for an ophthalmology office"');
|
|
128
|
+
console.log(' npm run chat -- --message "hello"');
|
|
129
|
+
console.log(" npm run dev");
|
|
130
|
+
console.log(" npm run agentkit -- deploy");
|
|
131
|
+
console.log("");
|
|
132
|
+
console.log("Optional: manage local secrets with `printf %s \"$VALUE\" | npm run agentkit -- env set <NAME> --stdin`.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (args.command === "dev") {
|
|
137
|
+
const port = parsePort(args.flags.port, "agentkit dev --port <number>");
|
|
138
|
+
const devServer = await startAgentDevServer(process.cwd(), { port, basePort: localDevBasePort() });
|
|
139
|
+
|
|
140
|
+
console.log("Agent Capsule running");
|
|
141
|
+
console.log("");
|
|
142
|
+
if (devServer.portConflict) {
|
|
143
|
+
const owner = devServer.portConflict.agent ? ` by ${devServer.portConflict.agent}` : "";
|
|
144
|
+
console.log(
|
|
145
|
+
`Port ${devServer.portConflict.port} is already used${owner}. Started ${devServer.capsule.config.name} at ${devServer.urls.chat}.`,
|
|
146
|
+
);
|
|
147
|
+
console.log("");
|
|
148
|
+
}
|
|
149
|
+
console.log(`Chat: ${devServer.urls.chat}`);
|
|
150
|
+
console.log(`API: ${devServer.urls.api}`);
|
|
151
|
+
console.log(`Inspect: ${devServer.urls.inspect}`);
|
|
152
|
+
console.log(`Storage: ${devServer.storage ?? "managed"}`);
|
|
153
|
+
console.log("Reload: prompts/config reload per request; restart after changing tool modules or dependencies");
|
|
154
|
+
await waitForShutdown(devServer.stop);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (args.command === "open") {
|
|
159
|
+
const server = await findRunningLocalDevServer(process.cwd());
|
|
160
|
+
|
|
161
|
+
if (!server) {
|
|
162
|
+
console.log("No running AgentKit dev server found for this capsule.");
|
|
163
|
+
console.log("Run: npm run dev");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await openLocalUrl(server.url);
|
|
168
|
+
console.log(`Opened ${server.url}`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (args.command === "chat-ui") {
|
|
173
|
+
if (!args.flags.deploy) {
|
|
174
|
+
throw new Error("Usage: agentkit chat-ui --deploy [--port <number>] [--token-file <path>]");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const port = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>") ?? 4124;
|
|
178
|
+
const tokenFile = parseOptionalStringFlag(
|
|
179
|
+
args.flags["token-file"],
|
|
180
|
+
"token-file",
|
|
181
|
+
"agentkit chat-ui --deploy --token-file <path>",
|
|
182
|
+
);
|
|
183
|
+
const server = await startDeployChatUiServer(process.cwd(), { port, tokenFile });
|
|
184
|
+
|
|
185
|
+
console.log("AgentKit hosted chat UI running");
|
|
186
|
+
console.log("");
|
|
187
|
+
console.log(`Chat: ${server.url}`);
|
|
188
|
+
console.log(`Production: ${server.deployUrl}`);
|
|
189
|
+
console.log(`Token: ${server.tokenSource}`);
|
|
190
|
+
await waitForShutdown(server.stop);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (args.command === "chat") {
|
|
195
|
+
const message = args.flags.message;
|
|
196
|
+
const conversationId = parseOptionalStringFlag(
|
|
197
|
+
args.flags["conversation-id"] ?? args.flags.conversation,
|
|
198
|
+
"conversation id",
|
|
199
|
+
'agentkit chat --message "hello" [--conversation-id <id>]',
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
if (typeof message !== "string") {
|
|
203
|
+
throw new Error('Missing message. Usage: agentkit chat --message "hello" [--conversation-id <id>]');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = await runAgentMessageFromCwd(process.cwd(), { message, conversationId });
|
|
207
|
+
console.log(result.message.content);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (args.command === "tool") {
|
|
212
|
+
const [name] = args.positional;
|
|
213
|
+
|
|
214
|
+
if (!name) {
|
|
215
|
+
throw new Error("Missing tool name. Usage: agentkit tool <name> [--input <path-or-json>]");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const input = await parseToolInput(process.cwd(), args.flags.input, args.flags["input-json"]);
|
|
219
|
+
const result = await runToolFromCwd(process.cwd(), { name, input });
|
|
220
|
+
console.log(JSON.stringify(result.output, null, 2));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (args.command === "db") {
|
|
225
|
+
const [subcommand] = args.positional;
|
|
226
|
+
|
|
227
|
+
if (subcommand === "migrate") {
|
|
228
|
+
const result = await migrateLocalDatabase(process.cwd());
|
|
229
|
+
console.log("Local database migrated");
|
|
230
|
+
console.log(`Database: ${result.databasePath}`);
|
|
231
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
232
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
233
|
+
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (subcommand === "reset") {
|
|
238
|
+
if (!args.flags.yes) {
|
|
239
|
+
throw new Error("agentkit db reset deletes the resolved local SQLite database. Re-run with --yes to confirm.");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const result = await resetLocalDatabase(process.cwd());
|
|
243
|
+
console.log("Local database reset");
|
|
244
|
+
console.log(`Deleted: ${result.resetPath}`);
|
|
245
|
+
console.log(`Database: ${result.databasePath}`);
|
|
246
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
247
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
248
|
+
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (subcommand === "shell") {
|
|
253
|
+
const result = await openLocalDatabaseShell(process.cwd());
|
|
254
|
+
|
|
255
|
+
if (!result.opened) {
|
|
256
|
+
console.log(result.message);
|
|
257
|
+
console.log(result.command);
|
|
258
|
+
} else if (result.exitCode !== 0) {
|
|
259
|
+
process.exitCode = result.exitCode;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (subcommand === "seed") {
|
|
266
|
+
const seedFile = parseOptionalStringFlag(args.flags.file, "file", "agentkit db seed --file <path>") ?? "seed.sql";
|
|
267
|
+
const result = await seedLocalDatabase(process.cwd(), seedFile);
|
|
268
|
+
console.log("Local database seeded");
|
|
269
|
+
console.log(`Database: ${result.databasePath}`);
|
|
270
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
271
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
272
|
+
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
273
|
+
console.log(`Seed: ${result.seedPath}`);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
throw new Error("Usage: agentkit db migrate | agentkit db reset --yes | agentkit db shell | agentkit db seed [--file <path>]");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (args.command === "eval") {
|
|
281
|
+
const [subcommand, conversationId] = args.positional;
|
|
282
|
+
|
|
283
|
+
if (subcommand === "from-conversation") {
|
|
284
|
+
if (!conversationId) {
|
|
285
|
+
throw new Error("Missing conversation id. Usage: agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const out = parseOptionalStringFlag(
|
|
289
|
+
args.flags.out,
|
|
290
|
+
"out",
|
|
291
|
+
"agentkit eval from-conversation <conversation-id> --out <path>",
|
|
292
|
+
);
|
|
293
|
+
const name = parseOptionalStringFlag(
|
|
294
|
+
args.flags.name,
|
|
295
|
+
"name",
|
|
296
|
+
"agentkit eval from-conversation <conversation-id> --name <name>",
|
|
297
|
+
);
|
|
298
|
+
const result = await writeEvalFromConversation(process.cwd(), {
|
|
299
|
+
conversationId,
|
|
300
|
+
out,
|
|
301
|
+
name,
|
|
302
|
+
force: args.flags.force === true,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
console.log("Eval created from conversation");
|
|
306
|
+
console.log(`Conversation: ${result.conversationId}`);
|
|
307
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
308
|
+
console.log(`Turns: ${result.turns}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (subcommand !== "run") {
|
|
313
|
+
throw new Error("Usage: agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const summary = await runEvalsFromCwd(process.cwd());
|
|
317
|
+
|
|
318
|
+
for (const result of summary.results) {
|
|
319
|
+
console.log(`${result.passed ? "PASS" : "FAIL"} ${result.name} (${result.file})`);
|
|
320
|
+
|
|
321
|
+
for (const failure of result.failures) {
|
|
322
|
+
console.log(` - ${failure}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log("");
|
|
327
|
+
console.log(`${summary.passed} passed, ${summary.failed} failed`);
|
|
328
|
+
|
|
329
|
+
if (summary.failed > 0) {
|
|
330
|
+
process.exitCode = 1;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (args.command === "conversations") {
|
|
337
|
+
const [subcommand, conversationId] = args.positional;
|
|
338
|
+
|
|
339
|
+
if (subcommand === "list") {
|
|
340
|
+
const conversations = await listConversationsFromCwd(process.cwd());
|
|
341
|
+
|
|
342
|
+
if (conversations.length === 0) {
|
|
343
|
+
console.log("No conversations found.");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log("id\ttitle\tupdated_at\tmessages");
|
|
348
|
+
|
|
349
|
+
for (const conversation of conversations) {
|
|
350
|
+
console.log(
|
|
351
|
+
[
|
|
352
|
+
conversation.id,
|
|
353
|
+
conversation.title ?? "",
|
|
354
|
+
conversation.updatedAt,
|
|
355
|
+
String(conversation.messageCount),
|
|
356
|
+
].join("\t"),
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (subcommand === "show") {
|
|
364
|
+
if (!conversationId) {
|
|
365
|
+
throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id>");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const conversation = await getConversationFromCwd(process.cwd(), conversationId);
|
|
369
|
+
|
|
370
|
+
console.log(`id: ${conversation.id}`);
|
|
371
|
+
console.log(`title: ${conversation.title ?? ""}`);
|
|
372
|
+
console.log(`updated_at: ${conversation.updatedAt}`);
|
|
373
|
+
console.log("");
|
|
374
|
+
|
|
375
|
+
for (const message of conversation.messages) {
|
|
376
|
+
console.log(`${message.role}: ${message.content}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (subcommand === "trace") {
|
|
383
|
+
if (!conversationId) {
|
|
384
|
+
throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id>");
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
|
|
388
|
+
console.log(JSON.stringify(trace, null, 2));
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>");
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (args.command === "knowledge") {
|
|
396
|
+
await handleKnowledgeCommand(args);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (args.command === "sync") {
|
|
401
|
+
const [subcommand] = args.positional;
|
|
402
|
+
|
|
403
|
+
if (subcommand === "init") {
|
|
404
|
+
const result = await initializeSyncScaffold(process.cwd(), {
|
|
405
|
+
force: args.flags.force === true,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
console.log("Sync scaffold ready");
|
|
409
|
+
if (result.files.length === 0) {
|
|
410
|
+
console.log("Files: none written; existing files kept");
|
|
411
|
+
} else {
|
|
412
|
+
console.log(`Files: ${result.files.join(", ")}`);
|
|
413
|
+
}
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (subcommand === "run") {
|
|
418
|
+
const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit sync run --file <path>");
|
|
419
|
+
const result = await runCapsuleSync(process.cwd(), { file });
|
|
420
|
+
|
|
421
|
+
console.log("Sync completed");
|
|
422
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
423
|
+
console.log(`Output: ${JSON.stringify(result.output)}`);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
throw new Error("Usage: agentkit sync init [--force] | agentkit sync run [--file <path>]");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (args.command === "spec") {
|
|
431
|
+
const [subcommand] = args.positional;
|
|
432
|
+
|
|
433
|
+
if (subcommand === "init") {
|
|
434
|
+
const brief = parseRequiredStringFlag(
|
|
435
|
+
args.flags.brief,
|
|
436
|
+
"brief",
|
|
437
|
+
'agentkit spec init --brief "agent for a real estate brokerage"',
|
|
438
|
+
);
|
|
439
|
+
const out = parseOptionalStringFlag(args.flags.out, "out", "agentkit spec init --out <path>");
|
|
440
|
+
const result = await initializeAgentSpec(process.cwd(), {
|
|
441
|
+
brief,
|
|
442
|
+
out,
|
|
443
|
+
force: args.flags.force === true,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
console.log("Agent spec created");
|
|
447
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (subcommand === "check") {
|
|
452
|
+
const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit spec check --file <path>");
|
|
453
|
+
const result = await checkAgentSpec(process.cwd(), file);
|
|
454
|
+
|
|
455
|
+
if (result.ok) {
|
|
456
|
+
console.log(`Agent spec ok: ${relativePath(result.root, result.file)}`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
console.log(`Agent spec incomplete: ${relativePath(result.root, result.file)}`);
|
|
461
|
+
for (const heading of result.missingHeadings) {
|
|
462
|
+
console.log(` - missing ## ${heading}`);
|
|
463
|
+
}
|
|
464
|
+
process.exitCode = 1;
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
throw new Error('Usage: agentkit spec init --brief "<owner request>" [--out <path>] [--force] | agentkit spec check [--file <path>]');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (args.command === "inspect") {
|
|
472
|
+
const state = await inspectAgentCapsule(process.cwd());
|
|
473
|
+
console.log(JSON.stringify(state, null, 2));
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (args.command === "build") {
|
|
478
|
+
const target = resolveBuildTarget(parseTargetFlag(args.flags.target, "build"));
|
|
479
|
+
const result = await buildAgentCapsule(process.cwd(), { target: target.id });
|
|
480
|
+
printBuildResult(result);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (args.command === "login") {
|
|
485
|
+
const token = parseRequiredStringFlag(args.flags.token, "token", "agentkit login --token <token> [--api <url>]");
|
|
486
|
+
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
487
|
+
const verified = await verifyCloudLogin(apiUrl, token);
|
|
488
|
+
await writeCloudAuth({ apiUrl, token });
|
|
489
|
+
const email = verified.me.account?.email;
|
|
490
|
+
console.log(email ? `Logged in to AgentKit Cloud as ${email}.` : "AgentKit Cloud token verified and stored.");
|
|
491
|
+
|
|
492
|
+
const hasCloudflareAlpha = (verified.capabilities.capabilities ?? []).some(
|
|
493
|
+
(capability) => capability.name === "cloudflare_deploy_alpha" && capability.status === "active",
|
|
494
|
+
);
|
|
495
|
+
console.log(
|
|
496
|
+
hasCloudflareAlpha
|
|
497
|
+
? "Entitlement: cloudflare_deploy_alpha active."
|
|
498
|
+
: "Warning: cloudflare_deploy_alpha entitlement was not found for this account.",
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
if (process.env.AGENTKIT_CLOUD_API_TOKEN && process.env.AGENTKIT_CLOUD_API_TOKEN !== token) {
|
|
502
|
+
console.log("Warning: AGENTKIT_CLOUD_API_TOKEN is set and will override this saved login in the current shell.");
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (args.command === "logout") {
|
|
509
|
+
await clearCloudAuth();
|
|
510
|
+
console.log("AgentKit Cloud token removed.");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (args.command === "deploy") {
|
|
515
|
+
const [deploySubcommand] = args.positional;
|
|
516
|
+
|
|
517
|
+
if (deploySubcommand === "smoke") {
|
|
518
|
+
const message =
|
|
519
|
+
parseOptionalStringFlag(args.flags.message ?? args.flags.smoke, "message", 'agentkit deploy smoke --message "hello"') ??
|
|
520
|
+
"hello";
|
|
521
|
+
const state = await requireLocalDeployState();
|
|
522
|
+
const token = await readDeployAccessTokenForSmoke(process.cwd(), state);
|
|
523
|
+
await runDeploySmoke({
|
|
524
|
+
deployUrl: requireDeployUrl(state),
|
|
525
|
+
message,
|
|
526
|
+
token,
|
|
527
|
+
});
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (deploySubcommand === "doctor") {
|
|
532
|
+
const report = await checkDeployReadiness({
|
|
533
|
+
apiUrl: await resolveCloudApiUrl(args.flags.api),
|
|
534
|
+
requireLogin: !args.flags.anonymous,
|
|
535
|
+
});
|
|
536
|
+
printDeployReadinessReport(report);
|
|
537
|
+
|
|
538
|
+
if (!report.ok) {
|
|
539
|
+
process.exitCode = 1;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (deploySubcommand === "status" || deploySubcommand === "pause" || deploySubcommand === "resume") {
|
|
546
|
+
const state = await requireLocalDeployState();
|
|
547
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
548
|
+
|
|
549
|
+
if (deploySubcommand === "status") {
|
|
550
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}`, {
|
|
551
|
+
method: "GET",
|
|
552
|
+
});
|
|
553
|
+
printJson(payload);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}/${deploySubcommand}`, {
|
|
558
|
+
method: "POST",
|
|
559
|
+
});
|
|
560
|
+
printJson(payload);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const target = resolveDeployTarget(parseTargetFlag(args.flags.target, "deploy"));
|
|
565
|
+
const anonymous = Boolean(args.flags.anonymous);
|
|
566
|
+
const smokeMessage = parseOptionalStringFlag(args.flags.smoke, "smoke", 'agentkit deploy --smoke "hello"');
|
|
567
|
+
|
|
568
|
+
if (target.id === "vps") {
|
|
569
|
+
const host = parseRequiredStringFlag(args.flags.host, "host", "agentkit deploy --target vps --host <host>");
|
|
570
|
+
const container = await buildAgentCapsule(process.cwd(), { target: "container" });
|
|
571
|
+
const plan = await prepareVpsDeploy(container, { host, dryRun: Boolean(args.flags["dry-run"]) });
|
|
572
|
+
printBuildResult(container);
|
|
573
|
+
printVpsDeployPlan(plan);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (!args.flags["dry-run"] && !args.flags["local-wrangler"]) {
|
|
578
|
+
const report = await checkDeployReadiness({
|
|
579
|
+
apiUrl: await resolveCloudApiUrl(args.flags.api),
|
|
580
|
+
requireLogin: !anonymous,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
if (!report.ok) {
|
|
584
|
+
printDeployReadinessReport(report);
|
|
585
|
+
throw new AgentKitError(
|
|
586
|
+
"deploy_readiness_failed",
|
|
587
|
+
"Fix deploy readiness failures before running agentkit deploy.",
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const result = await buildAgentCapsule(process.cwd(), { target: target.id });
|
|
593
|
+
if (result.target !== "cloudflare") {
|
|
594
|
+
throw new Error(`Unsupported AgentKit Cloud deploy target: ${result.target}`);
|
|
595
|
+
}
|
|
596
|
+
printBuildResult(result);
|
|
597
|
+
|
|
598
|
+
if (args.flags["dry-run"]) {
|
|
599
|
+
console.log("");
|
|
600
|
+
console.log("Dry run only. Artifact is ready for AgentKit Cloud.");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (args.flags["local-wrangler"]) {
|
|
605
|
+
await deployCloudflare(result.wranglerPath, result.buildDir);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
610
|
+
const auth = await readCloudAuth();
|
|
611
|
+
const deploy = await deployBuildToAgentKitCloud(result, {
|
|
612
|
+
apiUrl,
|
|
613
|
+
apiToken: auth?.token,
|
|
614
|
+
anonymous: anonymous || !auth?.token,
|
|
615
|
+
});
|
|
616
|
+
const statePath = await writeLocalDeployState(result.root, deploy);
|
|
617
|
+
const chatAccess = await ensureDeployChatAccessToken({
|
|
618
|
+
apiUrl,
|
|
619
|
+
root: result.root,
|
|
620
|
+
deployId: deploy.deploy.id,
|
|
621
|
+
skip: anonymous || !auth?.token || deploy.deploy.access.mode === "public",
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
console.log("");
|
|
625
|
+
console.log("Deploy ready");
|
|
626
|
+
console.log(`URL: ${deploy.deploy.url}`);
|
|
627
|
+
console.log(`Project: ${deploy.project.id}`);
|
|
628
|
+
console.log(`Deploy: ${deploy.deploy.id}`);
|
|
629
|
+
console.log(`State: ${statePath}`);
|
|
630
|
+
|
|
631
|
+
if (chatAccess?.status === "ready") {
|
|
632
|
+
console.log(`Chat auth: ${relativePath(process.cwd(), chatAccess.path)}`);
|
|
633
|
+
} else if (chatAccess?.status === "skipped") {
|
|
634
|
+
console.log("Chat auth: public or anonymous deploy; no local chat token was created.");
|
|
635
|
+
} else if (chatAccess?.status === "failed") {
|
|
636
|
+
console.log(`Chat auth: could not create local chat token. ${formatReadinessError(chatAccess.error)}`);
|
|
637
|
+
console.log(`Run: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (smokeMessage) {
|
|
641
|
+
if (chatAccess?.status === "failed") {
|
|
642
|
+
throw new AgentKitError("deploy_smoke_failed", "Could not run deploy smoke because chat access token creation failed.");
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
await runDeploySmoke({
|
|
646
|
+
deployUrl: requireDeployUrl({
|
|
647
|
+
deploy_id: deploy.deploy.id,
|
|
648
|
+
url: deploy.deploy.url,
|
|
649
|
+
}),
|
|
650
|
+
message: smokeMessage,
|
|
651
|
+
token:
|
|
652
|
+
chatAccess?.status === "ready"
|
|
653
|
+
? await readDeployAccessTokenForUi(chatAccess.path)
|
|
654
|
+
: undefined,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (args.command === "secret") {
|
|
661
|
+
const [subcommand, name, ...valueParts] = args.positional;
|
|
662
|
+
const projectId = await resolveProjectIdForSecretCommand(process.cwd());
|
|
663
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
664
|
+
|
|
665
|
+
if (subcommand === "list") {
|
|
666
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
|
|
667
|
+
method: "GET",
|
|
668
|
+
});
|
|
669
|
+
printJson(payload);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (subcommand === "set") {
|
|
674
|
+
const value = await resolveSecretInputValue({
|
|
675
|
+
name,
|
|
676
|
+
valueParts,
|
|
677
|
+
stdinFlag: args.flags.stdin,
|
|
678
|
+
fromEnvFlag: args.flags["from-env"],
|
|
679
|
+
fromLocalEnvFlag: args.flags["from-local-env"],
|
|
680
|
+
localEnvRoot: args.flags["from-local-env"] !== undefined ? await findAgentCapsuleRoot(process.cwd()) : undefined,
|
|
681
|
+
usage: "agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env [--api <url>]",
|
|
682
|
+
inlineSensitiveHelp: "Prefer --stdin, --from-env, or --from-local-env so the value does not enter shell history.",
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
if (!name) {
|
|
686
|
+
throw new Error("Usage: agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env [--api <url>]");
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(name)}`, {
|
|
690
|
+
method: "PUT",
|
|
691
|
+
body: JSON.stringify({ value }),
|
|
692
|
+
});
|
|
693
|
+
printJson(payload);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (subcommand === "sync") {
|
|
698
|
+
if (args.flags["from-local"] !== true) {
|
|
699
|
+
throw new Error("Usage: agentkit secret sync --from-local [--api <url>]");
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
await syncHostedSecretsFromLocal({ apiUrl, projectId });
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (subcommand === "unset") {
|
|
707
|
+
if (!name) {
|
|
708
|
+
throw new Error("Usage: agentkit secret unset <NAME> [--api <url>]");
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(name)}`, {
|
|
712
|
+
method: "DELETE",
|
|
713
|
+
});
|
|
714
|
+
printJson(payload);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
throw new Error("Usage: agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env | agentkit secret sync --from-local | agentkit secret list | agentkit secret unset <NAME>");
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (args.command === "access") {
|
|
722
|
+
const [subcommand, noun, name] = args.positional;
|
|
723
|
+
|
|
724
|
+
if (subcommand !== "token") {
|
|
725
|
+
throw new Error("Usage: agentkit access token create <name> [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const state = await requireLocalDeployState();
|
|
729
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
730
|
+
|
|
731
|
+
if (noun === "create" && name) {
|
|
732
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}/access-tokens`, {
|
|
733
|
+
method: "POST",
|
|
734
|
+
body: JSON.stringify({ name }),
|
|
735
|
+
});
|
|
736
|
+
await printJsonOrWrite(payload, args.flags.out, "agentkit access token create <name> [--api <url>] [--out <path>]");
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (noun === "list" && !name) {
|
|
741
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}/access-tokens`, {
|
|
742
|
+
method: "GET",
|
|
743
|
+
});
|
|
744
|
+
printJson(payload);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (noun === "revoke" && name) {
|
|
749
|
+
const payload = await cloudApiRequest(
|
|
750
|
+
apiUrl,
|
|
751
|
+
`/v1/deploys/${encodeURIComponent(state.deploy_id)}/access-tokens/${encodeURIComponent(name)}/revoke`,
|
|
752
|
+
{ method: "POST" },
|
|
753
|
+
);
|
|
754
|
+
printJson(payload);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
throw new Error("Usage: agentkit access token create <name> [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (args.command === "channels") {
|
|
762
|
+
await handleChannelsCommand(args);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (args.command === "env") {
|
|
767
|
+
const [subcommand, name, ...valueParts] = args.positional;
|
|
768
|
+
const root = await findAgentCapsuleRoot(process.cwd());
|
|
769
|
+
|
|
770
|
+
if (subcommand === "list") {
|
|
771
|
+
const names = await listCapsuleEnv(root);
|
|
772
|
+
|
|
773
|
+
if (names.length === 0) {
|
|
774
|
+
console.log("No local env values set.");
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
for (const envName of names) {
|
|
779
|
+
console.log(`${envName}\tset`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (subcommand === "set") {
|
|
786
|
+
const value = await resolveSecretInputValue({
|
|
787
|
+
name,
|
|
788
|
+
valueParts,
|
|
789
|
+
stdinFlag: args.flags.stdin,
|
|
790
|
+
fromEnvFlag: args.flags["from-env"],
|
|
791
|
+
fromLocalEnvFlag: undefined,
|
|
792
|
+
usage: "agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME]",
|
|
793
|
+
inlineSensitiveHelp: "Prefer --stdin or --from-env so the value does not enter shell history.",
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
if (!name) {
|
|
797
|
+
throw new Error("Usage: agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME]");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
await setCapsuleEnv(root, name, value);
|
|
801
|
+
console.log(`${name}\tset`);
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (subcommand === "unset") {
|
|
806
|
+
if (!name) {
|
|
807
|
+
throw new Error("Usage: agentkit env unset <NAME>");
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const removed = await unsetCapsuleEnv(root, name);
|
|
811
|
+
console.log(`${name}\t${removed ? "unset" : "not_set"}`);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
throw new Error("Usage: agentkit env list | agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME] | agentkit env unset <NAME>");
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (args.command === "docs") {
|
|
819
|
+
const [subcommand] = args.positional;
|
|
820
|
+
const docs = findAgentKitDocs();
|
|
821
|
+
|
|
822
|
+
if (!subcommand || subcommand === "path") {
|
|
823
|
+
console.log(docs.root);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (subcommand === "llms") {
|
|
828
|
+
console.log(docs.llms);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (subcommand === "full") {
|
|
833
|
+
console.log(docs.llmsFull);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
throw new Error("Usage: agentkit docs path | agentkit docs llms | agentkit docs full");
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (args.command === "handoff") {
|
|
841
|
+
const [target = "codex", ...goalParts] = args.positional;
|
|
842
|
+
|
|
843
|
+
if (target !== "codex" && target !== "claude") {
|
|
844
|
+
throw new Error("Usage: agentkit handoff codex [goal] | agentkit handoff claude [goal]");
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const root = await findAgentCapsuleRoot(process.cwd());
|
|
848
|
+
const docs = findAgentKitDocs();
|
|
849
|
+
console.log(renderHandoffPrompt({ target, root, docsLlms: docs.llms, goal: goalParts.join(" ").trim() }));
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
throw new Error(`Unknown command: ${args.command}`);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function renderHandoffPrompt(input: { target: string; root: string; docsLlms: string; goal: string }): string {
|
|
857
|
+
const agentName = input.target === "claude" ? "Claude Code" : "Codex";
|
|
858
|
+
const skillRouter = join(input.root, "skills/agentkit-capsule/SKILL.md");
|
|
859
|
+
const skillLine = existsSync(skillRouter) ? `- ${skillRouter}\n` : "";
|
|
860
|
+
const goal = input.goal
|
|
861
|
+
? `Owner request:
|
|
862
|
+
${input.goal}`
|
|
863
|
+
: `Owner request:
|
|
864
|
+
Use the owner's next natural-language request as the product brief. If they ask for a domain-specific agent, build the first useful local version immediately.`;
|
|
865
|
+
|
|
866
|
+
return `You are ${agentName}, working inside this AgentKit Agent Capsule:
|
|
867
|
+
|
|
868
|
+
${input.root}
|
|
869
|
+
|
|
870
|
+
Read these files first:
|
|
871
|
+
|
|
872
|
+
- ${join(input.root, "AGENTKIT.md")}
|
|
873
|
+
${skillLine}- ${input.docsLlms}
|
|
874
|
+
|
|
875
|
+
${goal}
|
|
876
|
+
|
|
877
|
+
Rules:
|
|
878
|
+
- Start with the AgentKit capsule skill when it exists.
|
|
879
|
+
- Use llms.txt as the docs router.
|
|
880
|
+
- Read llms-full.txt only when a skill or ambiguous framework behavior requires the complete contract.
|
|
881
|
+
- Treat the owner's request as the brief.
|
|
882
|
+
- Build or update the agent inside this capsule as far as you can without asking for obvious restatements.
|
|
883
|
+
- Do not wait for a wizard or recipe; implement directly in the capsule files.
|
|
884
|
+
- Edit agent behavior in prompts/instructions.md.
|
|
885
|
+
- Edit the agent contract in agentkit.config.ts.
|
|
886
|
+
- Add TypeScript tools under tools/.
|
|
887
|
+
- Keep the first useful version runnable with test/fake unless the owner has already chosen a real provider.
|
|
888
|
+
- Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
|
|
889
|
+
- Keep local secrets in .env only.
|
|
890
|
+
- Do not commit or print secret values.
|
|
891
|
+
- Do not edit .agentkit/ directly.
|
|
892
|
+
- Make practical assumptions for a first local version and list those assumptions in your final response.
|
|
893
|
+
- Ask follow-up questions only when missing information blocks a safe local implementation.
|
|
894
|
+
|
|
895
|
+
Verification:
|
|
896
|
+
Run these before you finish:
|
|
897
|
+
|
|
898
|
+
\`\`\`sh
|
|
899
|
+
npm run typecheck
|
|
900
|
+
npm run agentkit -- inspect
|
|
901
|
+
npm run chat -- --message "hello"
|
|
902
|
+
\`\`\`
|
|
903
|
+
|
|
904
|
+
If you add a tool, also run a tool smoke test with the fake provider, for example:
|
|
905
|
+
|
|
906
|
+
\`\`\`sh
|
|
907
|
+
npm run agentkit -- tool tool_name --input '{}'
|
|
908
|
+
\`\`\`
|
|
909
|
+
|
|
910
|
+
UI testing:
|
|
911
|
+
- For local UI testing, run \`npm run dev\`, open the printed \`Chat:\` URL, and tell the owner the exact URL.
|
|
912
|
+
- 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.
|
|
913
|
+
- If the provider is still \`test/fake\`, say the UI was tested only with the deterministic fake provider.
|
|
914
|
+
|
|
915
|
+
Final response:
|
|
916
|
+
- Summarize what changed.
|
|
917
|
+
- List files changed.
|
|
918
|
+
- Paste the verification commands and results.
|
|
919
|
+
- State whether testing used \`test/fake\` or a real provider selected by the owner.
|
|
920
|
+
- Call out any missing user details or blocked checks.
|
|
921
|
+
`;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
925
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
async function ensureDeployChatAccessToken(options: {
|
|
929
|
+
apiUrl: string;
|
|
930
|
+
root: string;
|
|
931
|
+
deployId: string;
|
|
932
|
+
skip: boolean;
|
|
933
|
+
}): Promise<
|
|
934
|
+
| { status: "ready"; path: string; tokenId: string; created: boolean }
|
|
935
|
+
| { status: "skipped" }
|
|
936
|
+
| { status: "failed"; error: unknown }
|
|
937
|
+
> {
|
|
938
|
+
if (options.skip) {
|
|
939
|
+
return { status: "skipped" };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
try {
|
|
943
|
+
const path = join(options.root, defaultDeployChatAccessTokenPath);
|
|
944
|
+
const existing = await readDeployAccessTokenFile(path);
|
|
945
|
+
|
|
946
|
+
if (existing?.deploy_id === options.deployId && existing.id) {
|
|
947
|
+
const listed = (await cloudApiRequest(
|
|
948
|
+
options.apiUrl,
|
|
949
|
+
`/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
|
|
950
|
+
{ method: "GET" },
|
|
951
|
+
)) as DeployAccessTokenListResponse;
|
|
952
|
+
const stillExists = (listed.access_tokens ?? []).some((token) => token.id === existing.id);
|
|
953
|
+
|
|
954
|
+
if (stillExists) {
|
|
955
|
+
return { status: "ready", path, tokenId: existing.id, created: false };
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const created = (await cloudApiRequest(
|
|
960
|
+
options.apiUrl,
|
|
961
|
+
`/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
|
|
962
|
+
{
|
|
963
|
+
method: "POST",
|
|
964
|
+
body: JSON.stringify({ name: defaultDeployChatAccessTokenName }),
|
|
965
|
+
},
|
|
966
|
+
)) as DeployAccessTokenCreateResponse;
|
|
967
|
+
const token = created.access_token;
|
|
968
|
+
|
|
969
|
+
if (!token?.id || token.deploy_id !== options.deployId || !token.token) {
|
|
970
|
+
throw new AgentKitError("access_token_invalid", "AgentKit Cloud returned an invalid deploy access token payload.");
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
await writeSecretJson(path, created);
|
|
974
|
+
return { status: "ready", path, tokenId: token.id, created: true };
|
|
975
|
+
} catch (error) {
|
|
976
|
+
return { status: "failed", error };
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; deploy_id?: string } | null> {
|
|
981
|
+
try {
|
|
982
|
+
const parsed = JSON.parse(await readFile(path, "utf8")) as DeployAccessTokenCreateResponse;
|
|
983
|
+
return parsed.access_token ?? null;
|
|
984
|
+
} catch {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
async function writeSecretJson(path: string, value: unknown): Promise<void> {
|
|
990
|
+
await mkdir(dirname(path), { recursive: true });
|
|
991
|
+
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
async function requireLocalDeployState(): Promise<LocalDeployState> {
|
|
995
|
+
const state = await readLocalDeployStateIfExists(process.cwd());
|
|
996
|
+
|
|
997
|
+
if (!state) {
|
|
998
|
+
throw new Error("No .agentkit/deploy.json found. Run agentkit deploy first.");
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return state;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function requireDeployUrl(state: Pick<LocalDeployState, "deploy_id" | "url">): string {
|
|
1005
|
+
if (!state.url) {
|
|
1006
|
+
throw new Error(`Deploy ${state.deploy_id} does not have a URL. Run agentkit deploy again.`);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return state.url;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
async function readDeployAccessTokenForSmoke(cwd: string, state: LocalDeployState): Promise<string | undefined> {
|
|
1013
|
+
if (state.access?.mode === "public") {
|
|
1014
|
+
return undefined;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const root = await findAgentCapsuleRoot(cwd);
|
|
1018
|
+
return readDeployAccessTokenForUi(join(root, defaultDeployChatAccessTokenPath));
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
async function runDeploySmoke(input: { deployUrl: string; message: string; token?: string }): Promise<void> {
|
|
1022
|
+
const headers = new Headers({ "Content-Type": "application/json" });
|
|
1023
|
+
|
|
1024
|
+
if (input.token) {
|
|
1025
|
+
headers.set("Authorization", `Bearer ${input.token}`);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
let response: Response;
|
|
1029
|
+
|
|
1030
|
+
try {
|
|
1031
|
+
response = await fetch(new URL("/v1/chat", input.deployUrl).href, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers,
|
|
1034
|
+
body: JSON.stringify({ message: input.message }),
|
|
1035
|
+
});
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
throw new AgentKitError(
|
|
1038
|
+
"deploy_smoke_failed",
|
|
1039
|
+
`Deploy smoke request failed before a response: ${error instanceof Error ? error.message : String(error)}`,
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const payload = await response.json().catch(() => null);
|
|
1044
|
+
|
|
1045
|
+
if (!response.ok) {
|
|
1046
|
+
throw new AgentKitError("deploy_smoke_failed", readCloudError(payload, response.status));
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const content =
|
|
1050
|
+
payload && typeof payload === "object" && "message" in payload && isRecord((payload as { message?: unknown }).message)
|
|
1051
|
+
? (payload as { message: { content?: unknown } }).message.content
|
|
1052
|
+
: undefined;
|
|
1053
|
+
|
|
1054
|
+
console.log("");
|
|
1055
|
+
console.log("Deploy smoke verified");
|
|
1056
|
+
console.log(`URL: ${input.deployUrl}`);
|
|
1057
|
+
console.log(`Message: ${input.message}`);
|
|
1058
|
+
if (typeof content === "string") {
|
|
1059
|
+
console.log(`Response: ${content}`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function printJson(value: unknown): void {
|
|
1064
|
+
console.log(JSON.stringify(value, null, 2));
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
|
|
1068
|
+
const outPath = parseOptionalStringFlag(outFlag, "out", usage);
|
|
1069
|
+
|
|
1070
|
+
if (!outPath) {
|
|
1071
|
+
printJson(value);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const absolutePath = resolve(process.cwd(), outPath);
|
|
1076
|
+
await writeSecretJson(absolutePath, value);
|
|
1077
|
+
console.log(`Deploy access token written to ${absolutePath}`);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function relativePath(root: string, path: string): string {
|
|
1081
|
+
const output = relative(root, path);
|
|
1082
|
+
return output.startsWith("..") ? path : output || ".";
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function printBuildResult(result: Awaited<ReturnType<typeof buildAgentCapsule>>): void {
|
|
1086
|
+
console.log("AgentKit build complete");
|
|
1087
|
+
console.log("");
|
|
1088
|
+
console.log(`Target: ${result.target}`);
|
|
1089
|
+
console.log(`Build dir: ${result.buildDir}`);
|
|
1090
|
+
console.log(`Manifest: ${result.manifestPath}`);
|
|
1091
|
+
|
|
1092
|
+
if (result.target === "cloudflare") {
|
|
1093
|
+
console.log(`Worker: ${result.workerPath}`);
|
|
1094
|
+
console.log(`Wrangler: ${result.wranglerPath}`);
|
|
1095
|
+
if (result.tursoSchemaPath) {
|
|
1096
|
+
console.log(`Turso: ${result.tursoSchemaPath}`);
|
|
1097
|
+
}
|
|
1098
|
+
console.log(`Bundle: ${result.bundleSha256}`);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (result.target === "container") {
|
|
1102
|
+
console.log(`Server: ${result.serverPath}`);
|
|
1103
|
+
console.log(`Docker: ${result.dockerfilePath}`);
|
|
1104
|
+
console.log(`Migrations:${result.migrationsDir}`);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (result.warnings.length > 0) {
|
|
1108
|
+
console.log("");
|
|
1109
|
+
console.log("Warnings:");
|
|
1110
|
+
|
|
1111
|
+
for (const warning of result.warnings) {
|
|
1112
|
+
console.log(` - ${warning}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
function printVpsDeployPlan(plan: Awaited<ReturnType<typeof prepareVpsDeploy>>): void {
|
|
1118
|
+
console.log("");
|
|
1119
|
+
console.log("VPS deploy plan ready");
|
|
1120
|
+
console.log(`Host: ${plan.host}`);
|
|
1121
|
+
console.log(`Compose: ${plan.composePath}`);
|
|
1122
|
+
console.log(`Env: ${plan.envExamplePath}`);
|
|
1123
|
+
console.log(`Secrets: ${plan.secretsPath}`);
|
|
1124
|
+
console.log(`Handoff: ${plan.handoffPath}`);
|
|
1125
|
+
console.log(`State: ${plan.statePath}`);
|
|
1126
|
+
console.log("");
|
|
1127
|
+
console.log("Commands:");
|
|
1128
|
+
|
|
1129
|
+
for (const command of plan.commands) {
|
|
1130
|
+
console.log(` ${command}`);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
async function deployCloudflare(wranglerPath: string, buildDir: string): Promise<void> {
|
|
1135
|
+
const exitCode = await runInheritedCommand("npx", ["wrangler", "deploy", "--config", wranglerPath], buildDir);
|
|
1136
|
+
|
|
1137
|
+
if (exitCode !== 0) {
|
|
1138
|
+
throw new Error(`Cloudflare deploy failed with exit code ${exitCode}.`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
async function installProjectDependencies(projectPath: string): Promise<void> {
|
|
1143
|
+
const exitCode = await runInheritedCommand("npm", ["install"], projectPath);
|
|
1144
|
+
|
|
1145
|
+
if (exitCode !== 0) {
|
|
1146
|
+
throw new Error(`Dependency install failed with exit code ${exitCode}. Run \`npm install\` in ${projectPath}.`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
async function parseToolInput(
|
|
1151
|
+
cwd: string,
|
|
1152
|
+
inputFlag: string | boolean | undefined,
|
|
1153
|
+
inputJsonFlag: string | boolean | undefined,
|
|
1154
|
+
): Promise<unknown> {
|
|
1155
|
+
if (inputFlag === undefined && inputJsonFlag === undefined) {
|
|
1156
|
+
return {};
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
if (inputFlag !== undefined && inputJsonFlag !== undefined) {
|
|
1160
|
+
throw new Error("Use either --input <path-or-json> or --input-json <json>, not both.");
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if (inputJsonFlag !== undefined) {
|
|
1164
|
+
if (typeof inputJsonFlag !== "string") {
|
|
1165
|
+
throw new Error("Missing JSON. Usage: agentkit tool <name> --input-json '<json>'");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
return parseJsonInput(inputJsonFlag, "--input-json");
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (typeof inputFlag !== "string") {
|
|
1172
|
+
throw new Error("Missing input. Usage: agentkit tool <name> --input <path-or-json>");
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
const trimmed = inputFlag.trim();
|
|
1176
|
+
|
|
1177
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
1178
|
+
return parseJsonInput(trimmed, "--input");
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const contents = await readFile(resolve(cwd, inputFlag), "utf8");
|
|
1182
|
+
return parseJsonInput(contents, inputFlag);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function parseJsonInput(source: string, label: string): unknown {
|
|
1186
|
+
try {
|
|
1187
|
+
return JSON.parse(source);
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
throw new Error(`${label} must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
async function findRunningLocalDevServer(cwd: string): Promise<{ url: string; agent: string } | null> {
|
|
1194
|
+
const capsule = await loadAgentCapsule(cwd);
|
|
1195
|
+
const basePort = localDevBasePort();
|
|
1196
|
+
|
|
1197
|
+
for (let port = basePort; port <= basePort + 50; port += 1) {
|
|
1198
|
+
const state = await fetchLocalAgentKitState(`http://localhost:${port}/_agentkit`);
|
|
1199
|
+
|
|
1200
|
+
if (state?.agent === capsule.config.name) {
|
|
1201
|
+
return {
|
|
1202
|
+
url: `http://localhost:${port}`,
|
|
1203
|
+
agent: state.agent,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function localDevBasePort(): number {
|
|
1212
|
+
const value = process.env.AGENTKIT_DEV_BASE_PORT;
|
|
1213
|
+
|
|
1214
|
+
if (!value) {
|
|
1215
|
+
return defaultAgentKitDevPort;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const parsed = Number(value);
|
|
1219
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : defaultAgentKitDevPort;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
async function fetchLocalAgentKitState(url: string): Promise<{ agent?: string } | null> {
|
|
1223
|
+
const controller = new AbortController();
|
|
1224
|
+
const timeout = setTimeout(() => controller.abort(), 300);
|
|
1225
|
+
|
|
1226
|
+
try {
|
|
1227
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
1228
|
+
const payload = await response.json().catch(() => null);
|
|
1229
|
+
|
|
1230
|
+
if (payload && typeof payload === "object") {
|
|
1231
|
+
return {
|
|
1232
|
+
agent: typeof (payload as { agent?: unknown }).agent === "string" ? (payload as { agent: string }).agent : undefined,
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
} catch {
|
|
1236
|
+
return null;
|
|
1237
|
+
} finally {
|
|
1238
|
+
clearTimeout(timeout);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
async function openLocalUrl(url: string): Promise<void> {
|
|
1245
|
+
if (process.env.AGENTKIT_OPEN_COMMAND === "none") {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const command =
|
|
1250
|
+
process.env.AGENTKIT_OPEN_COMMAND ??
|
|
1251
|
+
(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open");
|
|
1252
|
+
const args = process.env.AGENTKIT_OPEN_COMMAND
|
|
1253
|
+
? [url]
|
|
1254
|
+
: process.platform === "win32"
|
|
1255
|
+
? ["/c", "start", "", url]
|
|
1256
|
+
: [url];
|
|
1257
|
+
|
|
1258
|
+
await new Promise<void>((resolvePromise, reject) => {
|
|
1259
|
+
const child = spawn(command, args, {
|
|
1260
|
+
detached: true,
|
|
1261
|
+
stdio: "ignore",
|
|
1262
|
+
});
|
|
1263
|
+
child.once("error", reject);
|
|
1264
|
+
child.once("spawn", () => {
|
|
1265
|
+
child.unref();
|
|
1266
|
+
resolvePromise();
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
main().catch((error: unknown) => {
|
|
1272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1273
|
+
const prefix = isAgentKitError(error) ? `agentkit ${error.code}` : "agentkit";
|
|
1274
|
+
console.error(`${prefix}: ${message}`);
|
|
1275
|
+
process.exitCode = 1;
|
|
1276
|
+
});
|