@andreprado/agentkit 0.1.0-alpha.2 → 0.1.0-alpha.20
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 +67 -6
- package/docs/guides/add-channel.md +118 -7
- package/docs/guides/add-knowledge.md +144 -0
- package/docs/guides/add-managed-composio.md +163 -0
- package/docs/guides/add-tool.md +1 -1
- package/docs/guides/channel-security.md +97 -32
- package/docs/guides/connect-discord.md +178 -0
- package/docs/guides/connect-slack.md +126 -0
- package/docs/guides/connect-telegram.md +78 -1
- package/docs/guides/connect-whatsapp-zapster.md +112 -8
- package/docs/guides/create-agent.md +45 -4
- package/docs/guides/debug-channel.md +147 -0
- package/docs/guides/improve-from-production.md +151 -0
- package/docs/guides/prepare-deploy.md +47 -17
- package/docs/guides/replay-production-traces.md +72 -0
- package/docs/guides/run-evals.md +147 -20
- package/docs/guides/security-rules.md +7 -6
- package/docs/guides/send-feedback.md +135 -0
- package/docs/guides/use-provider.md +27 -3
- package/docs/llms-full.txt +303 -55
- package/docs/llms.txt +57 -7
- package/package.json +2 -5
- package/src/cli/args.ts +57 -0
- package/src/cli/cloud-client.ts +377 -0
- package/src/cli/commands/channels.ts +1315 -0
- package/src/cli/commands/feedback.ts +438 -0
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/constants.ts +4 -0
- package/src/cli/deploy-chat-ui.ts +535 -0
- package/src/cli/deploy-readiness.ts +481 -0
- package/src/cli/flags.ts +162 -0
- package/src/cli/help.ts +236 -0
- package/src/cli/index.ts +1167 -1005
- package/src/cli/process.ts +31 -0
- package/src/cloud/artifact.ts +139 -0
- package/src/cloud/client.ts +80 -0
- package/src/cloud/contracts.ts +63 -0
- package/src/cloud/index.ts +3 -0
- package/src/create-project.ts +21 -6
- package/src/index.ts +479 -7
- package/src/providers/pi.ts +70 -16
- package/src/providers/test.ts +88 -1
- package/src/providers/types.ts +7 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channel-test-harness.ts +8 -1
- package/src/runtime/channels/discord.ts +896 -0
- package/src/runtime/channels/slack.ts +646 -0
- package/src/runtime/channels/telegram.ts +466 -23
- package/src/runtime/channels/whatsapp-meta.ts +9 -0
- package/src/runtime/channels/whatsapp-zapster.ts +677 -40
- package/src/runtime/channels.ts +86 -3
- package/src/runtime/chat.ts +130 -38
- package/src/runtime/config.ts +483 -18
- package/src/runtime/core/manifest.ts +103 -5
- package/src/runtime/core/targets.ts +5 -5
- package/src/runtime/database.ts +93 -2
- package/src/runtime/db-commands.ts +9 -0
- package/src/runtime/deploy-readiness.ts +46 -4
- package/src/runtime/deploy.ts +1 -1
- package/src/runtime/dev-server.ts +759 -41
- package/src/runtime/env.ts +8 -3
- package/src/runtime/evals.ts +589 -43
- package/src/runtime/improve.ts +868 -0
- package/src/runtime/inspect.ts +194 -4
- package/src/runtime/integrations/composio.ts +423 -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 +303 -0
- package/src/runtime/knowledge/schema.ts +100 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/prompt-context.ts +141 -0
- package/src/runtime/runtime-contract.ts +86 -8
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +1430 -185
- package/src/runtime/targets/container/server.ts +1 -1
- package/src/runtime/targets/vps/deploy.ts +26 -9
- package/src/runtime/tool-runner.ts +9 -1
- package/src/runtime/tools.ts +128 -2
- package/src/runtime/traces.ts +41 -0
- package/src/runtime/transcription.ts +483 -0
- package/src/storage/sqlite.ts +149 -3
- package/src/templates/blank.ts +76 -17
- package/src/templates/dentista.ts +1011 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/skills/agentkit-build-agent/SKILL.md +52 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +21 -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 +70 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +104 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +65 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +66 -0
- package/src/templates/skills/agentkit-channels/references/discord.md +93 -0
- package/src/templates/skills/agentkit-channels/references/slack.md +56 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +72 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +77 -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 +50 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +109 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +29 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +27 -0
- package/src/templates/skills/agentkit-improve/SKILL.md +86 -0
- package/src/templates/skills/agentkit-improve/references/replay-side-effects.md +18 -0
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +22 -0
- package/src/templates/skills/agentkit-improve/templates/regression.eval.md +18 -0
- package/src/templates/skills/agentkit-integrations/SKILL.md +76 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +43 -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 +47 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +60 -0
- package/src/templates/skills/agentkit-security/SKILL.md +56 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +37 -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 +76 -0
- package/src/templates/support.ts +77 -18
- package/docs/guides/channels-production-handoff.md +0 -99
- package/docs/portable-deploy-release-checklist.md +0 -41
- package/src/runtime/targets/cloudflare/deploy.ts +0 -5475
package/src/cli/index.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
import { createProject } from "../create-project";
|
|
7
7
|
import { buildAgentCapsule } from "../runtime/build";
|
|
8
8
|
import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
|
|
9
9
|
import { readLocalDeployStateIfExists, type LocalDeployState } from "../runtime/core/deploy-state";
|
|
10
|
-
import {
|
|
10
|
+
import { resolveBuildTarget, resolveDeployTarget } from "../runtime/core/targets";
|
|
11
11
|
import { getConversationFromCwd, listConversationsFromCwd } from "../runtime/conversations";
|
|
12
|
+
import { getConversationTraceFromCwd } from "../runtime/traces";
|
|
12
13
|
import { runAgentMessageFromCwd } from "../runtime/chat";
|
|
13
14
|
import { deployBuildToAgentKitCloud, writeLocalDeployState } from "../runtime/deploy";
|
|
14
15
|
import {
|
|
@@ -17,182 +18,205 @@ import {
|
|
|
17
18
|
resetLocalDatabase,
|
|
18
19
|
seedLocalDatabase,
|
|
19
20
|
} from "../runtime/db-commands";
|
|
20
|
-
import { runEvalsFromCwd } from "../runtime/evals";
|
|
21
|
+
import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
|
|
21
22
|
import { findAgentKitDocs } from "../runtime/docs";
|
|
22
23
|
import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
|
|
23
|
-
import {
|
|
24
|
+
import { collectDeployImproveBundle, collectLocalImproveBundle, generateImproveEvals, replayImproveBundle } from "../runtime/improve";
|
|
25
|
+
import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
|
|
26
|
+
import { checkDefaultSkillStatus, syncDefaultSkillFiles } from "../runtime/skills";
|
|
27
|
+
import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
|
|
24
28
|
import { runToolFromCwd } from "../runtime/tool-runner";
|
|
25
29
|
import { startAgentDevServer } from "../runtime/dev-server";
|
|
26
30
|
import { inspectAgentCapsule } from "../runtime/inspect";
|
|
27
31
|
import { AgentKitError, isAgentKitError } from "../runtime/errors";
|
|
28
32
|
import { prepareVpsDeploy } from "../runtime/targets/vps/deploy";
|
|
33
|
+
import { parseArgs, isHelpRequested, type ParsedArgs } from "./args";
|
|
34
|
+
import {
|
|
35
|
+
defaultAgentKitDevPort,
|
|
36
|
+
defaultDeployChatAccessTokenName,
|
|
37
|
+
defaultDeployChatAccessTokenPath,
|
|
38
|
+
} from "./constants";
|
|
39
|
+
import {
|
|
40
|
+
clearCloudAuth,
|
|
41
|
+
cloudApiRequest,
|
|
42
|
+
readCloudAuth,
|
|
43
|
+
readCloudAuthForApiUrl,
|
|
44
|
+
readCloudError,
|
|
45
|
+
resolveCloudApiUrl,
|
|
46
|
+
verifyCloudLogin,
|
|
47
|
+
writeCloudAuth,
|
|
48
|
+
type AccountApiTokenCreateResponse,
|
|
49
|
+
type AccountApiTokenListResponse,
|
|
50
|
+
type BillingCheckoutIntentResponse,
|
|
51
|
+
type BillingCheckoutResponse,
|
|
52
|
+
type BillingClaimResponse,
|
|
53
|
+
type BillingPortalResponse,
|
|
54
|
+
type CloudMeResponse,
|
|
55
|
+
type DeployAccessTokenCreateResponse,
|
|
56
|
+
type DeployAccessTokenListResponse,
|
|
57
|
+
} from "./cloud-client";
|
|
58
|
+
import {
|
|
59
|
+
checkDeployReadiness,
|
|
60
|
+
formatReadinessError,
|
|
61
|
+
printDeployReadinessReport,
|
|
62
|
+
resolveProjectIdForSecretCommand,
|
|
63
|
+
syncHostedSecretsFromLocal,
|
|
64
|
+
} from "./deploy-readiness";
|
|
65
|
+
import { readDeployAccessTokenForUi, startDeployChatUiServer } from "./deploy-chat-ui";
|
|
66
|
+
import {
|
|
67
|
+
parseCloudApiUrl,
|
|
68
|
+
parseOptionalStringFlag,
|
|
69
|
+
parsePort,
|
|
70
|
+
parseRequiredStringFlag,
|
|
71
|
+
parseTargetFlag,
|
|
72
|
+
resolveSecretInputValue,
|
|
73
|
+
} from "./flags";
|
|
74
|
+
import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
|
|
75
|
+
import { runInheritedCommand, waitForShutdown } from "./process";
|
|
76
|
+
import { handleChannelsCommand } from "./commands/channels";
|
|
77
|
+
import { handleFeedbackCommand } from "./commands/feedback";
|
|
78
|
+
import { handleKnowledgeCommand } from "./commands/knowledge";
|
|
79
|
+
import { handleTranscribeCommand } from "./commands/transcribe";
|
|
80
|
+
|
|
81
|
+
const hostedDeployEntitlement = "agentkit_cloud_deploy";
|
|
29
82
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function renderHelp(): string {
|
|
33
|
-
return `AgentKit
|
|
34
|
-
|
|
35
|
-
Usage:
|
|
36
|
-
agentkit new <name> [--template blank|support]
|
|
37
|
-
agentkit dev
|
|
38
|
-
agentkit chat --message <text>
|
|
39
|
-
agentkit tool <name> [--input <path-or-json>]
|
|
40
|
-
agentkit db migrate
|
|
41
|
-
agentkit db reset --yes
|
|
42
|
-
agentkit db shell
|
|
43
|
-
agentkit db seed [--file <path>]
|
|
44
|
-
agentkit eval run
|
|
45
|
-
agentkit conversations list
|
|
46
|
-
agentkit conversations show <conversation-id>
|
|
47
|
-
agentkit channels list
|
|
48
|
-
agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
|
|
49
|
-
agentkit channels setup <name> [--apply] [--api <url>]
|
|
50
|
-
agentkit channels status <name> [--api <url>]
|
|
51
|
-
agentkit channels test <name> [--message <text>] [--fixture <path>] [--api <url>]
|
|
52
|
-
agentkit channels deliveries list <name> [--api <url>]
|
|
53
|
-
agentkit channels deliveries show <delivery-id> [--api <url>]
|
|
54
|
-
agentkit inspect
|
|
55
|
-
agentkit build [--target cloudflare]
|
|
56
|
-
agentkit login --token <token> [--api <url>]
|
|
57
|
-
agentkit logout
|
|
58
|
-
agentkit deploy [--target cloudflare|vps] [--host <host>] [--api <url>] [--dry-run] [--anonymous] [--local-wrangler]
|
|
59
|
-
agentkit deploy doctor [--api <url>] [--anonymous]
|
|
60
|
-
agentkit deploy status [--api <url>]
|
|
61
|
-
agentkit deploy pause [--api <url>]
|
|
62
|
-
agentkit deploy resume [--api <url>]
|
|
63
|
-
agentkit secret set <NAME> <VALUE> [--api <url>]
|
|
64
|
-
agentkit secret list [--api <url>]
|
|
65
|
-
agentkit secret unset <NAME> [--api <url>]
|
|
66
|
-
agentkit access token create <name> [--api <url>]
|
|
67
|
-
agentkit access token list [--api <url>]
|
|
68
|
-
agentkit access token revoke <token-id> [--api <url>]
|
|
69
|
-
agentkit env set <NAME> <VALUE>
|
|
70
|
-
agentkit env list
|
|
71
|
-
agentkit env unset <NAME>
|
|
72
|
-
agentkit docs path
|
|
73
|
-
agentkit docs llms
|
|
74
|
-
agentkit docs full
|
|
75
|
-
agentkit handoff codex [goal]
|
|
76
|
-
agentkit --help
|
|
77
|
-
|
|
78
|
-
Commands:
|
|
79
|
-
new Create a new Agent Capsule
|
|
80
|
-
dev Run the Agent Capsule locally
|
|
81
|
-
chat Send one message to the local Agent Capsule
|
|
82
|
-
tool Run one registered tool directly
|
|
83
|
-
db Manage the local SQLite database
|
|
84
|
-
eval Run local eval files
|
|
85
|
-
conversations List and inspect stored local conversations
|
|
86
|
-
channels Manage hosted channel resources
|
|
87
|
-
inspect Print machine-readable Agent Capsule state
|
|
88
|
-
build Build a hosted deploy artifact
|
|
89
|
-
login Store an AgentKit Cloud alpha token outside the capsule
|
|
90
|
-
logout Remove the stored AgentKit Cloud alpha token
|
|
91
|
-
deploy Build and send a hosted artifact to AgentKit Cloud
|
|
92
|
-
secret Manage hosted AgentKit Cloud secrets without readback
|
|
93
|
-
access Manage hosted deploy access tokens
|
|
94
|
-
env Manage local .env values without opening code
|
|
95
|
-
docs Print AgentKit docs locations
|
|
96
|
-
handoff Print a ready-to-paste prompt for another coding agent
|
|
97
|
-
|
|
98
|
-
Examples:
|
|
99
|
-
agentkit new support-agent --template support
|
|
100
|
-
cd support-agent
|
|
101
|
-
agentkit dev
|
|
102
|
-
agentkit tool lookup_order --input '{"orderId":"A100"}'
|
|
103
|
-
agentkit eval run
|
|
104
|
-
`;
|
|
105
|
-
}
|
|
83
|
+
async function main() {
|
|
84
|
+
const args = parseArgs(process.argv.slice(2));
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
};
|
|
86
|
+
if (!args.command || args.command === "--help" || args.command === "-h") {
|
|
87
|
+
console.log(renderHelp());
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
91
|
+
if (isHelpRequested(args)) {
|
|
92
|
+
console.log(renderCommandHelp(args.command));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
117
95
|
|
|
118
|
-
|
|
119
|
-
const
|
|
96
|
+
if (args.command === "help") {
|
|
97
|
+
const [topic] = args.positional;
|
|
120
98
|
|
|
121
|
-
if (!
|
|
122
|
-
|
|
123
|
-
|
|
99
|
+
if (!topic) {
|
|
100
|
+
console.log(renderHelp());
|
|
101
|
+
return;
|
|
124
102
|
}
|
|
125
103
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (next && !next.startsWith("--")) {
|
|
130
|
-
flags[name] = next;
|
|
131
|
-
index += 1;
|
|
132
|
-
} else {
|
|
133
|
-
flags[name] = true;
|
|
104
|
+
if (topic === "commands") {
|
|
105
|
+
console.log(renderCommandReference());
|
|
106
|
+
return;
|
|
134
107
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return { command, positional, flags };
|
|
138
|
-
}
|
|
139
108
|
|
|
140
|
-
|
|
141
|
-
const args = parseArgs(process.argv.slice(2));
|
|
142
|
-
|
|
143
|
-
if (!args.command || args.command === "--help" || args.command === "-h") {
|
|
144
|
-
console.log(renderHelp());
|
|
145
|
-
return;
|
|
109
|
+
throw new Error("Usage: agentkit help [commands]");
|
|
146
110
|
}
|
|
147
111
|
|
|
148
112
|
if (args.command === "new") {
|
|
149
113
|
const [name] = args.positional;
|
|
150
114
|
const template = String(args.flags.template ?? "blank");
|
|
115
|
+
const shouldInstall = args.flags["no-install"] !== true;
|
|
151
116
|
|
|
152
117
|
if (!name) {
|
|
153
|
-
throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support]");
|
|
118
|
+
throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support|dentista] [--no-install]");
|
|
154
119
|
}
|
|
155
120
|
|
|
156
121
|
const result = await createProject({ name, template, cwd: process.cwd() });
|
|
157
122
|
console.log(`Created ${result.name} at ${result.path}`);
|
|
123
|
+
|
|
124
|
+
if (shouldInstall) {
|
|
125
|
+
console.log("");
|
|
126
|
+
console.log("Installing dependencies...");
|
|
127
|
+
await installProjectDependencies(result.path);
|
|
128
|
+
}
|
|
129
|
+
|
|
158
130
|
console.log("");
|
|
159
131
|
console.log("Next:");
|
|
160
132
|
if (resolve(result.path) !== resolve(process.cwd())) {
|
|
161
133
|
console.log(` cd ${result.name}`);
|
|
162
134
|
}
|
|
163
|
-
|
|
135
|
+
if (!shouldInstall) {
|
|
136
|
+
console.log(" npm install");
|
|
137
|
+
}
|
|
164
138
|
console.log(" open this folder in Codex or Claude Code");
|
|
139
|
+
console.log(" coding agents start at skills/agentkit-capsule/SKILL.md");
|
|
165
140
|
console.log(' ask: "Develop an appointment and intake agent for an ophthalmology office"');
|
|
166
141
|
console.log(' npm run chat -- --message "hello"');
|
|
167
142
|
console.log(" npm run dev");
|
|
168
143
|
console.log(" npm run agentkit -- deploy");
|
|
169
144
|
console.log("");
|
|
170
|
-
console.log("Optional: manage local secrets with `npm run agentkit -- env set <NAME>
|
|
145
|
+
console.log("Optional: manage local secrets with `printf %s \"$VALUE\" | npm run agentkit -- env set <NAME> --stdin`.");
|
|
171
146
|
return;
|
|
172
147
|
}
|
|
173
148
|
|
|
174
149
|
if (args.command === "dev") {
|
|
175
|
-
const port = parsePort(args.flags.port);
|
|
176
|
-
const devServer = await startAgentDevServer(process.cwd(), { port });
|
|
150
|
+
const port = parsePort(args.flags.port, "agentkit dev --port <number>");
|
|
151
|
+
const devServer = await startAgentDevServer(process.cwd(), { port, basePort: localDevBasePort() });
|
|
177
152
|
|
|
178
153
|
console.log("Agent Capsule running");
|
|
179
154
|
console.log("");
|
|
155
|
+
if (devServer.portConflict) {
|
|
156
|
+
const owner = devServer.portConflict.agent ? ` by ${devServer.portConflict.agent}` : "";
|
|
157
|
+
console.log(
|
|
158
|
+
`Port ${devServer.portConflict.port} is already used${owner}. Started ${devServer.capsule.config.name} at ${devServer.urls.chat}.`,
|
|
159
|
+
);
|
|
160
|
+
console.log("");
|
|
161
|
+
}
|
|
180
162
|
console.log(`Chat: ${devServer.urls.chat}`);
|
|
181
163
|
console.log(`API: ${devServer.urls.api}`);
|
|
182
164
|
console.log(`Inspect: ${devServer.urls.inspect}`);
|
|
183
165
|
console.log(`Storage: ${devServer.storage ?? "managed"}`);
|
|
166
|
+
console.log("Reload: prompts/config reload per request; restart after changing tool modules or dependencies");
|
|
184
167
|
await waitForShutdown(devServer.stop);
|
|
185
168
|
return;
|
|
186
169
|
}
|
|
187
170
|
|
|
171
|
+
if (args.command === "open") {
|
|
172
|
+
const server = await findRunningLocalDevServer(process.cwd());
|
|
173
|
+
|
|
174
|
+
if (!server) {
|
|
175
|
+
console.log("No running AgentKit dev server found for this capsule.");
|
|
176
|
+
console.log("Run: npm run dev");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
await openLocalUrl(server.url);
|
|
181
|
+
console.log(`Opened ${server.url}`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (args.command === "chat-ui") {
|
|
186
|
+
if (!args.flags.deploy) {
|
|
187
|
+
throw new Error("Usage: agentkit chat-ui --deploy [--port <number>] [--token-file <path>]");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const port = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>") ?? 4124;
|
|
191
|
+
const tokenFile = parseOptionalStringFlag(
|
|
192
|
+
args.flags["token-file"],
|
|
193
|
+
"token-file",
|
|
194
|
+
"agentkit chat-ui --deploy --token-file <path>",
|
|
195
|
+
);
|
|
196
|
+
const server = await startDeployChatUiServer(process.cwd(), { port, tokenFile });
|
|
197
|
+
|
|
198
|
+
console.log("AgentKit hosted chat UI running");
|
|
199
|
+
console.log("");
|
|
200
|
+
console.log(`Chat: ${server.url}`);
|
|
201
|
+
console.log(`Production: ${server.deployUrl}`);
|
|
202
|
+
console.log(`Token: ${server.tokenSource}`);
|
|
203
|
+
await waitForShutdown(server.stop);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
if (args.command === "chat") {
|
|
189
208
|
const message = args.flags.message;
|
|
209
|
+
const conversationId = parseOptionalStringFlag(
|
|
210
|
+
args.flags["conversation-id"] ?? args.flags.conversation,
|
|
211
|
+
"conversation id",
|
|
212
|
+
'agentkit chat --message "hello" [--conversation-id <id>]',
|
|
213
|
+
);
|
|
190
214
|
|
|
191
215
|
if (typeof message !== "string") {
|
|
192
|
-
throw new Error('Missing message. Usage: agentkit chat --message "hello"');
|
|
216
|
+
throw new Error('Missing message. Usage: agentkit chat --message "hello" [--conversation-id <id>]');
|
|
193
217
|
}
|
|
194
218
|
|
|
195
|
-
const result = await runAgentMessageFromCwd(process.cwd(), { message });
|
|
219
|
+
const result = await runAgentMessageFromCwd(process.cwd(), { message, conversationId });
|
|
196
220
|
console.log(result.message.content);
|
|
197
221
|
return;
|
|
198
222
|
}
|
|
@@ -217,6 +241,8 @@ async function main() {
|
|
|
217
241
|
const result = await migrateLocalDatabase(process.cwd());
|
|
218
242
|
console.log("Local database migrated");
|
|
219
243
|
console.log(`Database: ${result.databasePath}`);
|
|
244
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
245
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
220
246
|
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
221
247
|
return;
|
|
222
248
|
}
|
|
@@ -230,6 +256,8 @@ async function main() {
|
|
|
230
256
|
console.log("Local database reset");
|
|
231
257
|
console.log(`Deleted: ${result.resetPath}`);
|
|
232
258
|
console.log(`Database: ${result.databasePath}`);
|
|
259
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
260
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
233
261
|
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
234
262
|
return;
|
|
235
263
|
}
|
|
@@ -252,6 +280,8 @@ async function main() {
|
|
|
252
280
|
const result = await seedLocalDatabase(process.cwd(), seedFile);
|
|
253
281
|
console.log("Local database seeded");
|
|
254
282
|
console.log(`Database: ${result.databasePath}`);
|
|
283
|
+
console.log(`Migrations: ${result.migrationsPath ?? "none"}`);
|
|
284
|
+
console.log(`Applied: ${result.migrationsApplied.length > 0 ? result.migrationsApplied.join(", ") : "none"}`);
|
|
255
285
|
console.log(`Schema: ${result.schemaPath ?? "none"}`);
|
|
256
286
|
console.log(`Seed: ${result.seedPath}`);
|
|
257
287
|
return;
|
|
@@ -261,10 +291,39 @@ async function main() {
|
|
|
261
291
|
}
|
|
262
292
|
|
|
263
293
|
if (args.command === "eval") {
|
|
264
|
-
const [subcommand] = args.positional;
|
|
294
|
+
const [subcommand, conversationId] = args.positional;
|
|
295
|
+
|
|
296
|
+
if (subcommand === "from-conversation") {
|
|
297
|
+
if (!conversationId) {
|
|
298
|
+
throw new Error("Missing conversation id. Usage: agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const out = parseOptionalStringFlag(
|
|
302
|
+
args.flags.out,
|
|
303
|
+
"out",
|
|
304
|
+
"agentkit eval from-conversation <conversation-id> --out <path>",
|
|
305
|
+
);
|
|
306
|
+
const name = parseOptionalStringFlag(
|
|
307
|
+
args.flags.name,
|
|
308
|
+
"name",
|
|
309
|
+
"agentkit eval from-conversation <conversation-id> --name <name>",
|
|
310
|
+
);
|
|
311
|
+
const result = await writeEvalFromConversation(process.cwd(), {
|
|
312
|
+
conversationId,
|
|
313
|
+
out,
|
|
314
|
+
name,
|
|
315
|
+
force: args.flags.force === true,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
console.log("Eval created from conversation");
|
|
319
|
+
console.log(`Conversation: ${result.conversationId}`);
|
|
320
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
321
|
+
console.log(`Turns: ${result.turns}`);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
265
324
|
|
|
266
325
|
if (subcommand !== "run") {
|
|
267
|
-
throw new Error("Usage: agentkit eval run");
|
|
326
|
+
throw new Error("Usage: agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]");
|
|
268
327
|
}
|
|
269
328
|
|
|
270
329
|
const summary = await runEvalsFromCwd(process.cwd());
|
|
@@ -287,10 +346,132 @@ async function main() {
|
|
|
287
346
|
return;
|
|
288
347
|
}
|
|
289
348
|
|
|
349
|
+
if (args.command === "improve") {
|
|
350
|
+
const [subcommand, bundlePath] = args.positional;
|
|
351
|
+
|
|
352
|
+
if (subcommand === "collect") {
|
|
353
|
+
const since = parseOptionalStringFlag(args.flags.since, "since", 'agentkit improve collect --since "24h"');
|
|
354
|
+
const conversationId = parseOptionalStringFlag(
|
|
355
|
+
args.flags["conversation-id"] ?? args.flags.conversation,
|
|
356
|
+
"conversation id",
|
|
357
|
+
"agentkit improve collect --conversation-id <id>",
|
|
358
|
+
);
|
|
359
|
+
const out = parseOptionalStringFlag(args.flags.out, "out", "agentkit improve collect --out <directory>");
|
|
360
|
+
const deployState = args.flags.deploy === true ? await requireLocalDeployState() : null;
|
|
361
|
+
const apiUrl = args.flags.deploy === true ? await resolveCloudApiUrl(args.flags.api) : null;
|
|
362
|
+
const cloudAuth = apiUrl ? await readCloudAuthForApiUrl(apiUrl) : null;
|
|
363
|
+
const result = args.flags.deploy === true
|
|
364
|
+
? await collectDeployImproveBundle(process.cwd(), {
|
|
365
|
+
deployId: deployState?.deploy_id ?? "",
|
|
366
|
+
since,
|
|
367
|
+
conversationId,
|
|
368
|
+
out,
|
|
369
|
+
collector: {
|
|
370
|
+
...(cloudAuth && apiUrl && deployState
|
|
371
|
+
? {
|
|
372
|
+
exportEvidence: (input: { deployId: string; since?: string; conversationId?: string }) =>
|
|
373
|
+
fetchDeployImproveEvidence(apiUrl, input),
|
|
374
|
+
}
|
|
375
|
+
: {}),
|
|
376
|
+
listConversations: () => fetchDeployConversationJson(process.cwd(), "/v1/conversations"),
|
|
377
|
+
getTrace: (id: string) =>
|
|
378
|
+
fetchDeployConversationJson(process.cwd(), `/v1/conversations/${encodeURIComponent(id)}/trace`),
|
|
379
|
+
},
|
|
380
|
+
})
|
|
381
|
+
: await collectLocalImproveBundle(process.cwd(), {
|
|
382
|
+
since,
|
|
383
|
+
conversationId,
|
|
384
|
+
out,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
console.log("Improve bundle collected");
|
|
388
|
+
console.log(`Source: ${result.source}`);
|
|
389
|
+
console.log(`Directory: ${relativePath(result.root, result.directory)}`);
|
|
390
|
+
console.log(`Bundle: ${relativePath(result.root, result.bundlePath)}`);
|
|
391
|
+
console.log(`Report: ${relativePath(result.root, result.reportPath)}`);
|
|
392
|
+
console.log(`Traces: ${result.traceCount}`);
|
|
393
|
+
console.log("");
|
|
394
|
+
console.log(`Next: agentkit improve evals ${relativePath(result.root, result.directory)}`);
|
|
395
|
+
console.log(`Replay: agentkit replay ${relativePath(result.root, result.directory)} --against local`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (subcommand === "evals") {
|
|
400
|
+
if (!bundlePath) {
|
|
401
|
+
throw new Error("Missing bundle path. Usage: agentkit improve evals <bundle-dir-or-json> [--force]");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const result = await generateImproveEvals(process.cwd(), {
|
|
405
|
+
bundle: bundlePath,
|
|
406
|
+
force: args.flags.force === true,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
console.log("Improve evals generated");
|
|
410
|
+
console.log(`Bundle: ${relativePath(result.root, result.bundlePath)}`);
|
|
411
|
+
console.log(`Files: ${result.files.length}`);
|
|
412
|
+
for (const file of result.files) {
|
|
413
|
+
console.log(` ${relativePath(result.root, file)}`);
|
|
414
|
+
}
|
|
415
|
+
if (result.skipped.length > 0) {
|
|
416
|
+
console.log(`Skipped: ${result.skipped.length}`);
|
|
417
|
+
for (const skipped of result.skipped) {
|
|
418
|
+
console.log(` ${skipped.trace_id}: ${skipped.reason}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
throw new Error("Usage: agentkit improve collect [--deploy] [--since <duration|iso>] [--conversation-id <id>] [--out <directory>] | agentkit improve evals <bundle-dir-or-json> [--force]");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (args.command === "replay") {
|
|
428
|
+
const [bundlePath] = args.positional;
|
|
429
|
+
|
|
430
|
+
if (!bundlePath) {
|
|
431
|
+
throw new Error("Missing bundle path. Usage: agentkit replay <bundle-dir-or-json> --against local");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const against = parseOptionalStringFlag(args.flags.against, "against", "agentkit replay <bundle> --against local") ?? "local";
|
|
435
|
+
|
|
436
|
+
if (against !== "local") {
|
|
437
|
+
throw new Error("Usage: agentkit replay <bundle-dir-or-json> --against local");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const result = await replayImproveBundle(process.cwd(), {
|
|
441
|
+
bundle: bundlePath,
|
|
442
|
+
against,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
for (const item of result.results) {
|
|
446
|
+
console.log(`${item.status.toUpperCase()} ${item.conversation_id} (${item.turns} turns)`);
|
|
447
|
+
if (item.error) {
|
|
448
|
+
console.log(` - ${item.error}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
console.log("");
|
|
453
|
+
console.log(`Replay passed: ${result.passed}`);
|
|
454
|
+
console.log(`Replay failed: ${result.failed}`);
|
|
455
|
+
console.log(`Replay skipped: ${result.skipped}`);
|
|
456
|
+
|
|
457
|
+
if (result.failed > 0) {
|
|
458
|
+
process.exitCode = 1;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
290
464
|
if (args.command === "conversations") {
|
|
291
465
|
const [subcommand, conversationId] = args.positional;
|
|
466
|
+
const fromDeploy = args.flags.deploy === true;
|
|
292
467
|
|
|
293
468
|
if (subcommand === "list") {
|
|
469
|
+
if (fromDeploy) {
|
|
470
|
+
const payload = await fetchDeployConversationJson(process.cwd(), "/v1/conversations");
|
|
471
|
+
printJson(payload);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
294
475
|
const conversations = await listConversationsFromCwd(process.cwd());
|
|
295
476
|
|
|
296
477
|
if (conversations.length === 0) {
|
|
@@ -316,7 +497,16 @@ async function main() {
|
|
|
316
497
|
|
|
317
498
|
if (subcommand === "show") {
|
|
318
499
|
if (!conversationId) {
|
|
319
|
-
throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id>");
|
|
500
|
+
throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id> [--deploy]");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (fromDeploy) {
|
|
504
|
+
const payload = await fetchDeployConversationJson(
|
|
505
|
+
process.cwd(),
|
|
506
|
+
`/v1/conversations/${encodeURIComponent(conversationId)}`,
|
|
507
|
+
);
|
|
508
|
+
printJson(payload);
|
|
509
|
+
return;
|
|
320
510
|
}
|
|
321
511
|
|
|
322
512
|
const conversation = await getConversationFromCwd(process.cwd(), conversationId);
|
|
@@ -333,7 +523,107 @@ async function main() {
|
|
|
333
523
|
return;
|
|
334
524
|
}
|
|
335
525
|
|
|
336
|
-
|
|
526
|
+
if (subcommand === "trace") {
|
|
527
|
+
if (!conversationId) {
|
|
528
|
+
throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id> [--deploy]");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (fromDeploy) {
|
|
532
|
+
const trace = await fetchDeployConversationJson(
|
|
533
|
+
process.cwd(),
|
|
534
|
+
`/v1/conversations/${encodeURIComponent(conversationId)}/trace`,
|
|
535
|
+
);
|
|
536
|
+
printJson(trace);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
|
|
541
|
+
console.log(JSON.stringify(trace, null, 2));
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
throw new Error("Usage: agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (args.command === "knowledge") {
|
|
549
|
+
await handleKnowledgeCommand(args);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (args.command === "feedback") {
|
|
554
|
+
await handleFeedbackCommand(args);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (args.command === "sync") {
|
|
559
|
+
const [subcommand] = args.positional;
|
|
560
|
+
|
|
561
|
+
if (subcommand === "init") {
|
|
562
|
+
const result = await initializeSyncScaffold(process.cwd(), {
|
|
563
|
+
force: args.flags.force === true,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
console.log("Sync scaffold ready");
|
|
567
|
+
if (result.files.length === 0) {
|
|
568
|
+
console.log("Files: none written; existing files kept");
|
|
569
|
+
} else {
|
|
570
|
+
console.log(`Files: ${result.files.join(", ")}`);
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (subcommand === "run") {
|
|
576
|
+
const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit sync run --file <path>");
|
|
577
|
+
const result = await runCapsuleSync(process.cwd(), { file });
|
|
578
|
+
|
|
579
|
+
console.log("Sync completed");
|
|
580
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
581
|
+
console.log(`Output: ${JSON.stringify(result.output)}`);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
throw new Error("Usage: agentkit sync init [--force] | agentkit sync run [--file <path>]");
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (args.command === "spec") {
|
|
589
|
+
const [subcommand] = args.positional;
|
|
590
|
+
|
|
591
|
+
if (subcommand === "init") {
|
|
592
|
+
const brief = parseRequiredStringFlag(
|
|
593
|
+
args.flags.brief,
|
|
594
|
+
"brief",
|
|
595
|
+
'agentkit spec init --brief "agent for a real estate brokerage"',
|
|
596
|
+
);
|
|
597
|
+
const out = parseOptionalStringFlag(args.flags.out, "out", "agentkit spec init --out <path>");
|
|
598
|
+
const result = await initializeAgentSpec(process.cwd(), {
|
|
599
|
+
brief,
|
|
600
|
+
out,
|
|
601
|
+
force: args.flags.force === true,
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
console.log("Agent spec created");
|
|
605
|
+
console.log(`File: ${relativePath(result.root, result.file)}`);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (subcommand === "check") {
|
|
610
|
+
const file = parseOptionalStringFlag(args.flags.file, "file", "agentkit spec check --file <path>");
|
|
611
|
+
const result = await checkAgentSpec(process.cwd(), file);
|
|
612
|
+
|
|
613
|
+
if (result.ok) {
|
|
614
|
+
console.log(`Agent spec ok: ${relativePath(result.root, result.file)}`);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
console.log(`Agent spec incomplete: ${relativePath(result.root, result.file)}`);
|
|
619
|
+
for (const heading of result.missingHeadings) {
|
|
620
|
+
console.log(` - missing ## ${heading}`);
|
|
621
|
+
}
|
|
622
|
+
process.exitCode = 1;
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
throw new Error('Usage: agentkit spec init --brief "<owner request>" [--out <path>] [--force] | agentkit spec check [--file <path>]');
|
|
337
627
|
}
|
|
338
628
|
|
|
339
629
|
if (args.command === "inspect") {
|
|
@@ -351,8 +641,27 @@ async function main() {
|
|
|
351
641
|
|
|
352
642
|
if (args.command === "login") {
|
|
353
643
|
const token = parseRequiredStringFlag(args.flags.token, "token", "agentkit login --token <token> [--api <url>]");
|
|
354
|
-
|
|
355
|
-
|
|
644
|
+
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
645
|
+
const verified = await verifyCloudLogin(apiUrl, token);
|
|
646
|
+
await writeCloudAuth({ apiUrl, token });
|
|
647
|
+
const email = verified.me.account?.email;
|
|
648
|
+
console.log(email ? `Logged in to AgentKit Cloud as ${email}.` : "AgentKit Cloud token verified and stored.");
|
|
649
|
+
|
|
650
|
+
const hasHostedDeploy = (verified.capabilities.capabilities ?? []).some(
|
|
651
|
+
(capability) =>
|
|
652
|
+
(capability.name === "cloudflare_deploy_alpha" || capability.name === hostedDeployEntitlement) &&
|
|
653
|
+
capability.status === "active",
|
|
654
|
+
);
|
|
655
|
+
console.log(
|
|
656
|
+
hasHostedDeploy
|
|
657
|
+
? "Entitlement: hosted deploy active."
|
|
658
|
+
: `Warning: cloudflare_deploy_alpha or ${hostedDeployEntitlement} entitlement was not found for this account.`,
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
if (process.env.AGENTKIT_CLOUD_API_TOKEN && process.env.AGENTKIT_CLOUD_API_TOKEN !== token) {
|
|
662
|
+
console.log("Warning: AGENTKIT_CLOUD_API_TOKEN is set and will override this saved login in the current shell.");
|
|
663
|
+
}
|
|
664
|
+
|
|
356
665
|
return;
|
|
357
666
|
}
|
|
358
667
|
|
|
@@ -362,94 +671,317 @@ async function main() {
|
|
|
362
671
|
return;
|
|
363
672
|
}
|
|
364
673
|
|
|
365
|
-
if (args.command === "
|
|
366
|
-
const [
|
|
674
|
+
if (args.command === "account") {
|
|
675
|
+
const [noun, verb, value] = args.positional;
|
|
367
676
|
|
|
368
|
-
if (
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
requireLogin: !args.flags.anonymous,
|
|
372
|
-
});
|
|
373
|
-
printDeployReadinessReport(report);
|
|
677
|
+
if (noun !== "token") {
|
|
678
|
+
throw new Error("Usage: agentkit account token create <name> [--use] [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
679
|
+
}
|
|
374
680
|
|
|
375
|
-
|
|
376
|
-
process.exitCode = 1;
|
|
377
|
-
}
|
|
681
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
378
682
|
|
|
379
|
-
|
|
380
|
-
|
|
683
|
+
if (verb === "create" && value) {
|
|
684
|
+
const payload = (await cloudApiRequest(apiUrl, "/v1/account/tokens", {
|
|
685
|
+
method: "POST",
|
|
686
|
+
body: JSON.stringify({ name: value }),
|
|
687
|
+
})) as AccountApiTokenCreateResponse;
|
|
688
|
+
const token = payload.token;
|
|
381
689
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
690
|
+
if (!token?.token || !token.id) {
|
|
691
|
+
throw new AgentKitError("account_token_invalid", "AgentKit Cloud returned an invalid account token payload.");
|
|
692
|
+
}
|
|
385
693
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
694
|
+
const outPath = parseOptionalStringFlag(
|
|
695
|
+
args.flags.out,
|
|
696
|
+
"out",
|
|
697
|
+
"agentkit account token create <name> [--api <url>] [--use] [--out <path>]",
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
if (outPath) {
|
|
701
|
+
const absolutePath = resolve(process.cwd(), outPath);
|
|
702
|
+
await writeSecretJson(absolutePath, payload);
|
|
703
|
+
console.log(`Account token written to ${absolutePath}`);
|
|
704
|
+
} else {
|
|
390
705
|
printJson(payload);
|
|
391
|
-
return;
|
|
392
706
|
}
|
|
393
707
|
|
|
394
|
-
|
|
708
|
+
if (args.flags.use === true) {
|
|
709
|
+
await writeCloudAuth({ apiUrl, token: token.token });
|
|
710
|
+
console.log(`Saved ${token.id} as the current AgentKit Cloud login.`);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (verb === "list" && !value) {
|
|
717
|
+
const payload = (await cloudApiRequest(apiUrl, "/v1/account/tokens", {
|
|
718
|
+
method: "GET",
|
|
719
|
+
})) as AccountApiTokenListResponse;
|
|
720
|
+
printJson(payload);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (verb === "revoke" && value) {
|
|
725
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/account/tokens/${encodeURIComponent(value)}/revoke`, {
|
|
395
726
|
method: "POST",
|
|
396
727
|
});
|
|
397
728
|
printJson(payload);
|
|
398
729
|
return;
|
|
399
730
|
}
|
|
400
731
|
|
|
401
|
-
|
|
402
|
-
|
|
732
|
+
throw new Error("Usage: agentkit account token create <name> [--use] [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
733
|
+
}
|
|
403
734
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const plan = await prepareVpsDeploy(container, { host, dryRun: Boolean(args.flags["dry-run"]) });
|
|
408
|
-
printBuildResult(container);
|
|
409
|
-
printVpsDeployPlan(plan);
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
735
|
+
if (args.command === "billing") {
|
|
736
|
+
const [subcommand, intentId] = args.positional;
|
|
737
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
412
738
|
|
|
413
|
-
if (
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
739
|
+
if (subcommand === "checkout") {
|
|
740
|
+
const slots = parsePositiveIntegerFlag(
|
|
741
|
+
args.flags.slots,
|
|
742
|
+
"slots",
|
|
743
|
+
"agentkit billing checkout --slots <count> [--email <email>] [--api <url>]",
|
|
744
|
+
);
|
|
745
|
+
const email = parseOptionalStringFlag(
|
|
746
|
+
args.flags.email,
|
|
747
|
+
"email",
|
|
748
|
+
"agentkit billing checkout --slots <count> --email <email>",
|
|
749
|
+
);
|
|
750
|
+
const payload = (await cloudApiRequest(apiUrl, "/v1/billing/checkout", {
|
|
751
|
+
method: "POST",
|
|
752
|
+
body: JSON.stringify({
|
|
753
|
+
slots,
|
|
754
|
+
...(email ? { email } : {}),
|
|
755
|
+
}),
|
|
756
|
+
})) as BillingCheckoutResponse;
|
|
757
|
+
const checkoutUrl = payload.checkout?.url;
|
|
758
|
+
const billingIntentId = payload.billing_intent?.id;
|
|
759
|
+
const secret = payload.billing_intent?.secret;
|
|
760
|
+
|
|
761
|
+
if (!checkoutUrl || !billingIntentId || !secret) {
|
|
762
|
+
throw new AgentKitError("billing_checkout_invalid", "AgentKit Cloud returned an invalid billing checkout payload.");
|
|
763
|
+
}
|
|
418
764
|
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
);
|
|
765
|
+
if (args.flags.json === true) {
|
|
766
|
+
printJson(payload);
|
|
767
|
+
} else {
|
|
768
|
+
console.log("AgentKit Cloud checkout");
|
|
769
|
+
console.log("");
|
|
770
|
+
console.log(`URL: ${checkoutUrl}`);
|
|
771
|
+
console.log(`Intent: ${billingIntentId}`);
|
|
772
|
+
console.log(`Secret: ${secret}`);
|
|
773
|
+
console.log("");
|
|
774
|
+
console.log(`After payment: agentkit billing claim ${billingIntentId} --secret ${secret} --api ${apiUrl}`);
|
|
425
775
|
}
|
|
426
|
-
}
|
|
427
776
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
printBuildResult(result);
|
|
777
|
+
if (args.flags.open === true) {
|
|
778
|
+
await openLocalUrl(checkoutUrl);
|
|
779
|
+
}
|
|
433
780
|
|
|
434
|
-
if (args.flags["dry-run"]) {
|
|
435
|
-
console.log("");
|
|
436
|
-
console.log("Dry run only. Artifact is ready for AgentKit Cloud.");
|
|
437
781
|
return;
|
|
438
782
|
}
|
|
439
783
|
|
|
440
|
-
if (
|
|
441
|
-
|
|
784
|
+
if (subcommand === "status" && intentId) {
|
|
785
|
+
const secret = parseRequiredStringFlag(
|
|
786
|
+
args.flags.secret,
|
|
787
|
+
"secret",
|
|
788
|
+
"agentkit billing status <billing-intent-id> --secret <secret> [--api <url>]",
|
|
789
|
+
);
|
|
790
|
+
const payload = (await cloudApiRequest(
|
|
791
|
+
apiUrl,
|
|
792
|
+
`/v1/billing/checkout-intents/${encodeURIComponent(intentId)}?secret=${encodeURIComponent(secret)}`,
|
|
793
|
+
{ method: "GET" },
|
|
794
|
+
)) as BillingCheckoutIntentResponse;
|
|
795
|
+
printJson(payload);
|
|
442
796
|
return;
|
|
443
797
|
}
|
|
444
798
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
799
|
+
if (subcommand === "claim" && intentId) {
|
|
800
|
+
const secret = parseRequiredStringFlag(
|
|
801
|
+
args.flags.secret,
|
|
802
|
+
"secret",
|
|
803
|
+
"agentkit billing claim <billing-intent-id> --secret <secret> [--token-name <name>] [--api <url>]",
|
|
804
|
+
);
|
|
805
|
+
const tokenName = parseOptionalStringFlag(
|
|
806
|
+
args.flags["token-name"],
|
|
807
|
+
"token-name",
|
|
808
|
+
"agentkit billing claim <billing-intent-id> --secret <secret> --token-name <name>",
|
|
809
|
+
);
|
|
810
|
+
const payload = (await cloudApiRequest(apiUrl, `/v1/billing/checkout-intents/${encodeURIComponent(intentId)}/claim`, {
|
|
811
|
+
method: "POST",
|
|
812
|
+
body: JSON.stringify({
|
|
813
|
+
secret,
|
|
814
|
+
...(tokenName ? { token_name: tokenName } : {}),
|
|
815
|
+
}),
|
|
816
|
+
})) as BillingClaimResponse;
|
|
817
|
+
const token = payload.token;
|
|
818
|
+
|
|
819
|
+
if (!token?.token || !token.id) {
|
|
820
|
+
throw new AgentKitError("billing_claim_invalid", "AgentKit Cloud returned an invalid billing claim payload.");
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
await writeCloudAuth({ apiUrl, token: token.token });
|
|
824
|
+
|
|
825
|
+
const outPath = parseOptionalStringFlag(
|
|
826
|
+
args.flags.out,
|
|
827
|
+
"out",
|
|
828
|
+
"agentkit billing claim <billing-intent-id> --secret <secret> [--out <path>]",
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
if (outPath) {
|
|
832
|
+
const absolutePath = resolve(process.cwd(), outPath);
|
|
833
|
+
await writeSecretJson(absolutePath, payload);
|
|
834
|
+
console.log(`Account token written to ${absolutePath}`);
|
|
835
|
+
} else if (args.flags.json === true) {
|
|
836
|
+
printJson(payload);
|
|
837
|
+
} else {
|
|
838
|
+
console.log("AgentKit Cloud account token claimed and saved.");
|
|
839
|
+
console.log(`Token: ${token.id}`);
|
|
840
|
+
console.log(`API: ${apiUrl}`);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (subcommand === "portal") {
|
|
847
|
+
const payload = (await cloudApiRequest(apiUrl, "/v1/billing/portal", {
|
|
848
|
+
method: "POST",
|
|
849
|
+
body: JSON.stringify({}),
|
|
850
|
+
})) as BillingPortalResponse;
|
|
851
|
+
const portalUrl = payload.portal?.url;
|
|
852
|
+
|
|
853
|
+
if (!portalUrl) {
|
|
854
|
+
throw new AgentKitError("billing_portal_invalid", "AgentKit Cloud returned an invalid billing portal payload.");
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (args.flags.json === true) {
|
|
858
|
+
printJson(payload);
|
|
859
|
+
} else {
|
|
860
|
+
console.log("AgentKit Cloud billing portal");
|
|
861
|
+
console.log(`URL: ${portalUrl}`);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (args.flags.open === true) {
|
|
865
|
+
await openLocalUrl(portalUrl);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
throw new Error(
|
|
872
|
+
"Usage: agentkit billing checkout --slots <count> [--email <email>] | status <intent-id> --secret <secret> | claim <intent-id> --secret <secret> | portal [--api <url>]",
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (args.command === "deploy") {
|
|
877
|
+
const [deploySubcommand] = args.positional;
|
|
878
|
+
|
|
879
|
+
if (deploySubcommand === "smoke") {
|
|
880
|
+
const message =
|
|
881
|
+
parseOptionalStringFlag(args.flags.message ?? args.flags.smoke, "message", 'agentkit deploy smoke --message "hello"') ??
|
|
882
|
+
"hello";
|
|
883
|
+
const state = await requireLocalDeployState();
|
|
884
|
+
const token = await readDeployAccessTokenForHostedRequest(process.cwd());
|
|
885
|
+
await runDeploySmoke({
|
|
886
|
+
deployUrl: requireDeployUrl(state),
|
|
887
|
+
message,
|
|
888
|
+
token,
|
|
889
|
+
});
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (deploySubcommand === "doctor") {
|
|
894
|
+
const report = await checkDeployReadiness({
|
|
895
|
+
apiUrl: await resolveCloudApiUrl(args.flags.api),
|
|
896
|
+
requireLogin: !args.flags.anonymous,
|
|
897
|
+
});
|
|
898
|
+
printDeployReadinessReport(report);
|
|
899
|
+
|
|
900
|
+
if (!report.ok) {
|
|
901
|
+
process.exitCode = 1;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (deploySubcommand === "status" || deploySubcommand === "pause" || deploySubcommand === "resume") {
|
|
908
|
+
const state = await requireLocalDeployState();
|
|
909
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
910
|
+
|
|
911
|
+
if (deploySubcommand === "status") {
|
|
912
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}`, {
|
|
913
|
+
method: "GET",
|
|
914
|
+
});
|
|
915
|
+
printJson(payload);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const payload = await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(state.deploy_id)}/${deploySubcommand}`, {
|
|
920
|
+
method: "POST",
|
|
921
|
+
});
|
|
922
|
+
printJson(payload);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const target = resolveDeployTarget(parseTargetFlag(args.flags.target, "deploy"));
|
|
927
|
+
const anonymous = Boolean(args.flags.anonymous);
|
|
928
|
+
const smokeMessage = parseOptionalStringFlag(args.flags.smoke, "smoke", 'agentkit deploy --smoke "hello"');
|
|
929
|
+
|
|
930
|
+
if (target.id === "vps") {
|
|
931
|
+
const host = parseRequiredStringFlag(args.flags.host, "host", "agentkit deploy --target vps --host <host>");
|
|
932
|
+
const container = await buildAgentCapsule(process.cwd(), { target: "container" });
|
|
933
|
+
const plan = await prepareVpsDeploy(container, { host, dryRun: Boolean(args.flags["dry-run"]) });
|
|
934
|
+
printBuildResult(container);
|
|
935
|
+
printVpsDeployPlan(plan);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (!args.flags["dry-run"] && !args.flags["local-wrangler"]) {
|
|
940
|
+
const report = await checkDeployReadiness({
|
|
941
|
+
apiUrl: await resolveCloudApiUrl(args.flags.api),
|
|
942
|
+
requireLogin: !anonymous,
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
if (!report.ok) {
|
|
946
|
+
printDeployReadinessReport(report);
|
|
947
|
+
throw new AgentKitError(
|
|
948
|
+
"deploy_readiness_failed",
|
|
949
|
+
"Fix deploy readiness failures before running agentkit deploy.",
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const result = await buildAgentCapsule(process.cwd(), { target: target.id });
|
|
955
|
+
if (result.target !== "cloudflare") {
|
|
956
|
+
throw new Error(`Unsupported AgentKit Cloud deploy target: ${result.target}`);
|
|
957
|
+
}
|
|
958
|
+
printBuildResult(result);
|
|
959
|
+
|
|
960
|
+
if (args.flags["dry-run"]) {
|
|
961
|
+
console.log("");
|
|
962
|
+
console.log("Dry run only. Artifact is ready for AgentKit Cloud.");
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (args.flags["local-wrangler"]) {
|
|
967
|
+
await deployCloudflare(result.wranglerPath, result.buildDir);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
972
|
+
const auth = anonymous ? null : await readCloudAuthForApiUrl(apiUrl);
|
|
973
|
+
const deploy = await deployBuildToAgentKitCloud(result, {
|
|
974
|
+
apiUrl,
|
|
975
|
+
apiToken: anonymous ? null : auth?.token,
|
|
976
|
+
anonymous: anonymous || !auth?.token,
|
|
977
|
+
});
|
|
452
978
|
const statePath = await writeLocalDeployState(result.root, deploy);
|
|
979
|
+
const chatAccess = await ensureDeployChatAccessToken({
|
|
980
|
+
apiUrl,
|
|
981
|
+
root: result.root,
|
|
982
|
+
deployId: deploy.deploy.id,
|
|
983
|
+
skip: anonymous || !auth?.token,
|
|
984
|
+
});
|
|
453
985
|
|
|
454
986
|
console.log("");
|
|
455
987
|
console.log("Deploy ready");
|
|
@@ -457,13 +989,40 @@ async function main() {
|
|
|
457
989
|
console.log(`Project: ${deploy.project.id}`);
|
|
458
990
|
console.log(`Deploy: ${deploy.deploy.id}`);
|
|
459
991
|
console.log(`State: ${statePath}`);
|
|
992
|
+
|
|
993
|
+
if (chatAccess?.status === "ready") {
|
|
994
|
+
console.log(`Chat auth: ${relativePath(process.cwd(), chatAccess.path)}`);
|
|
995
|
+
} else if (chatAccess?.status === "skipped") {
|
|
996
|
+
console.log("Chat auth: anonymous deploy; no local chat token was created.");
|
|
997
|
+
} else if (chatAccess?.status === "failed") {
|
|
998
|
+
console.log(`Chat auth: could not create local chat token. ${formatReadinessError(chatAccess.error)}`);
|
|
999
|
+
console.log(`Run: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if (smokeMessage) {
|
|
1003
|
+
if (chatAccess?.status === "failed") {
|
|
1004
|
+
throw new AgentKitError("deploy_smoke_failed", "Could not run deploy smoke because chat access token creation failed.");
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
await runDeploySmoke({
|
|
1008
|
+
deployUrl: requireDeployUrl({
|
|
1009
|
+
deploy_id: deploy.deploy.id,
|
|
1010
|
+
url: deploy.deploy.url,
|
|
1011
|
+
}),
|
|
1012
|
+
message: smokeMessage,
|
|
1013
|
+
token:
|
|
1014
|
+
chatAccess?.status === "ready"
|
|
1015
|
+
? await readDeployAccessTokenForUi(chatAccess.path)
|
|
1016
|
+
: undefined,
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
460
1019
|
return;
|
|
461
1020
|
}
|
|
462
1021
|
|
|
463
1022
|
if (args.command === "secret") {
|
|
464
1023
|
const [subcommand, name, ...valueParts] = args.positional;
|
|
465
|
-
const projectId = await resolveProjectIdForSecretCommand(process.cwd());
|
|
466
1024
|
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
1025
|
+
const projectId = await resolveProjectIdForSecretCommand(process.cwd(), apiUrl);
|
|
467
1026
|
|
|
468
1027
|
if (subcommand === "list") {
|
|
469
1028
|
const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
|
|
@@ -474,10 +1033,19 @@ async function main() {
|
|
|
474
1033
|
}
|
|
475
1034
|
|
|
476
1035
|
if (subcommand === "set") {
|
|
477
|
-
const value =
|
|
1036
|
+
const value = await resolveSecretInputValue({
|
|
1037
|
+
name,
|
|
1038
|
+
valueParts,
|
|
1039
|
+
stdinFlag: args.flags.stdin,
|
|
1040
|
+
fromEnvFlag: args.flags["from-env"],
|
|
1041
|
+
fromLocalEnvFlag: args.flags["from-local-env"],
|
|
1042
|
+
localEnvRoot: args.flags["from-local-env"] !== undefined ? await findAgentCapsuleRoot(process.cwd()) : undefined,
|
|
1043
|
+
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>]",
|
|
1044
|
+
inlineSensitiveHelp: "Prefer --stdin, --from-env, or --from-local-env so the value does not enter shell history.",
|
|
1045
|
+
});
|
|
478
1046
|
|
|
479
|
-
if (!name
|
|
480
|
-
throw new Error("Usage: agentkit secret set <NAME> <VALUE> [--api <url>]");
|
|
1047
|
+
if (!name) {
|
|
1048
|
+
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>]");
|
|
481
1049
|
}
|
|
482
1050
|
|
|
483
1051
|
const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(name)}`, {
|
|
@@ -488,6 +1056,15 @@ async function main() {
|
|
|
488
1056
|
return;
|
|
489
1057
|
}
|
|
490
1058
|
|
|
1059
|
+
if (subcommand === "sync") {
|
|
1060
|
+
if (args.flags["from-local"] !== true) {
|
|
1061
|
+
throw new Error("Usage: agentkit secret sync --from-local [--api <url>]");
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
await syncHostedSecretsFromLocal({ apiUrl, projectId });
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
491
1068
|
if (subcommand === "unset") {
|
|
492
1069
|
if (!name) {
|
|
493
1070
|
throw new Error("Usage: agentkit secret unset <NAME> [--api <url>]");
|
|
@@ -500,14 +1077,14 @@ async function main() {
|
|
|
500
1077
|
return;
|
|
501
1078
|
}
|
|
502
1079
|
|
|
503
|
-
throw new Error("Usage: agentkit secret set <NAME> <VALUE> | agentkit secret list | agentkit secret unset <NAME>");
|
|
1080
|
+
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>");
|
|
504
1081
|
}
|
|
505
1082
|
|
|
506
1083
|
if (args.command === "access") {
|
|
507
1084
|
const [subcommand, noun, name] = args.positional;
|
|
508
1085
|
|
|
509
1086
|
if (subcommand !== "token") {
|
|
510
|
-
throw new Error("Usage: agentkit access token create <name> | list | revoke <token-id> [--api <url>]");
|
|
1087
|
+
throw new Error("Usage: agentkit access token create <name> [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
511
1088
|
}
|
|
512
1089
|
|
|
513
1090
|
const state = await requireLocalDeployState();
|
|
@@ -518,7 +1095,7 @@ async function main() {
|
|
|
518
1095
|
method: "POST",
|
|
519
1096
|
body: JSON.stringify({ name }),
|
|
520
1097
|
});
|
|
521
|
-
|
|
1098
|
+
await printJsonOrWrite(payload, args.flags.out, "agentkit access token create <name> [--api <url>] [--out <path>]");
|
|
522
1099
|
return;
|
|
523
1100
|
}
|
|
524
1101
|
|
|
@@ -540,7 +1117,7 @@ async function main() {
|
|
|
540
1117
|
return;
|
|
541
1118
|
}
|
|
542
1119
|
|
|
543
|
-
throw new Error("Usage: agentkit access token create <name> | list | revoke <token-id> [--api <url>]");
|
|
1120
|
+
throw new Error("Usage: agentkit access token create <name> [--out <path>] | list | revoke <token-id> [--api <url>]");
|
|
544
1121
|
}
|
|
545
1122
|
|
|
546
1123
|
if (args.command === "channels") {
|
|
@@ -548,6 +1125,121 @@ async function main() {
|
|
|
548
1125
|
return;
|
|
549
1126
|
}
|
|
550
1127
|
|
|
1128
|
+
if (args.command === "transcribe") {
|
|
1129
|
+
await handleTranscribeCommand(args);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (args.command === "skills") {
|
|
1134
|
+
const [subcommand] = args.positional;
|
|
1135
|
+
const root = await findAgentCapsuleRoot(process.cwd());
|
|
1136
|
+
|
|
1137
|
+
if (subcommand === "status") {
|
|
1138
|
+
const status = await checkDefaultSkillStatus(root);
|
|
1139
|
+
printSkillStatus(status);
|
|
1140
|
+
if (status.status !== "current") {
|
|
1141
|
+
process.exitCode = 1;
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (subcommand === "sync") {
|
|
1147
|
+
const result = await syncDefaultSkillFiles(root);
|
|
1148
|
+
console.log(`Synced ${result.files.length} AgentKit skill files.`);
|
|
1149
|
+
console.log("Status: current");
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
throw new Error("Usage: agentkit skills status | agentkit skills sync");
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if (args.command === "integrations") {
|
|
1157
|
+
const [subcommand, provider] = args.positional;
|
|
1158
|
+
|
|
1159
|
+
if (provider !== undefined && provider !== "composio") {
|
|
1160
|
+
throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const state = await requireLocalDeployState();
|
|
1164
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
1165
|
+
|
|
1166
|
+
if (subcommand === "status") {
|
|
1167
|
+
const toolkit = parseOptionalStringFlag(
|
|
1168
|
+
args.flags.toolkit,
|
|
1169
|
+
"toolkit",
|
|
1170
|
+
"agentkit integrations status --toolkit <slug>",
|
|
1171
|
+
);
|
|
1172
|
+
const payload = await cloudApiRequest(
|
|
1173
|
+
apiUrl,
|
|
1174
|
+
`/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status${
|
|
1175
|
+
toolkit ? `?toolkit=${encodeURIComponent(toolkit)}` : ""
|
|
1176
|
+
}`,
|
|
1177
|
+
{ method: "GET" },
|
|
1178
|
+
);
|
|
1179
|
+
printJson(payload);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if (subcommand === "connect" && provider === "composio") {
|
|
1184
|
+
const toolkit = parseOptionalStringFlag(
|
|
1185
|
+
args.flags.toolkit,
|
|
1186
|
+
"toolkit",
|
|
1187
|
+
"agentkit integrations connect composio --toolkit <slug>",
|
|
1188
|
+
);
|
|
1189
|
+
const callbackUrl = parseOptionalStringFlag(
|
|
1190
|
+
args.flags["callback-url"],
|
|
1191
|
+
"callback-url",
|
|
1192
|
+
"agentkit integrations connect composio --callback-url <url>",
|
|
1193
|
+
);
|
|
1194
|
+
const payload = (await cloudApiRequest(
|
|
1195
|
+
apiUrl,
|
|
1196
|
+
`/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/connect-link`,
|
|
1197
|
+
{
|
|
1198
|
+
method: "POST",
|
|
1199
|
+
body: JSON.stringify({
|
|
1200
|
+
...(toolkit ? { toolkit } : {}),
|
|
1201
|
+
...(callbackUrl ? { callback_url: callbackUrl } : {}),
|
|
1202
|
+
}),
|
|
1203
|
+
},
|
|
1204
|
+
)) as {
|
|
1205
|
+
connect_link?: {
|
|
1206
|
+
toolkit?: string;
|
|
1207
|
+
user_id?: string;
|
|
1208
|
+
redirect_url?: string;
|
|
1209
|
+
expires_at?: string;
|
|
1210
|
+
connected_account_id?: string;
|
|
1211
|
+
auth_config_source?: string;
|
|
1212
|
+
};
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
const link = payload.connect_link;
|
|
1216
|
+
|
|
1217
|
+
if (!link?.redirect_url) {
|
|
1218
|
+
throw new AgentKitError("integration_response_invalid", "AgentKit Cloud returned an invalid Composio connect-link payload.");
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
console.log("Composio connect link");
|
|
1222
|
+
console.log(`Toolkit: ${link.toolkit ?? toolkit ?? "unknown"}`);
|
|
1223
|
+
console.log(`User: ${link.user_id ?? "unknown"}`);
|
|
1224
|
+
if (link.auth_config_source) {
|
|
1225
|
+
console.log(`Auth: ${link.auth_config_source}`);
|
|
1226
|
+
}
|
|
1227
|
+
console.log(`URL: ${link.redirect_url}`);
|
|
1228
|
+
|
|
1229
|
+
if (link.expires_at) {
|
|
1230
|
+
console.log(`Expires: ${link.expires_at}`);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (link.connected_account_id) {
|
|
1234
|
+
console.log(`Account: ${link.connected_account_id}`);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
|
|
1241
|
+
}
|
|
1242
|
+
|
|
551
1243
|
if (args.command === "env") {
|
|
552
1244
|
const [subcommand, name, ...valueParts] = args.positional;
|
|
553
1245
|
const root = await findAgentCapsuleRoot(process.cwd());
|
|
@@ -568,10 +1260,18 @@ async function main() {
|
|
|
568
1260
|
}
|
|
569
1261
|
|
|
570
1262
|
if (subcommand === "set") {
|
|
571
|
-
const value =
|
|
1263
|
+
const value = await resolveSecretInputValue({
|
|
1264
|
+
name,
|
|
1265
|
+
valueParts,
|
|
1266
|
+
stdinFlag: args.flags.stdin,
|
|
1267
|
+
fromEnvFlag: args.flags["from-env"],
|
|
1268
|
+
fromLocalEnvFlag: undefined,
|
|
1269
|
+
usage: "agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME]",
|
|
1270
|
+
inlineSensitiveHelp: "Prefer --stdin or --from-env so the value does not enter shell history.",
|
|
1271
|
+
});
|
|
572
1272
|
|
|
573
|
-
if (!name
|
|
574
|
-
throw new Error("Usage: agentkit env set <NAME> <VALUE>");
|
|
1273
|
+
if (!name) {
|
|
1274
|
+
throw new Error("Usage: agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME]");
|
|
575
1275
|
}
|
|
576
1276
|
|
|
577
1277
|
await setCapsuleEnv(root, name, value);
|
|
@@ -589,7 +1289,7 @@ async function main() {
|
|
|
589
1289
|
return;
|
|
590
1290
|
}
|
|
591
1291
|
|
|
592
|
-
throw new Error("Usage: agentkit env list | agentkit env set <NAME> <VALUE> | agentkit env unset <NAME>");
|
|
1292
|
+
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>");
|
|
593
1293
|
}
|
|
594
1294
|
|
|
595
1295
|
if (args.command === "docs") {
|
|
@@ -623,15 +1323,17 @@ async function main() {
|
|
|
623
1323
|
|
|
624
1324
|
const root = await findAgentCapsuleRoot(process.cwd());
|
|
625
1325
|
const docs = findAgentKitDocs();
|
|
626
|
-
console.log(renderHandoffPrompt({ target, root,
|
|
1326
|
+
console.log(renderHandoffPrompt({ target, root, docsLlms: docs.llms, goal: goalParts.join(" ").trim() }));
|
|
627
1327
|
return;
|
|
628
1328
|
}
|
|
629
1329
|
|
|
630
1330
|
throw new Error(`Unknown command: ${args.command}`);
|
|
631
1331
|
}
|
|
632
1332
|
|
|
633
|
-
function renderHandoffPrompt(input: { target: string; root: string;
|
|
1333
|
+
function renderHandoffPrompt(input: { target: string; root: string; docsLlms: string; goal: string }): string {
|
|
634
1334
|
const agentName = input.target === "claude" ? "Claude Code" : "Codex";
|
|
1335
|
+
const skillRouter = join(input.root, "skills/agentkit-capsule/SKILL.md");
|
|
1336
|
+
const skillLine = existsSync(skillRouter) ? `- ${skillRouter}\n` : "";
|
|
635
1337
|
const goal = input.goal
|
|
636
1338
|
? `Owner request:
|
|
637
1339
|
${input.goal}`
|
|
@@ -645,17 +1347,22 @@ ${input.root}
|
|
|
645
1347
|
Read these files first:
|
|
646
1348
|
|
|
647
1349
|
- ${join(input.root, "AGENTKIT.md")}
|
|
648
|
-
- ${input.
|
|
1350
|
+
${skillLine}- ${input.docsLlms}
|
|
649
1351
|
|
|
650
1352
|
${goal}
|
|
651
1353
|
|
|
652
1354
|
Rules:
|
|
653
|
-
-
|
|
1355
|
+
- Start with the AgentKit capsule skill when it exists.
|
|
1356
|
+
- Use llms.txt as the docs router.
|
|
1357
|
+
- Read llms-full.txt only when a skill or ambiguous framework behavior requires the complete contract.
|
|
654
1358
|
- Treat the owner's request as the brief.
|
|
655
1359
|
- Build or update the agent inside this capsule as far as you can without asking for obvious restatements.
|
|
1360
|
+
- Do not wait for a wizard or recipe; implement directly in the capsule files.
|
|
656
1361
|
- Edit agent behavior in prompts/instructions.md.
|
|
657
1362
|
- Edit the agent contract in agentkit.config.ts.
|
|
658
1363
|
- Add TypeScript tools under tools/.
|
|
1364
|
+
- Keep the first useful version runnable with test/fake unless the owner has already chosen a real provider.
|
|
1365
|
+
- Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
|
|
659
1366
|
- Keep local secrets in .env only.
|
|
660
1367
|
- Do not commit or print secret values.
|
|
661
1368
|
- Do not edit .agentkit/ directly.
|
|
@@ -666,7 +1373,6 @@ Verification:
|
|
|
666
1373
|
Run these before you finish:
|
|
667
1374
|
|
|
668
1375
|
\`\`\`sh
|
|
669
|
-
npm install
|
|
670
1376
|
npm run typecheck
|
|
671
1377
|
npm run agentkit -- inspect
|
|
672
1378
|
npm run chat -- --message "hello"
|
|
@@ -678,883 +1384,282 @@ If you add a tool, also run a tool smoke test with the fake provider, for exampl
|
|
|
678
1384
|
npm run agentkit -- tool tool_name --input '{}'
|
|
679
1385
|
\`\`\`
|
|
680
1386
|
|
|
1387
|
+
UI testing:
|
|
1388
|
+
- For local UI testing, run \`npm run dev\`, open the printed \`Chat:\` URL, and tell the owner the exact URL.
|
|
1389
|
+
- 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.
|
|
1390
|
+
- If the provider is still \`test/fake\`, say the UI was tested only with the deterministic fake provider.
|
|
1391
|
+
|
|
681
1392
|
Final response:
|
|
682
1393
|
- Summarize what changed.
|
|
683
1394
|
- List files changed.
|
|
684
1395
|
- Paste the verification commands and results.
|
|
1396
|
+
- State whether testing used \`test/fake\` or a real provider selected by the owner.
|
|
685
1397
|
- Call out any missing user details or blocked checks.
|
|
686
1398
|
`;
|
|
687
1399
|
}
|
|
688
1400
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
status: DeployReadinessStatus;
|
|
693
|
-
title: string;
|
|
694
|
-
message: string;
|
|
695
|
-
action?: string;
|
|
696
|
-
};
|
|
1401
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
1402
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1403
|
+
}
|
|
697
1404
|
|
|
698
|
-
|
|
699
|
-
ok: boolean;
|
|
700
|
-
apiUrl: string;
|
|
701
|
-
context: DeployReadinessContext;
|
|
702
|
-
checks: DeployReadinessCheck[];
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
type CloudMeResponse = {
|
|
706
|
-
account?: {
|
|
707
|
-
email?: string;
|
|
708
|
-
status?: string;
|
|
709
|
-
};
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
type CloudCapabilitiesResponse = {
|
|
713
|
-
capabilities?: Array<{
|
|
714
|
-
name?: string;
|
|
715
|
-
status?: string;
|
|
716
|
-
}>;
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
type CloudSecretsResponse = {
|
|
720
|
-
secrets?: Array<{
|
|
721
|
-
name?: string;
|
|
722
|
-
status?: string;
|
|
723
|
-
updated_at?: string;
|
|
724
|
-
}>;
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
async function checkDeployReadiness(options: {
|
|
1405
|
+
async function ensureDeployChatAccessToken(options: {
|
|
728
1406
|
apiUrl: string;
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
title: "Target runtime",
|
|
740
|
-
message: 'Cloudflare deploy requires runtime: "edge" in agentkit.config.ts.',
|
|
741
|
-
action: "set runtime: \"edge\" before deploying to AgentKit Cloud",
|
|
742
|
-
});
|
|
743
|
-
} else {
|
|
744
|
-
checks.push({
|
|
745
|
-
status: "pass",
|
|
746
|
-
title: "Target runtime",
|
|
747
|
-
message: "Capsule is configured for edge deploy.",
|
|
748
|
-
});
|
|
1407
|
+
root: string;
|
|
1408
|
+
deployId: string;
|
|
1409
|
+
skip: boolean;
|
|
1410
|
+
}): Promise<
|
|
1411
|
+
| { status: "ready"; path: string; tokenId: string; created: boolean }
|
|
1412
|
+
| { status: "skipped" }
|
|
1413
|
+
| { status: "failed"; error: unknown }
|
|
1414
|
+
> {
|
|
1415
|
+
if (options.skip) {
|
|
1416
|
+
return { status: "skipped" };
|
|
749
1417
|
}
|
|
750
1418
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
title: "Login",
|
|
755
|
-
message: options.requireLogin
|
|
756
|
-
? "No AgentKit Cloud token was found for hosted production deploy."
|
|
757
|
-
: "No AgentKit Cloud token was found; deploy will request anonymous mode.",
|
|
758
|
-
action: "agentkit login --token <token>",
|
|
759
|
-
});
|
|
760
|
-
} else {
|
|
761
|
-
try {
|
|
762
|
-
const me = (await cloudApiRequest(options.apiUrl, "/v1/me", { method: "GET" })) as CloudMeResponse;
|
|
763
|
-
const email = me.account?.email;
|
|
764
|
-
checks.push({
|
|
765
|
-
status: "pass",
|
|
766
|
-
title: "Login",
|
|
767
|
-
message: email ? `Authenticated as ${email}.` : "AgentKit Cloud token is accepted.",
|
|
768
|
-
});
|
|
769
|
-
cloudAuthUsable = true;
|
|
770
|
-
} catch (error) {
|
|
771
|
-
checks.push({
|
|
772
|
-
status: "fail",
|
|
773
|
-
title: "Login",
|
|
774
|
-
message: `AgentKit Cloud rejected the stored token. ${formatReadinessError(error)}`,
|
|
775
|
-
action: "agentkit login --token <token>",
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
}
|
|
1419
|
+
try {
|
|
1420
|
+
const path = join(options.root, defaultDeployChatAccessTokenPath);
|
|
1421
|
+
const existing = await readDeployAccessTokenFile(path);
|
|
779
1422
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
(
|
|
1423
|
+
if (existing?.deploy_id === options.deployId && existing.id) {
|
|
1424
|
+
const listed = (await cloudApiRequest(
|
|
1425
|
+
options.apiUrl,
|
|
1426
|
+
`/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
|
|
1427
|
+
{ method: "GET" },
|
|
1428
|
+
)) as DeployAccessTokenListResponse;
|
|
1429
|
+
const stillUsable = (listed.access_tokens ?? []).some(
|
|
1430
|
+
(token) => token.id === existing.id && deployAccessTokenMetadataIsUsable(token),
|
|
788
1431
|
);
|
|
789
1432
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
? "cloudflare_deploy_alpha is active."
|
|
795
|
-
: "This account is missing the cloudflare_deploy_alpha entitlement.",
|
|
796
|
-
...(hasCloudflareAlpha ? {} : { action: "log in with an invited AgentKit Cloud alpha token" }),
|
|
797
|
-
});
|
|
798
|
-
} catch (error) {
|
|
799
|
-
checks.push({
|
|
800
|
-
status: "fail",
|
|
801
|
-
title: "Deploy entitlement",
|
|
802
|
-
message: `Could not verify AgentKit Cloud capabilities. ${formatReadinessError(error)}`,
|
|
803
|
-
});
|
|
1433
|
+
if (stillUsable) {
|
|
1434
|
+
await chmod(path, 0o600);
|
|
1435
|
+
return { status: "ready", path, tokenId: existing.id, created: false };
|
|
1436
|
+
}
|
|
804
1437
|
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
if (context.agentKitManagedSecrets.length > 0) {
|
|
808
|
-
checks.push({
|
|
809
|
-
status: "info",
|
|
810
|
-
title: "AgentKit-managed secrets",
|
|
811
|
-
message: `${context.agentKitManagedSecrets.join(", ")} ${
|
|
812
|
-
context.agentKitManagedSecrets.length === 1 ? "is" : "are"
|
|
813
|
-
} provisioned by AgentKit Cloud during deploy.`,
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
1438
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1439
|
+
const created = (await cloudApiRequest(
|
|
1440
|
+
options.apiUrl,
|
|
1441
|
+
`/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
|
|
1442
|
+
{
|
|
1443
|
+
method: "POST",
|
|
1444
|
+
body: JSON.stringify({ name: defaultDeployChatAccessTokenName }),
|
|
1445
|
+
},
|
|
1446
|
+
)) as DeployAccessTokenCreateResponse;
|
|
1447
|
+
const token = created.access_token;
|
|
824
1448
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
status: "pass",
|
|
828
|
-
title: "Hosted secrets",
|
|
829
|
-
message: "No user-managed hosted secrets are required by this capsule.",
|
|
830
|
-
});
|
|
831
|
-
} else if (!cloudAuthUsable) {
|
|
832
|
-
checks.push({
|
|
833
|
-
status: options.requireLogin ? "fail" : "warn",
|
|
834
|
-
title: "Hosted secrets",
|
|
835
|
-
message: `Could not check hosted secrets without a valid AgentKit Cloud login. Required: ${context.userManagedSecrets.join(", ")}.`,
|
|
836
|
-
action: "agentkit secret list",
|
|
837
|
-
});
|
|
838
|
-
} else {
|
|
839
|
-
try {
|
|
840
|
-
const payload = (await cloudApiRequest(
|
|
841
|
-
options.apiUrl,
|
|
842
|
-
`/v1/projects/${encodeURIComponent(context.projectId)}/secrets`,
|
|
843
|
-
{ method: "GET" },
|
|
844
|
-
)) as CloudSecretsResponse;
|
|
845
|
-
const hostedSecretNames = new Set((payload.secrets ?? []).map((secret) => secret.name).filter(isString));
|
|
846
|
-
const missing = context.userManagedSecrets.filter((name) => !hostedSecretNames.has(name));
|
|
847
|
-
|
|
848
|
-
if (missing.length === 0) {
|
|
849
|
-
checks.push({
|
|
850
|
-
status: "pass",
|
|
851
|
-
title: "Hosted secrets",
|
|
852
|
-
message: "All user-managed hosted secrets are set.",
|
|
853
|
-
});
|
|
854
|
-
} else {
|
|
855
|
-
for (const secret of missing) {
|
|
856
|
-
const localSource = context.localSecrets[secret];
|
|
857
|
-
checks.push({
|
|
858
|
-
status: "fail",
|
|
859
|
-
title: `Hosted secret ${secret}`,
|
|
860
|
-
message: localSource
|
|
861
|
-
? `${secret} is present locally but has not been set in AgentKit Cloud.`
|
|
862
|
-
: `${secret} is required but missing from AgentKit Cloud.`,
|
|
863
|
-
action: secretSetCommand(secret, localSource),
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
} catch (error) {
|
|
868
|
-
checks.push({
|
|
869
|
-
status: "fail",
|
|
870
|
-
title: "Hosted secrets",
|
|
871
|
-
message: `Could not list hosted secrets for project ${context.projectId}. ${formatReadinessError(error)}`,
|
|
872
|
-
action: "agentkit secret list",
|
|
873
|
-
});
|
|
1449
|
+
if (!token?.id || token.deploy_id !== options.deployId || !token.token) {
|
|
1450
|
+
throw new AgentKitError("access_token_invalid", "AgentKit Cloud returned an invalid deploy access token payload.");
|
|
874
1451
|
}
|
|
875
|
-
}
|
|
876
1452
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
};
|
|
1453
|
+
await writeSecretJson(path, created);
|
|
1454
|
+
return { status: "ready", path, tokenId: token.id, created: true };
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
return { status: "failed", error };
|
|
1457
|
+
}
|
|
883
1458
|
}
|
|
884
1459
|
|
|
885
|
-
function
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
console.log(`Target: ${report.context.target}`);
|
|
890
|
-
console.log(`API: ${report.apiUrl}`);
|
|
891
|
-
console.log("");
|
|
1460
|
+
function deployAccessTokenMetadataIsUsable(token: { expires_at?: string }): boolean {
|
|
1461
|
+
if (!token.expires_at) {
|
|
1462
|
+
return true;
|
|
1463
|
+
}
|
|
892
1464
|
|
|
893
|
-
|
|
894
|
-
|
|
1465
|
+
const expiresAtMs = Date.parse(token.expires_at);
|
|
1466
|
+
return Number.isFinite(expiresAtMs) && expiresAtMs > Date.now();
|
|
1467
|
+
}
|
|
895
1468
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1469
|
+
async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; deploy_id?: string } | null> {
|
|
1470
|
+
try {
|
|
1471
|
+
const parsed = JSON.parse(await readFile(path, "utf8")) as DeployAccessTokenCreateResponse;
|
|
1472
|
+
return parsed.access_token ?? null;
|
|
1473
|
+
} catch {
|
|
1474
|
+
return null;
|
|
899
1475
|
}
|
|
900
1476
|
}
|
|
901
1477
|
|
|
902
|
-
function
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1478
|
+
async function writeSecretJson(path: string, value: unknown): Promise<void> {
|
|
1479
|
+
await mkdir(dirname(path), { recursive: true });
|
|
1480
|
+
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
1481
|
+
await chmod(path, 0o600);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
async function requireLocalDeployState(): Promise<LocalDeployState> {
|
|
1485
|
+
const state = await readLocalDeployStateIfExists(process.cwd());
|
|
906
1486
|
|
|
907
|
-
if (
|
|
908
|
-
|
|
1487
|
+
if (!state) {
|
|
1488
|
+
throw new Error("No .agentkit/deploy.json found. Run agentkit deploy first.");
|
|
909
1489
|
}
|
|
910
1490
|
|
|
911
|
-
return
|
|
1491
|
+
return state;
|
|
912
1492
|
}
|
|
913
1493
|
|
|
914
|
-
function
|
|
915
|
-
if (
|
|
916
|
-
|
|
1494
|
+
function requireDeployUrl(state: Pick<LocalDeployState, "deploy_id" | "url">): string {
|
|
1495
|
+
if (!state.url) {
|
|
1496
|
+
throw new Error(`Deploy ${state.deploy_id} does not have a URL. Run agentkit deploy again.`);
|
|
917
1497
|
}
|
|
918
1498
|
|
|
919
|
-
return
|
|
1499
|
+
return state.url;
|
|
920
1500
|
}
|
|
921
1501
|
|
|
922
|
-
function
|
|
923
|
-
|
|
1502
|
+
async function readDeployAccessTokenForHostedRequest(cwd: string): Promise<string> {
|
|
1503
|
+
const root = await findAgentCapsuleRoot(cwd);
|
|
1504
|
+
return readDeployAccessTokenForUi(join(root, defaultDeployChatAccessTokenPath));
|
|
924
1505
|
}
|
|
925
1506
|
|
|
926
|
-
async function
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
if (subcommand === "list") {
|
|
930
|
-
const deployState = await readDeployStateIfExists(process.cwd());
|
|
931
|
-
|
|
932
|
-
if (deployState) {
|
|
933
|
-
const channels = await cloudGet<{ channels: CloudChannel[] }>(
|
|
934
|
-
parseCloudApiUrl(args.flags.api),
|
|
935
|
-
`/v1/deploys/${encodeURIComponent(deployState.deploy_id)}/channels`,
|
|
936
|
-
);
|
|
937
|
-
printChannelsTable(channels.channels);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
1507
|
+
async function runDeploySmoke(input: { deployUrl: string; message: string; token?: string }): Promise<void> {
|
|
1508
|
+
const headers = new Headers({ "Content-Type": "application/json" });
|
|
940
1509
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
(capsule.config.channels ?? []).map((channel) => ({
|
|
944
|
-
id: "",
|
|
945
|
-
name: channel.name,
|
|
946
|
-
type: channel.type,
|
|
947
|
-
provider: channel.provider,
|
|
948
|
-
status: "local_config",
|
|
949
|
-
webhook_url: "",
|
|
950
|
-
required_secrets: channel.secrets.map((name) => ({ name, status: "missing" })),
|
|
951
|
-
})),
|
|
952
|
-
);
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
if (subcommand === "add") {
|
|
957
|
-
const type = first;
|
|
958
|
-
const name = second;
|
|
959
|
-
|
|
960
|
-
if (!type || !name || (type !== "website" && type !== "telegram" && type !== "whatsapp")) {
|
|
961
|
-
throw new Error("Usage: agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta]");
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
const deployState = await readDeployStateRequired(process.cwd());
|
|
965
|
-
const provider = channelProviderForCli(type, args.flags.provider);
|
|
966
|
-
const payload = {
|
|
967
|
-
name,
|
|
968
|
-
type,
|
|
969
|
-
provider,
|
|
970
|
-
secrets: defaultChannelSecretsForCli(type, provider),
|
|
971
|
-
};
|
|
972
|
-
const response = await cloudPost<{ channel: CloudChannel }>(
|
|
973
|
-
parseCloudApiUrl(args.flags.api),
|
|
974
|
-
`/v1/deploys/${encodeURIComponent(deployState.deploy_id)}/channels`,
|
|
975
|
-
payload,
|
|
976
|
-
);
|
|
977
|
-
printChannelStatus(response.channel);
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
if (subcommand === "setup") {
|
|
982
|
-
if (!first) {
|
|
983
|
-
throw new Error("Usage: agentkit channels setup <name> [--apply]");
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
987
|
-
printChannelStatus(channel);
|
|
988
|
-
if (channel.type === "telegram") {
|
|
989
|
-
if (args.flags.apply) {
|
|
990
|
-
await applyTelegramWebhook(channel);
|
|
991
|
-
console.log("Telegram webhook updated.");
|
|
992
|
-
} else {
|
|
993
|
-
console.log(`Setup: call Telegram setWebhook with ${channel.webhook_url}`);
|
|
994
|
-
console.log("Use --apply to mutate Telegram webhook settings.");
|
|
995
|
-
}
|
|
996
|
-
} else if (channel.type === "whatsapp") {
|
|
997
|
-
console.log(`Next step: paste this URL into ${channel.provider}'s webhook settings.`);
|
|
998
|
-
} else {
|
|
999
|
-
console.log(`Website endpoint: ${channel.webhook_url}`);
|
|
1000
|
-
console.log("Use this endpoint from the AgentKit website client or a signed server-to-server test.");
|
|
1001
|
-
}
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
if (subcommand === "status") {
|
|
1006
|
-
if (!first) {
|
|
1007
|
-
throw new Error("Usage: agentkit channels status <name>");
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
1011
|
-
printChannelStatus(channel);
|
|
1012
|
-
await printChannelDeliverySummary(channel, args.flags.api);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
if (subcommand === "test") {
|
|
1017
|
-
if (!first) {
|
|
1018
|
-
throw new Error('Usage: agentkit channels test <name> [--message "hello"] [--fixture <path>]');
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
1022
|
-
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
1023
|
-
const body = await channelTestBody(channel, args.flags.message, args.flags.fixture);
|
|
1024
|
-
const path = new URL(channel.webhook_url).pathname;
|
|
1025
|
-
const headers = channelTestHeaders(channel);
|
|
1026
|
-
const response = await cloudFetch(apiUrl, path, {
|
|
1027
|
-
method: channel.type === "whatsapp" && channel.provider === "meta" ? "POST" : "POST",
|
|
1028
|
-
headers,
|
|
1029
|
-
body,
|
|
1030
|
-
});
|
|
1031
|
-
const payload = await response.json();
|
|
1032
|
-
|
|
1033
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
1034
|
-
if (!response.ok) {
|
|
1035
|
-
process.exitCode = 1;
|
|
1036
|
-
}
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
if (subcommand === "deliveries") {
|
|
1041
|
-
if (first === "list") {
|
|
1042
|
-
if (!second) {
|
|
1043
|
-
throw new Error("Usage: agentkit channels deliveries list <name>");
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
const channel = await resolveCloudChannelByName(second, args.flags.api);
|
|
1047
|
-
const response = await cloudGet<{ deliveries: CloudDelivery[] }>(
|
|
1048
|
-
parseCloudApiUrl(args.flags.api),
|
|
1049
|
-
`/v1/channels/${encodeURIComponent(channel.id)}/deliveries`,
|
|
1050
|
-
);
|
|
1051
|
-
|
|
1052
|
-
if (response.deliveries.length === 0) {
|
|
1053
|
-
console.log("No channel deliveries found.");
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
console.log("ID\tCHANNEL\tDIRECTION\tSTATUS\tPROVIDER_EVENT\tRECEIVED");
|
|
1058
|
-
for (const delivery of response.deliveries) {
|
|
1059
|
-
console.log(
|
|
1060
|
-
[
|
|
1061
|
-
delivery.id,
|
|
1062
|
-
delivery.channel_id,
|
|
1063
|
-
delivery.direction,
|
|
1064
|
-
delivery.status,
|
|
1065
|
-
delivery.provider.event_id ?? "",
|
|
1066
|
-
delivery.received_at,
|
|
1067
|
-
].join("\t"),
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
if (first === "show") {
|
|
1074
|
-
if (!second) {
|
|
1075
|
-
throw new Error("Usage: agentkit channels deliveries show <delivery-id>");
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
const response = await cloudGet<{ delivery: CloudDelivery }>(
|
|
1079
|
-
parseCloudApiUrl(args.flags.api),
|
|
1080
|
-
`/v1/channel-deliveries/${encodeURIComponent(second)}`,
|
|
1081
|
-
);
|
|
1082
|
-
console.log(JSON.stringify(response.delivery, null, 2));
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
throw new Error(
|
|
1088
|
-
"Usage: agentkit channels list | add | setup | status | test | deliveries list | deliveries show",
|
|
1089
|
-
);
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
type DeployState = LocalDeployState;
|
|
1093
|
-
|
|
1094
|
-
type CloudChannel = {
|
|
1095
|
-
id: string;
|
|
1096
|
-
name: string;
|
|
1097
|
-
type: "website" | "telegram" | "whatsapp";
|
|
1098
|
-
provider: "agentkit" | "telegram" | "zapster" | "meta";
|
|
1099
|
-
status: string;
|
|
1100
|
-
webhook_url: string;
|
|
1101
|
-
required_secrets: Array<{ name: string; status: string }>;
|
|
1102
|
-
};
|
|
1103
|
-
|
|
1104
|
-
type CloudDelivery = {
|
|
1105
|
-
id: string;
|
|
1106
|
-
channel_id: string;
|
|
1107
|
-
direction: string;
|
|
1108
|
-
status: string;
|
|
1109
|
-
provider: {
|
|
1110
|
-
event_id?: string;
|
|
1111
|
-
};
|
|
1112
|
-
received_at: string;
|
|
1113
|
-
};
|
|
1114
|
-
|
|
1115
|
-
async function applyTelegramWebhook(channel: CloudChannel): Promise<void> {
|
|
1116
|
-
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
1117
|
-
const secretToken = process.env.TELEGRAM_WEBHOOK_SECRET;
|
|
1118
|
-
|
|
1119
|
-
if (!botToken || !secretToken) {
|
|
1120
|
-
throw new Error("TELEGRAM_BOT_TOKEN and TELEGRAM_WEBHOOK_SECRET must be set to apply Telegram setup.");
|
|
1510
|
+
if (input.token) {
|
|
1511
|
+
headers.set("Authorization", `Bearer ${input.token}`);
|
|
1121
1512
|
}
|
|
1122
1513
|
|
|
1123
1514
|
let response: Response;
|
|
1124
1515
|
|
|
1125
1516
|
try {
|
|
1126
|
-
response = await fetch(
|
|
1517
|
+
response = await fetch(new URL("/v1/chat", input.deployUrl).href, {
|
|
1127
1518
|
method: "POST",
|
|
1128
|
-
headers
|
|
1129
|
-
body: JSON.stringify({
|
|
1130
|
-
url: channel.webhook_url,
|
|
1131
|
-
secret_token: secretToken,
|
|
1132
|
-
allowed_updates: ["message"],
|
|
1133
|
-
}),
|
|
1519
|
+
headers,
|
|
1520
|
+
body: JSON.stringify({ message: input.message }),
|
|
1134
1521
|
});
|
|
1135
|
-
} catch {
|
|
1136
|
-
throw new
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
if (!response.ok || !isTelegramOk(payload)) {
|
|
1142
|
-
throw new Error(readTelegramSetupError(payload, response.status));
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function isTelegramOk(payload: unknown): payload is { ok: true } {
|
|
1147
|
-
return Boolean(payload && typeof payload === "object" && "ok" in payload && payload.ok === true);
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
function readTelegramSetupError(payload: unknown, status: number): string {
|
|
1151
|
-
if (payload && typeof payload === "object" && "description" in payload && typeof payload.description === "string") {
|
|
1152
|
-
return `Telegram setWebhook failed: ${payload.description}`;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
return `Telegram setWebhook failed with HTTP ${status}.`;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
async function readDeployStateIfExists(cwd: string): Promise<DeployState | null> {
|
|
1159
|
-
return readLocalDeployStateIfExists(cwd);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
async function readDeployStateRequired(cwd: string): Promise<DeployState> {
|
|
1163
|
-
const state = await readDeployStateIfExists(cwd);
|
|
1164
|
-
|
|
1165
|
-
if (!state?.deploy_id) {
|
|
1166
|
-
throw new Error("No .agentkit/deploy.json found. Run agentkit deploy before creating hosted channels.");
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
return state;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
function printChannelsTable(channels: CloudChannel[]): void {
|
|
1173
|
-
if (channels.length === 0) {
|
|
1174
|
-
console.log("No channels found.");
|
|
1175
|
-
return;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
console.log("NAME\tTYPE\tPROVIDER\tSTATUS\tLAST_EVENT");
|
|
1179
|
-
for (const channel of channels) {
|
|
1180
|
-
console.log([channel.name, channel.type, channel.provider, channel.status, ""].join("\t"));
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function printChannelStatus(channel: CloudChannel): void {
|
|
1185
|
-
console.log(`Channel: ${channel.name}`);
|
|
1186
|
-
if (channel.id) {
|
|
1187
|
-
console.log(`ID: ${channel.id}`);
|
|
1188
|
-
}
|
|
1189
|
-
console.log(`Type: ${channel.type}`);
|
|
1190
|
-
console.log(`Provider: ${channel.provider}`);
|
|
1191
|
-
console.log(`Status: ${channel.status}`);
|
|
1192
|
-
if (channel.webhook_url) {
|
|
1193
|
-
console.log(`Webhook URL: ${channel.webhook_url}`);
|
|
1194
|
-
}
|
|
1195
|
-
console.log("Required secrets:");
|
|
1196
|
-
for (const secret of channel.required_secrets) {
|
|
1197
|
-
console.log(` ${secret.name}: ${secret.status}`);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
async function printChannelDeliverySummary(
|
|
1202
|
-
channel: CloudChannel,
|
|
1203
|
-
apiFlag: string | boolean | undefined,
|
|
1204
|
-
): Promise<void> {
|
|
1205
|
-
const response = await cloudGet<{ deliveries: CloudDelivery[] }>(
|
|
1206
|
-
parseCloudApiUrl(apiFlag),
|
|
1207
|
-
`/v1/channels/${encodeURIComponent(channel.id)}/deliveries`,
|
|
1208
|
-
);
|
|
1209
|
-
const inbound = [...response.deliveries].reverse().find((delivery) => delivery.direction === "inbound");
|
|
1210
|
-
const outbound = [...response.deliveries].reverse().find((delivery) => delivery.direction === "outbound");
|
|
1211
|
-
|
|
1212
|
-
console.log("Recent delivery:");
|
|
1213
|
-
console.log(` Last inbound: ${inbound ? `${inbound.status} ${inbound.received_at}` : "none"}`);
|
|
1214
|
-
console.log(` Last outbound: ${outbound ? `${outbound.status} ${outbound.received_at}` : "none"}`);
|
|
1215
|
-
console.log("Provider health: not_checked");
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
async function resolveCloudChannelByName(name: string, apiFlag: string | boolean | undefined): Promise<CloudChannel> {
|
|
1219
|
-
const deployState = await readDeployStateRequired(process.cwd());
|
|
1220
|
-
const response = await cloudGet<{ channels: CloudChannel[] }>(
|
|
1221
|
-
parseCloudApiUrl(apiFlag),
|
|
1222
|
-
`/v1/deploys/${encodeURIComponent(deployState.deploy_id)}/channels`,
|
|
1223
|
-
);
|
|
1224
|
-
const channel = response.channels.find((candidate) => candidate.name === name);
|
|
1225
|
-
|
|
1226
|
-
if (!channel) {
|
|
1227
|
-
throw new Error(`Channel "${name}" was not found for deploy ${deployState.deploy_id}.`);
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
throw new AgentKitError(
|
|
1524
|
+
"deploy_smoke_failed",
|
|
1525
|
+
`Deploy smoke request failed before a response: ${error instanceof Error ? error.message : String(error)}`,
|
|
1526
|
+
);
|
|
1228
1527
|
}
|
|
1229
1528
|
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
function channelProviderForCli(type: string, providerFlag: string | boolean | undefined): string {
|
|
1234
|
-
if (type === "website") {
|
|
1235
|
-
return "agentkit";
|
|
1236
|
-
}
|
|
1529
|
+
const payload = await response.json().catch(() => null);
|
|
1237
1530
|
|
|
1238
|
-
if (
|
|
1239
|
-
|
|
1531
|
+
if (!response.ok) {
|
|
1532
|
+
throw new AgentKitError("deploy_smoke_failed", readCloudError(payload, response.status));
|
|
1240
1533
|
}
|
|
1241
1534
|
|
|
1242
|
-
const
|
|
1535
|
+
const content =
|
|
1536
|
+
payload && typeof payload === "object" && "message" in payload && isRecord((payload as { message?: unknown }).message)
|
|
1537
|
+
? (payload as { message: { content?: unknown } }).message.content
|
|
1538
|
+
: undefined;
|
|
1243
1539
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1540
|
+
console.log("");
|
|
1541
|
+
console.log("Deploy smoke verified");
|
|
1542
|
+
console.log(`URL: ${input.deployUrl}`);
|
|
1543
|
+
console.log(`Message: ${input.message}`);
|
|
1544
|
+
if (typeof content === "string") {
|
|
1545
|
+
console.log(`Response: ${content}`);
|
|
1246
1546
|
}
|
|
1247
|
-
|
|
1248
|
-
return provider;
|
|
1249
1547
|
}
|
|
1250
1548
|
|
|
1251
|
-
function
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1549
|
+
async function fetchDeployConversationJson(cwd: string, path: string): Promise<unknown> {
|
|
1550
|
+
const state = await requireLocalDeployState();
|
|
1551
|
+
const deployUrl = requireDeployUrl(state);
|
|
1552
|
+
const token = await readDeployAccessTokenForHostedRequest(cwd);
|
|
1553
|
+
const headers = new Headers();
|
|
1255
1554
|
|
|
1256
|
-
|
|
1257
|
-
return ["TELEGRAM_BOT_TOKEN", "TELEGRAM_WEBHOOK_SECRET"];
|
|
1258
|
-
}
|
|
1555
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1259
1556
|
|
|
1260
|
-
|
|
1261
|
-
return ["ZAPSTER_API_KEY", "ZAPSTER_WEBHOOK_SECRET"];
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
if (type === "whatsapp" && provider === "meta") {
|
|
1265
|
-
return ["META_WHATSAPP_ACCESS_TOKEN", "META_WHATSAPP_APP_SECRET", "META_WHATSAPP_VERIFY_TOKEN"];
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
return [];
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
async function channelTestBody(
|
|
1272
|
-
channel: CloudChannel,
|
|
1273
|
-
messageFlag: string | boolean | undefined,
|
|
1274
|
-
fixtureFlag: string | boolean | undefined,
|
|
1275
|
-
): Promise<string> {
|
|
1276
|
-
if (typeof fixtureFlag === "string") {
|
|
1277
|
-
return readFile(resolve(process.cwd(), fixtureFlag), "utf8");
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
const message = typeof messageFlag === "string" ? messageFlag : "hello";
|
|
1281
|
-
|
|
1282
|
-
if (channel.type === "telegram") {
|
|
1283
|
-
return JSON.stringify({
|
|
1284
|
-
update_id: Date.now(),
|
|
1285
|
-
message: {
|
|
1286
|
-
message_id: 1,
|
|
1287
|
-
chat: { id: 456, type: "private" },
|
|
1288
|
-
from: { id: 123, first_name: "AgentKit" },
|
|
1289
|
-
text: message,
|
|
1290
|
-
},
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1557
|
+
let response: Response;
|
|
1293
1558
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
message: {
|
|
1299
|
-
id: `msg_${Date.now()}`,
|
|
1300
|
-
from: "5511999999999",
|
|
1301
|
-
type: "text",
|
|
1302
|
-
text: { body: message },
|
|
1303
|
-
},
|
|
1559
|
+
try {
|
|
1560
|
+
response = await fetch(new URL(path, deployUrl).href, {
|
|
1561
|
+
method: "GET",
|
|
1562
|
+
headers,
|
|
1304
1563
|
});
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
throw new AgentKitError(
|
|
1566
|
+
"deploy_conversation_request_failed",
|
|
1567
|
+
`Could not read hosted conversation data: ${error instanceof Error ? error.message : String(error)}`,
|
|
1568
|
+
);
|
|
1305
1569
|
}
|
|
1306
1570
|
|
|
1307
|
-
|
|
1308
|
-
event_id: `web_${Date.now()}`,
|
|
1309
|
-
visitor_id: "visitor_test",
|
|
1310
|
-
message: {
|
|
1311
|
-
id: `msg_${Date.now()}`,
|
|
1312
|
-
text: message,
|
|
1313
|
-
},
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
function channelTestHeaders(channel: CloudChannel): Headers {
|
|
1318
|
-
const headers = new Headers({ "Content-Type": "application/json" });
|
|
1319
|
-
|
|
1320
|
-
if (channel.type === "website") {
|
|
1321
|
-
const token = process.env.AGENTKIT_WEBSITE_CHANNEL_TOKEN;
|
|
1322
|
-
|
|
1323
|
-
if (token) {
|
|
1324
|
-
headers.set("Authorization", `Bearer ${token}`);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
if (channel.type === "telegram") {
|
|
1329
|
-
headers.set("X-Telegram-Bot-Api-Secret-Token", process.env.TELEGRAM_WEBHOOK_SECRET ?? "");
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
if (channel.type === "whatsapp" && channel.provider === "zapster") {
|
|
1333
|
-
headers.set("X-Zapster-Webhook-Secret", process.env.ZAPSTER_WEBHOOK_SECRET ?? "");
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
return headers;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
async function cloudGet<T>(apiUrl: string, path: string): Promise<T> {
|
|
1340
|
-
const response = await cloudFetch(apiUrl, path);
|
|
1341
|
-
const payload = await response.json();
|
|
1342
|
-
|
|
1343
|
-
if (!response.ok) {
|
|
1344
|
-
throw new Error(readCloudError(payload, response.status));
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
return payload as T;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
async function cloudPost<T>(apiUrl: string, path: string, body: unknown): Promise<T> {
|
|
1351
|
-
const response = await cloudFetch(apiUrl, path, {
|
|
1352
|
-
method: "POST",
|
|
1353
|
-
headers: { "Content-Type": "application/json" },
|
|
1354
|
-
body: JSON.stringify(body),
|
|
1355
|
-
});
|
|
1356
|
-
const payload = await response.json();
|
|
1571
|
+
const payload = await response.json().catch(() => null);
|
|
1357
1572
|
|
|
1358
1573
|
if (!response.ok) {
|
|
1359
|
-
throw new
|
|
1574
|
+
throw new AgentKitError("deploy_conversation_request_failed", readCloudError(payload, response.status));
|
|
1360
1575
|
}
|
|
1361
1576
|
|
|
1362
|
-
return payload
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
function cloudFetch(apiUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
|
|
1366
|
-
const headers = new Headers(init.headers);
|
|
1367
|
-
const apiToken = process.env.AGENTKIT_CLOUD_API_TOKEN;
|
|
1368
|
-
|
|
1369
|
-
if (apiToken && !headers.has("Authorization")) {
|
|
1370
|
-
headers.set("Authorization", `Bearer ${apiToken}`);
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
return fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
|
|
1374
|
-
...init,
|
|
1375
|
-
headers,
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
type CloudAuthConfig = {
|
|
1380
|
-
apiUrl: string;
|
|
1381
|
-
token: string;
|
|
1382
|
-
};
|
|
1383
|
-
|
|
1384
|
-
function cloudAuthPath(): string {
|
|
1385
|
-
return join(homedir(), ".agentkit", "cloud.json");
|
|
1577
|
+
return payload;
|
|
1386
1578
|
}
|
|
1387
1579
|
|
|
1388
|
-
async function
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
token: process.env.AGENTKIT_CLOUD_API_TOKEN,
|
|
1393
|
-
};
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1580
|
+
async function fetchDeployImproveEvidence(
|
|
1581
|
+
apiUrl: string,
|
|
1582
|
+
input: { deployId: string; since?: string; conversationId?: string },
|
|
1583
|
+
): Promise<unknown | null> {
|
|
1396
1584
|
try {
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
}
|
|
1585
|
+
return await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(input.deployId)}/improve/export`, {
|
|
1586
|
+
method: "POST",
|
|
1587
|
+
body: JSON.stringify({
|
|
1588
|
+
status: "all",
|
|
1589
|
+
...(input.since ? { since: input.since } : {}),
|
|
1590
|
+
...(input.conversationId ? { conversation_id: input.conversationId } : {}),
|
|
1591
|
+
}),
|
|
1592
|
+
});
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
if (
|
|
1595
|
+
isAgentKitError(error) &&
|
|
1596
|
+
(error.code === "improve_evidence_store_not_configured" || error.code === "not_found")
|
|
1597
|
+
) {
|
|
1598
|
+
return null;
|
|
1404
1599
|
}
|
|
1405
|
-
} catch {
|
|
1406
|
-
return null;
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
return null;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
async function writeCloudAuth(config: CloudAuthConfig): Promise<void> {
|
|
1413
|
-
await mkdir(join(homedir(), ".agentkit"), { recursive: true });
|
|
1414
|
-
await writeFile(cloudAuthPath(), `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
async function clearCloudAuth(): Promise<void> {
|
|
1418
|
-
await rm(cloudAuthPath(), { force: true });
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
async function resolveCloudApiUrl(flag: string | boolean | undefined): Promise<string> {
|
|
1422
|
-
if (flag !== undefined) {
|
|
1423
|
-
return parseCloudApiUrl(flag);
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
const auth = await readCloudAuth();
|
|
1427
|
-
return auth?.apiUrl ?? parseCloudApiUrl(undefined);
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
async function requireLocalDeployState(): Promise<LocalDeployState> {
|
|
1431
|
-
const state = await readLocalDeployStateIfExists(process.cwd());
|
|
1432
|
-
|
|
1433
|
-
if (!state) {
|
|
1434
|
-
throw new Error("No .agentkit/deploy.json found. Run agentkit deploy first.");
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
return state;
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
async function resolveProjectIdForSecretCommand(cwd: string): Promise<string> {
|
|
1441
|
-
const state = await readLocalDeployStateIfExists(cwd);
|
|
1442
1600
|
|
|
1443
|
-
|
|
1444
|
-
return state.project_id;
|
|
1601
|
+
throw error;
|
|
1445
1602
|
}
|
|
1446
|
-
|
|
1447
|
-
const root = await findAgentCapsuleRoot(cwd);
|
|
1448
|
-
const capsule = await loadAgentCapsule(root);
|
|
1449
|
-
return stableCloudProjectId(capsule.config.name);
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
async function cloudApiRequest(apiUrl: string, path: string, init: RequestInit): Promise<unknown> {
|
|
1453
|
-
const auth = await readCloudAuth();
|
|
1454
|
-
const headers = new Headers(init.headers);
|
|
1455
|
-
|
|
1456
|
-
if (!headers.has("Content-Type") && init.body !== undefined) {
|
|
1457
|
-
headers.set("Content-Type", "application/json");
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
if (auth?.token && !headers.has("Authorization")) {
|
|
1461
|
-
headers.set("Authorization", `Bearer ${auth.token}`);
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
const response = await fetch(new URL(path, parseCloudApiUrl(apiUrl)).href, {
|
|
1465
|
-
...init,
|
|
1466
|
-
headers,
|
|
1467
|
-
});
|
|
1468
|
-
const payload = await response.json().catch(() => null);
|
|
1469
|
-
|
|
1470
|
-
if (!response.ok) {
|
|
1471
|
-
const code =
|
|
1472
|
-
payload && typeof payload === "object" && "error" in payload
|
|
1473
|
-
? String((payload as { error?: { code?: unknown } }).error?.code ?? "cloud_request_failed")
|
|
1474
|
-
: "cloud_request_failed";
|
|
1475
|
-
throw new AgentKitError(code, readCloudError(payload, response.status));
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
return payload;
|
|
1479
1603
|
}
|
|
1480
1604
|
|
|
1481
1605
|
function printJson(value: unknown): void {
|
|
1482
1606
|
console.log(JSON.stringify(value, null, 2));
|
|
1483
1607
|
}
|
|
1484
1608
|
|
|
1485
|
-
function
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1609
|
+
function printSkillStatus(status: Awaited<ReturnType<typeof checkDefaultSkillStatus>>): void {
|
|
1610
|
+
console.log("AgentKit skills");
|
|
1611
|
+
console.log(`Status: ${status.status}`);
|
|
1612
|
+
console.log(`Checked: ${status.checked}`);
|
|
1613
|
+
|
|
1614
|
+
if (status.missing.length > 0) {
|
|
1615
|
+
console.log(`Missing: ${status.missing.length}`);
|
|
1616
|
+
for (const file of status.missing.slice(0, 10)) {
|
|
1617
|
+
console.log(` ${file}`);
|
|
1490
1618
|
}
|
|
1491
1619
|
}
|
|
1492
1620
|
|
|
1493
|
-
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
return undefined;
|
|
1621
|
+
if (status.outdated.length > 0) {
|
|
1622
|
+
console.log(`Outdated: ${status.outdated.length}`);
|
|
1623
|
+
for (const file of status.outdated.slice(0, 10)) {
|
|
1624
|
+
console.log(` ${file}`);
|
|
1625
|
+
}
|
|
1499
1626
|
}
|
|
1500
1627
|
|
|
1501
|
-
if (
|
|
1502
|
-
|
|
1628
|
+
if (status.status !== "current") {
|
|
1629
|
+
console.log("Run: npm run agentkit -- skills sync");
|
|
1503
1630
|
}
|
|
1504
|
-
|
|
1505
|
-
const port = Number(value);
|
|
1506
|
-
|
|
1507
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
1508
|
-
throw new Error("Invalid port. Usage: agentkit dev --port <number>");
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
return port;
|
|
1512
1631
|
}
|
|
1513
1632
|
|
|
1514
|
-
function
|
|
1515
|
-
|
|
1516
|
-
return "cloudflare";
|
|
1517
|
-
}
|
|
1633
|
+
async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
|
|
1634
|
+
const outPath = parseOptionalStringFlag(outFlag, "out", usage);
|
|
1518
1635
|
|
|
1519
|
-
if (
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
);
|
|
1636
|
+
if (!outPath) {
|
|
1637
|
+
printJson(value);
|
|
1638
|
+
return;
|
|
1523
1639
|
}
|
|
1524
1640
|
|
|
1525
|
-
|
|
1641
|
+
const absolutePath = resolve(process.cwd(), outPath);
|
|
1642
|
+
await writeSecretJson(absolutePath, value);
|
|
1643
|
+
console.log(`Deploy access token written to ${absolutePath}`);
|
|
1526
1644
|
}
|
|
1527
1645
|
|
|
1528
|
-
function
|
|
1529
|
-
if (value === undefined) {
|
|
1530
|
-
return process.env.AGENTKIT_CLOUD_API_URL ?? defaultAgentKitCloudApiUrl;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1646
|
+
function parsePositiveIntegerFlag(value: string | boolean | undefined, name: string, usage: string): number {
|
|
1533
1647
|
if (typeof value !== "string") {
|
|
1534
|
-
throw new Error("Missing API URL. Usage: agentkit deploy --api <url>");
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
return value;
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
function parseRequiredStringFlag(value: string | boolean | undefined, name: string, usage: string): string {
|
|
1541
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1542
1648
|
throw new Error(`Missing ${name}. Usage: ${usage}`);
|
|
1543
1649
|
}
|
|
1544
1650
|
|
|
1545
|
-
|
|
1546
|
-
}
|
|
1651
|
+
const parsed = Number(value);
|
|
1547
1652
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
return undefined;
|
|
1653
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
1654
|
+
throw new Error(`Invalid ${name}. Usage: ${usage}`);
|
|
1551
1655
|
}
|
|
1552
1656
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
}
|
|
1657
|
+
return parsed;
|
|
1658
|
+
}
|
|
1556
1659
|
|
|
1557
|
-
|
|
1660
|
+
function relativePath(root: string, path: string): string {
|
|
1661
|
+
const output = relative(root, path);
|
|
1662
|
+
return output.startsWith("..") ? path : output || ".";
|
|
1558
1663
|
}
|
|
1559
1664
|
|
|
1560
1665
|
function printBuildResult(result: Awaited<ReturnType<typeof buildAgentCapsule>>): void {
|
|
@@ -1614,16 +1719,12 @@ async function deployCloudflare(wranglerPath: string, buildDir: string): Promise
|
|
|
1614
1719
|
}
|
|
1615
1720
|
}
|
|
1616
1721
|
|
|
1617
|
-
function
|
|
1618
|
-
|
|
1619
|
-
const child = spawn(command, args, {
|
|
1620
|
-
cwd,
|
|
1621
|
-
stdio: "inherit",
|
|
1622
|
-
});
|
|
1722
|
+
async function installProjectDependencies(projectPath: string): Promise<void> {
|
|
1723
|
+
const exitCode = await runInheritedCommand("npm", ["install"], projectPath);
|
|
1623
1724
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
}
|
|
1725
|
+
if (exitCode !== 0) {
|
|
1726
|
+
throw new Error(`Dependency install failed with exit code ${exitCode}. Run \`npm install\` in ${projectPath}.`);
|
|
1727
|
+
}
|
|
1627
1728
|
}
|
|
1628
1729
|
|
|
1629
1730
|
async function parseToolInput(
|
|
@@ -1669,21 +1770,81 @@ function parseJsonInput(source: string, label: string): unknown {
|
|
|
1669
1770
|
}
|
|
1670
1771
|
}
|
|
1671
1772
|
|
|
1672
|
-
async function
|
|
1673
|
-
await
|
|
1674
|
-
|
|
1773
|
+
async function findRunningLocalDevServer(cwd: string): Promise<{ url: string; agent: string } | null> {
|
|
1774
|
+
const capsule = await loadAgentCapsule(cwd);
|
|
1775
|
+
const basePort = localDevBasePort();
|
|
1675
1776
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
return;
|
|
1679
|
-
}
|
|
1777
|
+
for (let port = basePort; port <= basePort + 50; port += 1) {
|
|
1778
|
+
const state = await fetchLocalAgentKitState(`http://localhost:${port}/_agentkit`);
|
|
1680
1779
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1780
|
+
if (state?.agent === capsule.config.name) {
|
|
1781
|
+
return {
|
|
1782
|
+
url: `http://localhost:${port}`,
|
|
1783
|
+
agent: state.agent,
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1684
1787
|
|
|
1685
|
-
|
|
1686
|
-
|
|
1788
|
+
return null;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
function localDevBasePort(): number {
|
|
1792
|
+
const value = process.env.AGENTKIT_DEV_BASE_PORT;
|
|
1793
|
+
|
|
1794
|
+
if (!value) {
|
|
1795
|
+
return defaultAgentKitDevPort;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
const parsed = Number(value);
|
|
1799
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : defaultAgentKitDevPort;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
async function fetchLocalAgentKitState(url: string): Promise<{ agent?: string } | null> {
|
|
1803
|
+
const controller = new AbortController();
|
|
1804
|
+
const timeout = setTimeout(() => controller.abort(), 300);
|
|
1805
|
+
|
|
1806
|
+
try {
|
|
1807
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
1808
|
+
const payload = await response.json().catch(() => null);
|
|
1809
|
+
|
|
1810
|
+
if (payload && typeof payload === "object") {
|
|
1811
|
+
return {
|
|
1812
|
+
agent: typeof (payload as { agent?: unknown }).agent === "string" ? (payload as { agent: string }).agent : undefined,
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
} catch {
|
|
1816
|
+
return null;
|
|
1817
|
+
} finally {
|
|
1818
|
+
clearTimeout(timeout);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
async function openLocalUrl(url: string): Promise<void> {
|
|
1825
|
+
if (process.env.AGENTKIT_OPEN_COMMAND === "none") {
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
const command =
|
|
1830
|
+
process.env.AGENTKIT_OPEN_COMMAND ??
|
|
1831
|
+
(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open");
|
|
1832
|
+
const args = process.env.AGENTKIT_OPEN_COMMAND
|
|
1833
|
+
? [url]
|
|
1834
|
+
: process.platform === "win32"
|
|
1835
|
+
? ["/c", "start", "", url]
|
|
1836
|
+
: [url];
|
|
1837
|
+
|
|
1838
|
+
await new Promise<void>((resolvePromise, reject) => {
|
|
1839
|
+
const child = spawn(command, args, {
|
|
1840
|
+
detached: true,
|
|
1841
|
+
stdio: "ignore",
|
|
1842
|
+
});
|
|
1843
|
+
child.once("error", reject);
|
|
1844
|
+
child.once("spawn", () => {
|
|
1845
|
+
child.unref();
|
|
1846
|
+
resolvePromise();
|
|
1847
|
+
});
|
|
1687
1848
|
});
|
|
1688
1849
|
}
|
|
1689
1850
|
|
|
@@ -1691,5 +1852,6 @@ main().catch((error: unknown) => {
|
|
|
1691
1852
|
const message = error instanceof Error ? error.message : String(error);
|
|
1692
1853
|
const prefix = isAgentKitError(error) ? `agentkit ${error.code}` : "agentkit";
|
|
1693
1854
|
console.error(`${prefix}: ${message}`);
|
|
1855
|
+
console.error("Feedback draft: agentkit feedback create --about last-run --kind bug --summary <text>");
|
|
1694
1856
|
process.exitCode = 1;
|
|
1695
1857
|
});
|