@agent-native/core 0.52.0 → 0.53.0
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 +41 -95
- package/blueprints/action/crud.md +98 -0
- package/blueprints/channel/discord.md +74 -0
- package/blueprints/provider/stripe.md +87 -0
- package/blueprints/sandbox/docker.md +78 -0
- package/dist/action.d.ts +24 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +4 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/observational-memory/compactor.d.ts +43 -0
- package/dist/agent/observational-memory/compactor.d.ts.map +1 -0
- package/dist/agent/observational-memory/compactor.js +50 -0
- package/dist/agent/observational-memory/compactor.js.map +1 -0
- package/dist/agent/observational-memory/config.d.ts +37 -0
- package/dist/agent/observational-memory/config.d.ts.map +1 -0
- package/dist/agent/observational-memory/config.js +48 -0
- package/dist/agent/observational-memory/config.js.map +1 -0
- package/dist/agent/observational-memory/index.d.ts +26 -0
- package/dist/agent/observational-memory/index.d.ts.map +1 -0
- package/dist/agent/observational-memory/index.js +25 -0
- package/dist/agent/observational-memory/index.js.map +1 -0
- package/dist/agent/observational-memory/internal-run.d.ts +37 -0
- package/dist/agent/observational-memory/internal-run.d.ts.map +1 -0
- package/dist/agent/observational-memory/internal-run.js +59 -0
- package/dist/agent/observational-memory/internal-run.js.map +1 -0
- package/dist/agent/observational-memory/message-text.d.ts +13 -0
- package/dist/agent/observational-memory/message-text.d.ts.map +1 -0
- package/dist/agent/observational-memory/message-text.js +46 -0
- package/dist/agent/observational-memory/message-text.js.map +1 -0
- package/dist/agent/observational-memory/migrations.d.ts +13 -0
- package/dist/agent/observational-memory/migrations.d.ts.map +1 -0
- package/dist/agent/observational-memory/migrations.js +43 -0
- package/dist/agent/observational-memory/migrations.js.map +1 -0
- package/dist/agent/observational-memory/observer.d.ts +37 -0
- package/dist/agent/observational-memory/observer.d.ts.map +1 -0
- package/dist/agent/observational-memory/observer.js +82 -0
- package/dist/agent/observational-memory/observer.js.map +1 -0
- package/dist/agent/observational-memory/plugin.d.ts +16 -0
- package/dist/agent/observational-memory/plugin.d.ts.map +1 -0
- package/dist/agent/observational-memory/plugin.js +26 -0
- package/dist/agent/observational-memory/plugin.js.map +1 -0
- package/dist/agent/observational-memory/prompts.d.ts +27 -0
- package/dist/agent/observational-memory/prompts.d.ts.map +1 -0
- package/dist/agent/observational-memory/prompts.js +42 -0
- package/dist/agent/observational-memory/prompts.js.map +1 -0
- package/dist/agent/observational-memory/read.d.ts +47 -0
- package/dist/agent/observational-memory/read.d.ts.map +1 -0
- package/dist/agent/observational-memory/read.js +99 -0
- package/dist/agent/observational-memory/read.js.map +1 -0
- package/dist/agent/observational-memory/reflector.d.ts +31 -0
- package/dist/agent/observational-memory/reflector.d.ts.map +1 -0
- package/dist/agent/observational-memory/reflector.js +76 -0
- package/dist/agent/observational-memory/reflector.js.map +1 -0
- package/dist/agent/observational-memory/schema.d.ts +267 -0
- package/dist/agent/observational-memory/schema.d.ts.map +1 -0
- package/dist/agent/observational-memory/schema.js +48 -0
- package/dist/agent/observational-memory/schema.js.map +1 -0
- package/dist/agent/observational-memory/store.d.ts +52 -0
- package/dist/agent/observational-memory/store.d.ts.map +1 -0
- package/dist/agent/observational-memory/store.js +197 -0
- package/dist/agent/observational-memory/store.js.map +1 -0
- package/dist/agent/observational-memory/types.d.ts +61 -0
- package/dist/agent/observational-memory/types.d.ts.map +1 -0
- package/dist/agent/observational-memory/types.js +9 -0
- package/dist/agent/observational-memory/types.js.map +1 -0
- package/dist/agent/production-agent.d.ts +15 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +240 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +49 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +55 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/runtime-context.d.ts +30 -0
- package/dist/agent/runtime-context.d.ts.map +1 -1
- package/dist/agent/runtime-context.js +54 -1
- package/dist/agent/runtime-context.js.map +1 -1
- package/dist/agent/tool-call-journal.d.ts +101 -0
- package/dist/agent/tool-call-journal.d.ts.map +1 -0
- package/dist/agent/tool-call-journal.js +214 -0
- package/dist/agent/tool-call-journal.js.map +1 -0
- package/dist/agent/types.d.ts +24 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/add.d.ts +109 -0
- package/dist/cli/add.d.ts.map +1 -0
- package/dist/cli/add.js +352 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/connect.d.ts +2 -2
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +92 -24
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/eval.d.ts +17 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +121 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/index.js +44 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +11 -5
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/plan-local.d.ts +66 -5
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +495 -19
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +70 -59
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +118 -92
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +16 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +20 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +32 -7
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +13 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +21 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/db/client.d.ts +4 -2
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +6 -4
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/route-discovery.d.ts.map +1 -1
- package/dist/deploy/route-discovery.js +1 -0
- package/dist/deploy/route-discovery.js.map +1 -1
- package/dist/eval/agent-runner.d.ts +63 -0
- package/dist/eval/agent-runner.d.ts.map +1 -0
- package/dist/eval/agent-runner.js +142 -0
- package/dist/eval/agent-runner.js.map +1 -0
- package/dist/eval/define-eval.d.ts +29 -0
- package/dist/eval/define-eval.d.ts.map +1 -0
- package/dist/eval/define-eval.js +43 -0
- package/dist/eval/define-eval.js.map +1 -0
- package/dist/eval/index.d.ts +18 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +17 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/report.d.ts +8 -0
- package/dist/eval/report.d.ts.map +1 -0
- package/dist/eval/report.js +44 -0
- package/dist/eval/report.js.map +1 -0
- package/dist/eval/runner.d.ts +67 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +256 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/eval/scorer.d.ts +83 -0
- package/dist/eval/scorer.d.ts.map +1 -0
- package/dist/eval/scorer.js +195 -0
- package/dist/eval/scorer.js.map +1 -0
- package/dist/eval/types.d.ts +162 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +20 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +100 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/observability/tracing.d.ts +73 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +126 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +4 -1
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +10 -3
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +9 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +118 -110
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts +62 -0
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +99 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +7 -4
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +2 -0
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +33 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +10 -0
- package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/docs/content/agent-teams.md +32 -0
- package/docs/content/blueprint-installer.md +73 -0
- package/docs/content/evals.md +141 -0
- package/docs/content/pr-visual-recap.md +7 -4
- package/docs/content/sandbox-adapters.md +134 -0
- package/docs/content/template-plan.md +20 -8
- package/package.json +5 -1
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +10 -0
- package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
package/dist/cli/add.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agent-native add <kind> [name|url]` — the **blueprint installer**.
|
|
3
|
+
*
|
|
4
|
+
* Borrowed from Flue's `flue add`: instead of being a dumb scaffolder that
|
|
5
|
+
* writes files for you, this command emits a curated Markdown *integration
|
|
6
|
+
* blueprint* to stdout. You pipe that blueprint into your own coding agent,
|
|
7
|
+
* which applies the changes against the live repo:
|
|
8
|
+
*
|
|
9
|
+
* agent-native add provider stripe | claude
|
|
10
|
+
* agent-native add channel discord | codex
|
|
11
|
+
*
|
|
12
|
+
* This fits the agent-applies-changes, filesystem-first house style: the
|
|
13
|
+
* framework supplies the recipe (the canonical files to touch, the rules to
|
|
14
|
+
* honor, the verification step), and the coding agent does the editing with
|
|
15
|
+
* full repo context.
|
|
16
|
+
*
|
|
17
|
+
* A bare name resolves a curated blueprint from `blueprints/<kind>/<name>.md`.
|
|
18
|
+
* A URL instead of a name emits a GENERIC "research-and-integrate" blueprint
|
|
19
|
+
* for that kind with the URL embedded as the research starting point (mirrors
|
|
20
|
+
* Flue: a URL is a research seed, not a known recipe).
|
|
21
|
+
*
|
|
22
|
+
* Blueprint `.md` files ship in the published package via the `blueprints`
|
|
23
|
+
* entry in `package.json` `files`, so they live at
|
|
24
|
+
* `node_modules/@agent-native/core/blueprints/**` at runtime. Resolution works
|
|
25
|
+
* both from source (tsx: `src/cli` → `../../blueprints`) and from the compiled
|
|
26
|
+
* package (`dist/cli` → `../../blueprints`), with an upward-walk fallback.
|
|
27
|
+
*/
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import { fileURLToPath } from "node:url";
|
|
31
|
+
/**
|
|
32
|
+
* Locate the directory that holds the blueprint `.md` recipes.
|
|
33
|
+
*
|
|
34
|
+
* Both `src/cli` (tsx/source) and `dist/cli` (published) sit two levels under
|
|
35
|
+
* the package root, where `blueprints/` lives, so `../../blueprints` from this
|
|
36
|
+
* module's directory is the primary path. We additionally walk upward looking
|
|
37
|
+
* for a `blueprints` directory as a resilience fallback (e.g. unusual bundler
|
|
38
|
+
* layouts). An explicit override is honored for tests.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveBlueprintsRoot(overrideRoot) {
|
|
41
|
+
if (overrideRoot)
|
|
42
|
+
return overrideRoot;
|
|
43
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
44
|
+
const primary = path.resolve(here, "../../blueprints");
|
|
45
|
+
if (isDir(primary))
|
|
46
|
+
return primary;
|
|
47
|
+
// Fallback: walk up from this module looking for a sibling `blueprints` dir.
|
|
48
|
+
let dir = here;
|
|
49
|
+
for (let i = 0; i < 8; i += 1) {
|
|
50
|
+
const candidate = path.join(dir, "blueprints");
|
|
51
|
+
if (isDir(candidate))
|
|
52
|
+
return candidate;
|
|
53
|
+
const parent = path.dirname(dir);
|
|
54
|
+
if (parent === dir)
|
|
55
|
+
break;
|
|
56
|
+
dir = parent;
|
|
57
|
+
}
|
|
58
|
+
// Return the primary path even if missing so error messages are concrete.
|
|
59
|
+
return primary;
|
|
60
|
+
}
|
|
61
|
+
function isDir(p) {
|
|
62
|
+
try {
|
|
63
|
+
return fs.statSync(p).isDirectory();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** True for an argument that should be treated as a research URL, not a name. */
|
|
70
|
+
export function looksLikeUrl(value) {
|
|
71
|
+
return /^https?:\/\//i.test(value.trim());
|
|
72
|
+
}
|
|
73
|
+
/** List the blueprint kinds (subdirectories) available, sorted. */
|
|
74
|
+
export function listKinds(root) {
|
|
75
|
+
if (!isDir(root))
|
|
76
|
+
return [];
|
|
77
|
+
return fs
|
|
78
|
+
.readdirSync(root, { withFileTypes: true })
|
|
79
|
+
.filter((e) => e.isDirectory())
|
|
80
|
+
.map((e) => e.name)
|
|
81
|
+
.sort();
|
|
82
|
+
}
|
|
83
|
+
/** List the blueprint names available under a kind, sorted (no `.md`). */
|
|
84
|
+
export function listBlueprintNames(root, kind) {
|
|
85
|
+
const dir = path.join(root, kind);
|
|
86
|
+
if (!isDir(dir))
|
|
87
|
+
return [];
|
|
88
|
+
return fs
|
|
89
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
90
|
+
.filter((e) => e.isFile() && e.name.endsWith(".md"))
|
|
91
|
+
.map((e) => e.name.slice(0, -".md".length))
|
|
92
|
+
.sort();
|
|
93
|
+
}
|
|
94
|
+
/** A flat catalog of every kind and its blueprint names. */
|
|
95
|
+
export function listCatalog(root) {
|
|
96
|
+
return listKinds(root).map((kind) => ({
|
|
97
|
+
kind,
|
|
98
|
+
names: listBlueprintNames(root, kind),
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
/** Render the `--list` / no-args catalog text. */
|
|
102
|
+
export function formatCatalog(root) {
|
|
103
|
+
const catalog = listCatalog(root);
|
|
104
|
+
const lines = [];
|
|
105
|
+
lines.push("Available blueprints:");
|
|
106
|
+
lines.push("");
|
|
107
|
+
if (catalog.length === 0) {
|
|
108
|
+
lines.push(" (none found)");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
for (const { kind, names } of catalog) {
|
|
112
|
+
const shown = names.length > 0 ? names.join(", ") : "(generic — pass a URL)";
|
|
113
|
+
lines.push(` ${kind.padEnd(10)} ${shown}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push("Usage:");
|
|
118
|
+
lines.push(" agent-native add <kind> <name> Print a curated blueprint");
|
|
119
|
+
lines.push(" agent-native add <kind> <https://docs…> Research-and-integrate from a URL");
|
|
120
|
+
lines.push(" agent-native add --list Show this list");
|
|
121
|
+
lines.push("");
|
|
122
|
+
lines.push("Pipe a blueprint into your coding agent to apply it:");
|
|
123
|
+
lines.push(" agent-native add provider stripe | claude");
|
|
124
|
+
lines.push(" agent-native add channel discord | codex");
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the generic "research-and-integrate" blueprint emitted when the user
|
|
129
|
+
* passes a URL instead of a known blueprint name. Mirrors Flue: a URL is a
|
|
130
|
+
* research seed. We keep this self-contained — the coding agent reading it has
|
|
131
|
+
* no other context.
|
|
132
|
+
*/
|
|
133
|
+
export function buildGenericUrlBlueprint(kind, url) {
|
|
134
|
+
const kindGuidance = GENERIC_KIND_GUIDANCE[kind] ?? GENERIC_DEFAULT_GUIDANCE;
|
|
135
|
+
return `# Blueprint: integrate a new ${kind} from a URL
|
|
136
|
+
|
|
137
|
+
You are a coding agent working inside an **agent-native** app (a repo built on
|
|
138
|
+
\`@agent-native/core\`). Apply this blueprint as real source changes on the
|
|
139
|
+
current branch. Do not just describe the work — research, implement, then verify.
|
|
140
|
+
|
|
141
|
+
## Research seed
|
|
142
|
+
|
|
143
|
+
Start from this URL and follow it for the API/protocol/contract you need:
|
|
144
|
+
|
|
145
|
+
${url}
|
|
146
|
+
|
|
147
|
+
Fetch it (and the pages it links to) to learn the real endpoints, auth model,
|
|
148
|
+
payload shapes, and any signature/verification requirements. Do not guess from
|
|
149
|
+
training data — the docs are the source of truth, and package/API versions
|
|
150
|
+
drift. Confirm the current version before writing code.
|
|
151
|
+
|
|
152
|
+
## What you're adding
|
|
153
|
+
|
|
154
|
+
A new **${kind}** integration. ${kindGuidance}
|
|
155
|
+
|
|
156
|
+
## How agent-native wants it built
|
|
157
|
+
|
|
158
|
+
- **Actions are the single source of truth.** Add app operations in \`actions/\`
|
|
159
|
+
with \`defineAction\` (Zod schema). The agent calls them as tools; the frontend
|
|
160
|
+
calls the same action through \`useActionQuery\` / \`useActionMutation\`. Do NOT
|
|
161
|
+
add \`/api/*\` or Nitro pass-through routes that just wrap an action.
|
|
162
|
+
- **Prefer the provider-api substrate for external HTTP.** For ad-hoc reads
|
|
163
|
+
against a third-party API, register the provider with
|
|
164
|
+
\`@agent-native/core/provider-api\` and expose the
|
|
165
|
+
\`provider-api-catalog\` / \`provider-api-docs\` / \`provider-api-request\` trio
|
|
166
|
+
instead of one rigid action per endpoint. First-class actions are shortcuts,
|
|
167
|
+
not capability limits.
|
|
168
|
+
- **Read the relevant skill before editing** — \`actions\`,
|
|
169
|
+
\`integration-webhooks\`, \`secrets\`, \`onboarding\`, \`sharing\`, \`security\` —
|
|
170
|
+
for the area you're touching.
|
|
171
|
+
- **Never hardcode secrets.** Read API keys / tokens / signing secrets from the
|
|
172
|
+
secret store (\`readAppSecret\`, the provider credential adapter, OAuth token
|
|
173
|
+
helpers) at call time. Use obviously-fake placeholders in any example
|
|
174
|
+
(e.g. \`sk_test_PLACEHOLDER_xxx\`), never a real credential.
|
|
175
|
+
- **Scope ownable data.** Tables with \`ownableColumns()\` require
|
|
176
|
+
\`accessFilter\` / \`resolveAccess\` / \`assertAccess\`; fail closed.
|
|
177
|
+
- **Changeset.** If you edit a publishable package's source
|
|
178
|
+
(\`packages/core\`, \`packages/dispatch\`, \`packages/scheduling\`,
|
|
179
|
+
\`packages/pinpoint\`), add a \`.changeset/*.md\`.
|
|
180
|
+
|
|
181
|
+
## Verify
|
|
182
|
+
|
|
183
|
+
1. \`agent-native typecheck\` (or \`tsc --noEmit\`) passes.
|
|
184
|
+
2. Add a focused \`*.spec.ts\` for the new surface and run it.
|
|
185
|
+
3. Exercise the real workflow end to end: invoke the new action from the agent
|
|
186
|
+
chat (and the UI if applicable), and confirm the external call round-trips
|
|
187
|
+
with credentials injected and secrets redacted.
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
190
|
+
const GENERIC_DEFAULT_GUIDANCE = "Identify whether it is best modeled as an action, a provider-api integration, " +
|
|
191
|
+
"an inbound channel adapter, or a sandbox backend, and follow that area's skill.";
|
|
192
|
+
const GENERIC_KIND_GUIDANCE = {
|
|
193
|
+
provider: "Wire it into the provider-api substrate (`@agent-native/core/provider-api`): " +
|
|
194
|
+
"register the base URL, auth style, credential key, and docs URLs, then expose " +
|
|
195
|
+
"the `provider-api-catalog` / `provider-api-docs` / `provider-api-request` trio " +
|
|
196
|
+
"so any endpoint is reachable without one action per endpoint.",
|
|
197
|
+
channel: "Implement a `PlatformAdapter` (see " +
|
|
198
|
+
"`packages/core/src/integrations/types.ts` and the adapters under " +
|
|
199
|
+
"`packages/core/src/integrations/adapters/`) and register it in " +
|
|
200
|
+
"`getDefaultAdapters()` in `packages/core/src/integrations/plugin.ts`. " +
|
|
201
|
+
"Enqueue to SQL and return 200 fast; run the agent loop in the separate " +
|
|
202
|
+
"`_process-task` execution. Read the `integration-webhooks` skill.",
|
|
203
|
+
sandbox: "Implement the `SandboxAdapter` seam in " +
|
|
204
|
+
"`packages/core/src/coding-tools/sandbox/` (mirror " +
|
|
205
|
+
"`local-child-process-adapter.ts`) and select it via `AGENT_NATIVE_SANDBOX` " +
|
|
206
|
+
"or `registerSandboxAdapter()`. The adapter only runs the already-prepared, " +
|
|
207
|
+
"non-secret module source — it never sees app secrets.",
|
|
208
|
+
action: "Add a single multi-surface `defineAction` in `actions/` with a Zod schema. " +
|
|
209
|
+
"Keep the surface small and orthogonal (one CRUD-style `update` over N " +
|
|
210
|
+
"per-field actions). Read the `actions` skill.",
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Resolve a blueprint for a `kind` + optional `name`/`url`.
|
|
214
|
+
*
|
|
215
|
+
* - A known name → the curated `blueprints/<kind>/<name>.md`.
|
|
216
|
+
* - A URL → the generic research-and-integrate blueprint for the kind.
|
|
217
|
+
* - Unknown name → throws `AddResolutionError` listing what's available.
|
|
218
|
+
*/
|
|
219
|
+
export function resolveBlueprint(opts) {
|
|
220
|
+
const { kind, nameOrUrl, root } = opts;
|
|
221
|
+
const kinds = listKinds(root);
|
|
222
|
+
if (!kinds.includes(kind)) {
|
|
223
|
+
throw new AddResolutionError(`Unknown blueprint kind: ${kind}\n\n${formatCatalog(root)}`);
|
|
224
|
+
}
|
|
225
|
+
// A URL seed → generic blueprint for the kind.
|
|
226
|
+
if (nameOrUrl && looksLikeUrl(nameOrUrl)) {
|
|
227
|
+
return {
|
|
228
|
+
markdown: buildGenericUrlBlueprint(kind, nameOrUrl.trim()),
|
|
229
|
+
source: {
|
|
230
|
+
kind: "generic-url",
|
|
231
|
+
blueprintKind: kind,
|
|
232
|
+
url: nameOrUrl.trim(),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const names = listBlueprintNames(root, kind);
|
|
237
|
+
// No name given: if exactly one curated blueprint exists, use it; otherwise
|
|
238
|
+
// ask the user to pick one (or pass a URL for the generic path).
|
|
239
|
+
if (!nameOrUrl) {
|
|
240
|
+
if (names.length === 1) {
|
|
241
|
+
return loadCurated(root, kind, names[0]);
|
|
242
|
+
}
|
|
243
|
+
throw new AddResolutionError(`Specify a ${kind} blueprint name or a URL.\n` +
|
|
244
|
+
` Available ${kind} blueprints: ${names.length ? names.join(", ") : "(none — pass a URL)"}\n` +
|
|
245
|
+
` Or research from a URL: agent-native add ${kind} https://…`);
|
|
246
|
+
}
|
|
247
|
+
if (!names.includes(nameOrUrl)) {
|
|
248
|
+
throw new AddResolutionError(`Unknown ${kind} blueprint: ${nameOrUrl}\n` +
|
|
249
|
+
` Available ${kind} blueprints: ${names.length ? names.join(", ") : "(none)"}\n` +
|
|
250
|
+
` Or research from a URL: agent-native add ${kind} https://…`);
|
|
251
|
+
}
|
|
252
|
+
return loadCurated(root, kind, nameOrUrl);
|
|
253
|
+
}
|
|
254
|
+
function loadCurated(root, kind, name) {
|
|
255
|
+
const filePath = path.join(root, kind, `${name}.md`);
|
|
256
|
+
const markdown = fs.readFileSync(filePath, "utf-8");
|
|
257
|
+
return {
|
|
258
|
+
markdown,
|
|
259
|
+
source: { kind: "curated", blueprintKind: kind, name, path: filePath },
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/** Thrown for an unknown kind/name; the CLI prints the message and exits 1. */
|
|
263
|
+
export class AddResolutionError extends Error {
|
|
264
|
+
constructor(message) {
|
|
265
|
+
super(message);
|
|
266
|
+
this.name = "AddResolutionError";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
export function parseAddArgs(argv) {
|
|
270
|
+
const out = {
|
|
271
|
+
list: false,
|
|
272
|
+
help: false,
|
|
273
|
+
print: false,
|
|
274
|
+
positionals: [],
|
|
275
|
+
};
|
|
276
|
+
for (const arg of argv) {
|
|
277
|
+
if (arg === "--")
|
|
278
|
+
continue;
|
|
279
|
+
if (arg === "--list" || arg === "-l")
|
|
280
|
+
out.list = true;
|
|
281
|
+
else if (arg === "--help" || arg === "-h")
|
|
282
|
+
out.help = true;
|
|
283
|
+
else if (arg === "--print" || arg === "-p")
|
|
284
|
+
out.print = true;
|
|
285
|
+
else if (arg.startsWith("-")) {
|
|
286
|
+
// Ignore unknown flags rather than misparse them as a name.
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
else
|
|
290
|
+
out.positionals.push(arg);
|
|
291
|
+
}
|
|
292
|
+
return out;
|
|
293
|
+
}
|
|
294
|
+
const HELP = `agent-native add — emit an integration blueprint for your coding agent.
|
|
295
|
+
|
|
296
|
+
Instead of scaffolding files, \`add\` prints a curated Markdown recipe to stdout.
|
|
297
|
+
Pipe it into a coding agent (Claude Code, Codex, …) and it applies the changes
|
|
298
|
+
against your live repo, the agent-native way.
|
|
299
|
+
|
|
300
|
+
Usage:
|
|
301
|
+
agent-native add <kind> <name> Print a curated blueprint
|
|
302
|
+
agent-native add <kind> <https://docs…> Research-and-integrate from a URL
|
|
303
|
+
agent-native add --list List available kinds and blueprints
|
|
304
|
+
|
|
305
|
+
Examples:
|
|
306
|
+
agent-native add provider stripe | claude
|
|
307
|
+
agent-native add channel discord | codex
|
|
308
|
+
agent-native add sandbox docker
|
|
309
|
+
agent-native add action crud
|
|
310
|
+
agent-native add provider https://docs.example.com/api | claude
|
|
311
|
+
|
|
312
|
+
Options:
|
|
313
|
+
-l, --list List available blueprint kinds and names
|
|
314
|
+
-p, --print Print the blueprint to stdout (the default; explicit no-op)
|
|
315
|
+
-h, --help Show this help`;
|
|
316
|
+
/**
|
|
317
|
+
* CLI entry point. Returns the process exit code so the dispatcher / tests can
|
|
318
|
+
* assert on it. Writes the blueprint Markdown to stdout and diagnostics to
|
|
319
|
+
* stderr so `... | claude` only receives the blueprint.
|
|
320
|
+
*/
|
|
321
|
+
export function runAdd(argv, io = {}) {
|
|
322
|
+
const out = io.out ?? ((s) => process.stdout.write(s));
|
|
323
|
+
const err = io.err ?? ((s) => process.stderr.write(s));
|
|
324
|
+
const root = resolveBlueprintsRoot(io.root);
|
|
325
|
+
const parsed = parseAddArgs(argv);
|
|
326
|
+
if (parsed.help) {
|
|
327
|
+
out(HELP + "\n");
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
// `--list` or no positionals → show the catalog (to stdout; this is the
|
|
331
|
+
// requested output, not an error).
|
|
332
|
+
if (parsed.list || parsed.positionals.length === 0) {
|
|
333
|
+
out(formatCatalog(root) + "\n");
|
|
334
|
+
return 0;
|
|
335
|
+
}
|
|
336
|
+
const [kind, nameOrUrl] = parsed.positionals;
|
|
337
|
+
try {
|
|
338
|
+
const resolved = resolveBlueprint({ kind, nameOrUrl, root });
|
|
339
|
+
out(resolved.markdown.endsWith("\n")
|
|
340
|
+
? resolved.markdown
|
|
341
|
+
: resolved.markdown + "\n");
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
if (e instanceof AddResolutionError) {
|
|
346
|
+
err(e.message + "\n");
|
|
347
|
+
return 1;
|
|
348
|
+
}
|
|
349
|
+
throw e;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/cli/add.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `agent-native add <kind> [name|url]` — the **blueprint installer**.\n *\n * Borrowed from Flue's `flue add`: instead of being a dumb scaffolder that\n * writes files for you, this command emits a curated Markdown *integration\n * blueprint* to stdout. You pipe that blueprint into your own coding agent,\n * which applies the changes against the live repo:\n *\n * agent-native add provider stripe | claude\n * agent-native add channel discord | codex\n *\n * This fits the agent-applies-changes, filesystem-first house style: the\n * framework supplies the recipe (the canonical files to touch, the rules to\n * honor, the verification step), and the coding agent does the editing with\n * full repo context.\n *\n * A bare name resolves a curated blueprint from `blueprints/<kind>/<name>.md`.\n * A URL instead of a name emits a GENERIC \"research-and-integrate\" blueprint\n * for that kind with the URL embedded as the research starting point (mirrors\n * Flue: a URL is a research seed, not a known recipe).\n *\n * Blueprint `.md` files ship in the published package via the `blueprints`\n * entry in `package.json` `files`, so they live at\n * `node_modules/@agent-native/core/blueprints/**` at runtime. Resolution works\n * both from source (tsx: `src/cli` → `../../blueprints`) and from the compiled\n * package (`dist/cli` → `../../blueprints`), with an upward-walk fallback.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** A coarse classification of what `add` resolved for a given invocation. */\nexport type AddBlueprintSource =\n | { kind: \"curated\"; blueprintKind: string; name: string; path: string }\n | { kind: \"generic-url\"; blueprintKind: string; url: string };\n\nexport interface ResolvedBlueprint {\n /** The Markdown to print to stdout (piped into a coding agent). */\n markdown: string;\n source: AddBlueprintSource;\n}\n\n/**\n * Locate the directory that holds the blueprint `.md` recipes.\n *\n * Both `src/cli` (tsx/source) and `dist/cli` (published) sit two levels under\n * the package root, where `blueprints/` lives, so `../../blueprints` from this\n * module's directory is the primary path. We additionally walk upward looking\n * for a `blueprints` directory as a resilience fallback (e.g. unusual bundler\n * layouts). An explicit override is honored for tests.\n */\nexport function resolveBlueprintsRoot(overrideRoot?: string): string {\n if (overrideRoot) return overrideRoot;\n\n const here = path.dirname(fileURLToPath(import.meta.url));\n const primary = path.resolve(here, \"../../blueprints\");\n if (isDir(primary)) return primary;\n\n // Fallback: walk up from this module looking for a sibling `blueprints` dir.\n let dir = here;\n for (let i = 0; i < 8; i += 1) {\n const candidate = path.join(dir, \"blueprints\");\n if (isDir(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n // Return the primary path even if missing so error messages are concrete.\n return primary;\n}\n\nfunction isDir(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** True for an argument that should be treated as a research URL, not a name. */\nexport function looksLikeUrl(value: string): boolean {\n return /^https?:\\/\\//i.test(value.trim());\n}\n\n/** List the blueprint kinds (subdirectories) available, sorted. */\nexport function listKinds(root: string): string[] {\n if (!isDir(root)) return [];\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n}\n\n/** List the blueprint names available under a kind, sorted (no `.md`). */\nexport function listBlueprintNames(root: string, kind: string): string[] {\n const dir = path.join(root, kind);\n if (!isDir(dir)) return [];\n return fs\n .readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => e.name.slice(0, -\".md\".length))\n .sort();\n}\n\n/** A flat catalog of every kind and its blueprint names. */\nexport function listCatalog(root: string): Array<{\n kind: string;\n names: string[];\n}> {\n return listKinds(root).map((kind) => ({\n kind,\n names: listBlueprintNames(root, kind),\n }));\n}\n\n/** Render the `--list` / no-args catalog text. */\nexport function formatCatalog(root: string): string {\n const catalog = listCatalog(root);\n const lines: string[] = [];\n lines.push(\"Available blueprints:\");\n lines.push(\"\");\n if (catalog.length === 0) {\n lines.push(\" (none found)\");\n } else {\n for (const { kind, names } of catalog) {\n const shown =\n names.length > 0 ? names.join(\", \") : \"(generic — pass a URL)\";\n lines.push(` ${kind.padEnd(10)} ${shown}`);\n }\n }\n lines.push(\"\");\n lines.push(\"Usage:\");\n lines.push(\n \" agent-native add <kind> <name> Print a curated blueprint\",\n );\n lines.push(\n \" agent-native add <kind> <https://docs…> Research-and-integrate from a URL\",\n );\n lines.push(\" agent-native add --list Show this list\");\n lines.push(\"\");\n lines.push(\"Pipe a blueprint into your coding agent to apply it:\");\n lines.push(\" agent-native add provider stripe | claude\");\n lines.push(\" agent-native add channel discord | codex\");\n return lines.join(\"\\n\");\n}\n\n/**\n * Build the generic \"research-and-integrate\" blueprint emitted when the user\n * passes a URL instead of a known blueprint name. Mirrors Flue: a URL is a\n * research seed. We keep this self-contained — the coding agent reading it has\n * no other context.\n */\nexport function buildGenericUrlBlueprint(kind: string, url: string): string {\n const kindGuidance = GENERIC_KIND_GUIDANCE[kind] ?? GENERIC_DEFAULT_GUIDANCE;\n return `# Blueprint: integrate a new ${kind} from a URL\n\nYou are a coding agent working inside an **agent-native** app (a repo built on\n\\`@agent-native/core\\`). Apply this blueprint as real source changes on the\ncurrent branch. Do not just describe the work — research, implement, then verify.\n\n## Research seed\n\nStart from this URL and follow it for the API/protocol/contract you need:\n\n ${url}\n\nFetch it (and the pages it links to) to learn the real endpoints, auth model,\npayload shapes, and any signature/verification requirements. Do not guess from\ntraining data — the docs are the source of truth, and package/API versions\ndrift. Confirm the current version before writing code.\n\n## What you're adding\n\nA new **${kind}** integration. ${kindGuidance}\n\n## How agent-native wants it built\n\n- **Actions are the single source of truth.** Add app operations in \\`actions/\\`\n with \\`defineAction\\` (Zod schema). The agent calls them as tools; the frontend\n calls the same action through \\`useActionQuery\\` / \\`useActionMutation\\`. Do NOT\n add \\`/api/*\\` or Nitro pass-through routes that just wrap an action.\n- **Prefer the provider-api substrate for external HTTP.** For ad-hoc reads\n against a third-party API, register the provider with\n \\`@agent-native/core/provider-api\\` and expose the\n \\`provider-api-catalog\\` / \\`provider-api-docs\\` / \\`provider-api-request\\` trio\n instead of one rigid action per endpoint. First-class actions are shortcuts,\n not capability limits.\n- **Read the relevant skill before editing** — \\`actions\\`,\n \\`integration-webhooks\\`, \\`secrets\\`, \\`onboarding\\`, \\`sharing\\`, \\`security\\` —\n for the area you're touching.\n- **Never hardcode secrets.** Read API keys / tokens / signing secrets from the\n secret store (\\`readAppSecret\\`, the provider credential adapter, OAuth token\n helpers) at call time. Use obviously-fake placeholders in any example\n (e.g. \\`sk_test_PLACEHOLDER_xxx\\`), never a real credential.\n- **Scope ownable data.** Tables with \\`ownableColumns()\\` require\n \\`accessFilter\\` / \\`resolveAccess\\` / \\`assertAccess\\`; fail closed.\n- **Changeset.** If you edit a publishable package's source\n (\\`packages/core\\`, \\`packages/dispatch\\`, \\`packages/scheduling\\`,\n \\`packages/pinpoint\\`), add a \\`.changeset/*.md\\`.\n\n## Verify\n\n1. \\`agent-native typecheck\\` (or \\`tsc --noEmit\\`) passes.\n2. Add a focused \\`*.spec.ts\\` for the new surface and run it.\n3. Exercise the real workflow end to end: invoke the new action from the agent\n chat (and the UI if applicable), and confirm the external call round-trips\n with credentials injected and secrets redacted.\n`;\n}\n\nconst GENERIC_DEFAULT_GUIDANCE =\n \"Identify whether it is best modeled as an action, a provider-api integration, \" +\n \"an inbound channel adapter, or a sandbox backend, and follow that area's skill.\";\n\nconst GENERIC_KIND_GUIDANCE: Record<string, string> = {\n provider:\n \"Wire it into the provider-api substrate (`@agent-native/core/provider-api`): \" +\n \"register the base URL, auth style, credential key, and docs URLs, then expose \" +\n \"the `provider-api-catalog` / `provider-api-docs` / `provider-api-request` trio \" +\n \"so any endpoint is reachable without one action per endpoint.\",\n channel:\n \"Implement a `PlatformAdapter` (see \" +\n \"`packages/core/src/integrations/types.ts` and the adapters under \" +\n \"`packages/core/src/integrations/adapters/`) and register it in \" +\n \"`getDefaultAdapters()` in `packages/core/src/integrations/plugin.ts`. \" +\n \"Enqueue to SQL and return 200 fast; run the agent loop in the separate \" +\n \"`_process-task` execution. Read the `integration-webhooks` skill.\",\n sandbox:\n \"Implement the `SandboxAdapter` seam in \" +\n \"`packages/core/src/coding-tools/sandbox/` (mirror \" +\n \"`local-child-process-adapter.ts`) and select it via `AGENT_NATIVE_SANDBOX` \" +\n \"or `registerSandboxAdapter()`. The adapter only runs the already-prepared, \" +\n \"non-secret module source — it never sees app secrets.\",\n action:\n \"Add a single multi-surface `defineAction` in `actions/` with a Zod schema. \" +\n \"Keep the surface small and orthogonal (one CRUD-style `update` over N \" +\n \"per-field actions). Read the `actions` skill.\",\n};\n\n/**\n * Resolve a blueprint for a `kind` + optional `name`/`url`.\n *\n * - A known name → the curated `blueprints/<kind>/<name>.md`.\n * - A URL → the generic research-and-integrate blueprint for the kind.\n * - Unknown name → throws `AddResolutionError` listing what's available.\n */\nexport function resolveBlueprint(opts: {\n kind: string;\n nameOrUrl?: string;\n root: string;\n}): ResolvedBlueprint {\n const { kind, nameOrUrl, root } = opts;\n const kinds = listKinds(root);\n\n if (!kinds.includes(kind)) {\n throw new AddResolutionError(\n `Unknown blueprint kind: ${kind}\\n\\n${formatCatalog(root)}`,\n );\n }\n\n // A URL seed → generic blueprint for the kind.\n if (nameOrUrl && looksLikeUrl(nameOrUrl)) {\n return {\n markdown: buildGenericUrlBlueprint(kind, nameOrUrl.trim()),\n source: {\n kind: \"generic-url\",\n blueprintKind: kind,\n url: nameOrUrl.trim(),\n },\n };\n }\n\n const names = listBlueprintNames(root, kind);\n\n // No name given: if exactly one curated blueprint exists, use it; otherwise\n // ask the user to pick one (or pass a URL for the generic path).\n if (!nameOrUrl) {\n if (names.length === 1) {\n return loadCurated(root, kind, names[0]);\n }\n throw new AddResolutionError(\n `Specify a ${kind} blueprint name or a URL.\\n` +\n ` Available ${kind} blueprints: ${\n names.length ? names.join(\", \") : \"(none — pass a URL)\"\n }\\n` +\n ` Or research from a URL: agent-native add ${kind} https://…`,\n );\n }\n\n if (!names.includes(nameOrUrl)) {\n throw new AddResolutionError(\n `Unknown ${kind} blueprint: ${nameOrUrl}\\n` +\n ` Available ${kind} blueprints: ${\n names.length ? names.join(\", \") : \"(none)\"\n }\\n` +\n ` Or research from a URL: agent-native add ${kind} https://…`,\n );\n }\n\n return loadCurated(root, kind, nameOrUrl);\n}\n\nfunction loadCurated(\n root: string,\n kind: string,\n name: string,\n): ResolvedBlueprint {\n const filePath = path.join(root, kind, `${name}.md`);\n const markdown = fs.readFileSync(filePath, \"utf-8\");\n return {\n markdown,\n source: { kind: \"curated\", blueprintKind: kind, name, path: filePath },\n };\n}\n\n/** Thrown for an unknown kind/name; the CLI prints the message and exits 1. */\nexport class AddResolutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AddResolutionError\";\n }\n}\n\ninterface ParsedAddArgs {\n list: boolean;\n help: boolean;\n /** Accepted as an explicit no-op alias for the default print behavior. */\n print: boolean;\n positionals: string[];\n}\n\nexport function parseAddArgs(argv: string[]): ParsedAddArgs {\n const out: ParsedAddArgs = {\n list: false,\n help: false,\n print: false,\n positionals: [],\n };\n for (const arg of argv) {\n if (arg === \"--\") continue;\n if (arg === \"--list\" || arg === \"-l\") out.list = true;\n else if (arg === \"--help\" || arg === \"-h\") out.help = true;\n else if (arg === \"--print\" || arg === \"-p\") out.print = true;\n else if (arg.startsWith(\"-\")) {\n // Ignore unknown flags rather than misparse them as a name.\n continue;\n } else out.positionals.push(arg);\n }\n return out;\n}\n\nconst HELP = `agent-native add — emit an integration blueprint for your coding agent.\n\nInstead of scaffolding files, \\`add\\` prints a curated Markdown recipe to stdout.\nPipe it into a coding agent (Claude Code, Codex, …) and it applies the changes\nagainst your live repo, the agent-native way.\n\nUsage:\n agent-native add <kind> <name> Print a curated blueprint\n agent-native add <kind> <https://docs…> Research-and-integrate from a URL\n agent-native add --list List available kinds and blueprints\n\nExamples:\n agent-native add provider stripe | claude\n agent-native add channel discord | codex\n agent-native add sandbox docker\n agent-native add action crud\n agent-native add provider https://docs.example.com/api | claude\n\nOptions:\n -l, --list List available blueprint kinds and names\n -p, --print Print the blueprint to stdout (the default; explicit no-op)\n -h, --help Show this help`;\n\n/**\n * CLI entry point. Returns the process exit code so the dispatcher / tests can\n * assert on it. Writes the blueprint Markdown to stdout and diagnostics to\n * stderr so `... | claude` only receives the blueprint.\n */\nexport function runAdd(\n argv: string[],\n io: {\n out?: (s: string) => void;\n err?: (s: string) => void;\n root?: string;\n } = {},\n): number {\n const out = io.out ?? ((s: string) => process.stdout.write(s));\n const err = io.err ?? ((s: string) => process.stderr.write(s));\n const root = resolveBlueprintsRoot(io.root);\n\n const parsed = parseAddArgs(argv);\n\n if (parsed.help) {\n out(HELP + \"\\n\");\n return 0;\n }\n\n // `--list` or no positionals → show the catalog (to stdout; this is the\n // requested output, not an error).\n if (parsed.list || parsed.positionals.length === 0) {\n out(formatCatalog(root) + \"\\n\");\n return 0;\n }\n\n const [kind, nameOrUrl] = parsed.positionals;\n\n try {\n const resolved = resolveBlueprint({ kind, nameOrUrl, root });\n out(\n resolved.markdown.endsWith(\"\\n\")\n ? resolved.markdown\n : resolved.markdown + \"\\n\",\n );\n return 0;\n } catch (e) {\n if (e instanceof AddResolutionError) {\n err(e.message + \"\\n\");\n return 1;\n }\n throw e;\n }\n}\n"]}
|
package/dist/cli/connect.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* browser device-code flow: open the verification URL, approve in the browser,
|
|
6
6
|
* and the minted HTTP MCP server entry is written idempotently.
|
|
7
7
|
*
|
|
8
|
-
* agent-native connect <url> [--client all|claude-code|
|
|
8
|
+
* agent-native connect <url> [--client all|claude-code|
|
|
9
9
|
* codex|cowork|cursor|opencode|github-copilot]
|
|
10
10
|
* [--scope user|project]
|
|
11
11
|
* [--name <serverName>]
|
|
@@ -35,7 +35,7 @@ export interface ParsedConnectArgs {
|
|
|
35
35
|
mode?: "dev" | "prod" | "reauth" | "reconnect";
|
|
36
36
|
/** Positional URL (the deployed app origin). Undefined for `--all`. */
|
|
37
37
|
url?: string;
|
|
38
|
-
/** all | claude-code |
|
|
38
|
+
/** all | claude-code | codex | cowork | cursor | opencode | github-copilot (default "all"). claude-code-cli is accepted as a legacy alias for claude-code. */
|
|
39
39
|
client: string;
|
|
40
40
|
/** True when the user passed --client explicitly, so we skip the picker. */
|
|
41
41
|
clientExplicit: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAQH,OAAO,EAEL,QAAQ,EAOT,MAAM,yBAAyB,CAAC;AA+EjC,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC/C,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/connect.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* browser device-code flow: open the verification URL, approve in the browser,
|
|
6
6
|
* and the minted HTTP MCP server entry is written idempotently.
|
|
7
7
|
*
|
|
8
|
-
* agent-native connect <url> [--client all|claude-code|
|
|
8
|
+
* agent-native connect <url> [--client all|claude-code|
|
|
9
9
|
* codex|cowork|cursor|opencode|github-copilot]
|
|
10
10
|
* [--scope user|project]
|
|
11
11
|
* [--name <serverName>]
|
|
@@ -185,11 +185,15 @@ export function normalizeUrl(raw) {
|
|
|
185
185
|
const base = `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "");
|
|
186
186
|
return base;
|
|
187
187
|
}
|
|
188
|
+
// Clients offered in the interactive picker and expanded by "all". Excludes
|
|
189
|
+
// the `claude-code-cli` alias so users only ever see a single "Claude Code"
|
|
190
|
+
// option (it still works if passed explicitly via --client).
|
|
191
|
+
const SELECTABLE_CLIENTS = CLIENTS.filter((c) => c !== "claude-code-cli");
|
|
188
192
|
/** Resolve the requested clients list. "all" → every supported client. */
|
|
189
193
|
export function resolveClients(client) {
|
|
190
194
|
const c = normalizeClientAlias(client ?? "all");
|
|
191
195
|
if (c === "all" || c === "")
|
|
192
|
-
return [...
|
|
196
|
+
return [...SELECTABLE_CLIENTS];
|
|
193
197
|
if (c.includes(",")) {
|
|
194
198
|
const clients = normalizeClientIds(c.split(",").map((part) => part.trim()));
|
|
195
199
|
if (clients.length > 0)
|
|
@@ -201,7 +205,12 @@ export function resolveClients(client) {
|
|
|
201
205
|
}
|
|
202
206
|
function normalizeClientAlias(value) {
|
|
203
207
|
const id = value.trim().toLowerCase();
|
|
204
|
-
|
|
208
|
+
// The Claude Code CLI and desktop share ~/.claude.json, so they are one
|
|
209
|
+
// client. `claude-code-cli` stays accepted for back-compat but collapses to
|
|
210
|
+
// the single "Claude Code" option everywhere it surfaces.
|
|
211
|
+
if (id === "claude" ||
|
|
212
|
+
id === "claude-code-desktop" ||
|
|
213
|
+
id === "claude-code-cli")
|
|
205
214
|
return "claude-code";
|
|
206
215
|
if (id === "copilot" || id === "vscode" || id === "vs-code") {
|
|
207
216
|
return "github-copilot";
|
|
@@ -252,7 +261,7 @@ export function writeConnectClientPreferences(clients, file = connectPreferences
|
|
|
252
261
|
}, null, 2) + "\n", "utf-8");
|
|
253
262
|
}
|
|
254
263
|
function clientPromptOptions() {
|
|
255
|
-
return
|
|
264
|
+
return SELECTABLE_CLIENTS.map((client) => ({
|
|
256
265
|
value: client,
|
|
257
266
|
label: CLIENT_LABELS[client],
|
|
258
267
|
hint: CLIENT_HINTS[client],
|
|
@@ -591,26 +600,43 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
|
|
|
591
600
|
const sleep = deps.sleep ?? realSleep;
|
|
592
601
|
const open = deps.openBrowser ?? openInBrowser;
|
|
593
602
|
const now = deps.now ?? (() => Date.now());
|
|
594
|
-
let start;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
603
|
+
let start = null;
|
|
604
|
+
// A cold/propagating Plan instance can briefly 404/5xx before its connect
|
|
605
|
+
// route is registered (async plugin init). Retry a few times so a recoverable
|
|
606
|
+
// blip doesn't kill the connect before polling even begins.
|
|
607
|
+
const START_ATTEMPTS = 4;
|
|
608
|
+
for (let attempt = 0; attempt < START_ATTEMPTS; attempt++) {
|
|
609
|
+
try {
|
|
610
|
+
const { status, json } = await postJson(fetchImpl, `${baseUrl}${DEVICE_START_PATH}`, {
|
|
611
|
+
client: clientArg,
|
|
612
|
+
app: appSlug,
|
|
613
|
+
...(options.fullCatalog ? { fullCatalog: true } : {}),
|
|
614
|
+
});
|
|
615
|
+
if (status >= 200 && status < 300 && json?.device_code) {
|
|
616
|
+
start = json;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
if ((status === 404 || status >= 500) && attempt < START_ATTEMPTS - 1) {
|
|
620
|
+
await sleep(1000 * (attempt + 1));
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
602
623
|
logErr(` Could not start the connect flow on ${baseUrl} ` +
|
|
603
624
|
`(HTTP ${status}). Is this an agent-native app, and is it ` +
|
|
604
625
|
`deployed with the connect endpoint enabled?`);
|
|
605
626
|
return null;
|
|
606
627
|
}
|
|
607
|
-
|
|
628
|
+
catch (err) {
|
|
629
|
+
if (attempt < START_ATTEMPTS - 1) {
|
|
630
|
+
await sleep(1000 * (attempt + 1));
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
logErr(` Could not reach ${baseUrl} (${err?.message ?? err}). ` +
|
|
634
|
+
`Check the URL and your network.`);
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
608
637
|
}
|
|
609
|
-
|
|
610
|
-
logErr(` Could not reach ${baseUrl} (${err?.message ?? err}). ` +
|
|
611
|
-
`Check the URL and your network.`);
|
|
638
|
+
if (!start)
|
|
612
639
|
return null;
|
|
613
|
-
}
|
|
614
640
|
const interval = Math.max(1, Number(start.interval) || 5);
|
|
615
641
|
const expiresIn = Math.max(interval, Number(start.expires_in) || 600);
|
|
616
642
|
const deadline = now() + expiresIn * 1000;
|
|
@@ -623,23 +649,59 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
|
|
|
623
649
|
logOut(" Approve in the browser to finish. Opening it now…");
|
|
624
650
|
open(start.verification_uri_complete);
|
|
625
651
|
let spin = 0;
|
|
652
|
+
let transientStreak = 0;
|
|
653
|
+
// Ride out brief cold-instance blips, but don't poll a persistently-dead
|
|
654
|
+
// endpoint forever: give up after this many consecutive transient (404/5xx
|
|
655
|
+
// or network-error) polls. Reset as soon as one poll responds normally.
|
|
656
|
+
const MAX_TRANSIENT_POLLS = 20;
|
|
626
657
|
const isTTY = !!process.stdout.isTTY;
|
|
627
658
|
while (now() < deadline) {
|
|
628
659
|
let poll;
|
|
660
|
+
let transient = false;
|
|
629
661
|
try {
|
|
630
662
|
const { status, json } = await postJson(fetchImpl, `${baseUrl}${DEVICE_POLL_PATH}`, { device_code: start.device_code });
|
|
631
663
|
if (status < 200 || status >= 300) {
|
|
664
|
+
if (isTerminalPollBody(json)) {
|
|
665
|
+
poll = json;
|
|
666
|
+
}
|
|
667
|
+
else if (status === 404 || status >= 500) {
|
|
668
|
+
// Transient: a cold/propagating Plan instance can briefly serve a
|
|
669
|
+
// bare 404 (the MCP route isn't registered until async plugin init
|
|
670
|
+
// settles) or a 5xx before it's healthy. The next poll usually lands
|
|
671
|
+
// on a warm instance, so keep polling until the deadline instead of
|
|
672
|
+
// hard-failing the whole connect on a recoverable blip. (This is the
|
|
673
|
+
// recurring "Cannot find any route matching [POST] .../mcp" case.)
|
|
674
|
+
poll = { status: "pending" };
|
|
675
|
+
transient = true;
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
if (isTTY)
|
|
679
|
+
process.stdout.write("\r\x1b[K");
|
|
680
|
+
logErr(` Connect polling failed (HTTP ${status}): ` +
|
|
681
|
+
responseMessage(json, "server returned an error."));
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
poll = (json ?? { status: "pending" });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
catch {
|
|
690
|
+
// Transient network error — keep polling.
|
|
691
|
+
poll = { status: "pending" };
|
|
692
|
+
transient = true;
|
|
693
|
+
}
|
|
694
|
+
if (transient) {
|
|
695
|
+
if (++transientStreak > MAX_TRANSIENT_POLLS) {
|
|
632
696
|
if (isTTY)
|
|
633
697
|
process.stdout.write("\r\x1b[K");
|
|
634
|
-
logErr(
|
|
635
|
-
|
|
698
|
+
logErr(" Connect endpoint is not responding (repeated 404/5xx). It may be " +
|
|
699
|
+
"mid-deploy — wait a minute and run the command again.");
|
|
636
700
|
return null;
|
|
637
701
|
}
|
|
638
|
-
poll = (json ?? { status: "pending" });
|
|
639
702
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
poll = { status: "pending" };
|
|
703
|
+
else {
|
|
704
|
+
transientStreak = 0;
|
|
643
705
|
}
|
|
644
706
|
if (poll.status === "approved") {
|
|
645
707
|
if (isTTY)
|
|
@@ -687,6 +749,12 @@ export async function runDeviceFlow(baseUrl, appSlug, clientArg, deps = {}, opti
|
|
|
687
749
|
logErr(" Timed out waiting for approval. Run the command again to retry.");
|
|
688
750
|
return null;
|
|
689
751
|
}
|
|
752
|
+
function isTerminalPollBody(json) {
|
|
753
|
+
return (json?.status === "not_found" ||
|
|
754
|
+
json?.status === "error" ||
|
|
755
|
+
json?.status === "expired" ||
|
|
756
|
+
json?.status === "consumed");
|
|
757
|
+
}
|
|
690
758
|
// ---------------------------------------------------------------------------
|
|
691
759
|
// Writing config(s)
|
|
692
760
|
// ---------------------------------------------------------------------------
|
|
@@ -1849,7 +1917,7 @@ Developer:
|
|
|
1849
1917
|
npx @agent-native/core@latest connect prod [--apps mail,calendar] [--client <c>]
|
|
1850
1918
|
Restore production MCP entries saved before the dev switch.
|
|
1851
1919
|
|
|
1852
|
-
Clients: all (default), claude-code,
|
|
1920
|
+
Clients: all (default), claude-code, codex, cowork, cursor, opencode, github-copilot
|
|
1853
1921
|
Scope: user (default, ~/.claude.json) or project (.mcp.json)`;
|
|
1854
1922
|
/**
|
|
1855
1923
|
* `agent-native connect` entry point. `deps` is injectable for tests; the
|