@inneranimalmedia/agentsam-sdk 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEVELOPMENT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/package.json +23 -4
- package/src/AgentSam.js +67 -0
- package/src/cli.js +257 -8
- package/src/index.js +10 -4
- package/src/lib/responses.js +21 -0
- package/src/lib/router.js +51 -0
- package/src/lib/scaffold.js +260 -0
- package/src/lib/sessions.js +53 -0
- package/src/lib/tools.js +38 -0
- package/src/schemas/config.js +2 -0
- package/test/smoke.mjs +38 -0
package/DEVELOPMENT.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Developing agentsam-sdk with Inner Animal Media
|
|
2
|
+
|
|
3
|
+
## Local smoke test
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd agentsam-sdk
|
|
7
|
+
npm test
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Link into a scaffolded project
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd agentsam-sdk && npm link
|
|
14
|
+
cd /path/to/your-project && npm link @inneranimalmedia/agentsam-sdk
|
|
15
|
+
npm run dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Link into inneranimalmedia (monorepo-style)
|
|
19
|
+
|
|
20
|
+
From the platform repo:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd inneranimalmedia
|
|
24
|
+
npm link ../agentsam-sdk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then in any Worker or script:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Non-interactive init (CI / smoke)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx agentsam init \
|
|
37
|
+
--name my-agent \
|
|
38
|
+
--lane fullstack \
|
|
39
|
+
--provider cloudflare \
|
|
40
|
+
--agent orchestrator \
|
|
41
|
+
--yes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Publish checklist
|
|
45
|
+
|
|
46
|
+
1. `npm test` passes
|
|
47
|
+
2. Bump version in `package.json`
|
|
48
|
+
3. `npm publish --access public`
|
|
49
|
+
4. Tag: `git tag v1.1.1 && git push origin v1.1.1`
|
|
50
|
+
|
|
51
|
+
## Known gaps (roadmap)
|
|
52
|
+
|
|
53
|
+
- `agentsam deploy`, `status`, `logs` — not implemented yet
|
|
54
|
+
- CMS lane in README — CLI has 4 lanes today (Full Stack, Data, CRM, Creative)
|
|
55
|
+
- Published API route may differ; stub mode works without `AGENTSAM_API_KEY`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Inner Animal Media
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Agent Sam SDK
|
|
2
|
+
|
|
3
|
+
> The AI agent layer for developers who need more than a chatbot.
|
|
4
|
+
|
|
5
|
+
Agent Sam is a full-stack autonomous agent SDK built on Cloudflare Workers, D1, Supabase, Durable Objects, and MCP — designed to converse, plan, and execute real work across your entire stack. CMS websites, full-stack applications, data pipelines, creative workflows, terminal execution, deployments, and multi-step agentic pipelines — all through one unified agent interface.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What is Agent Sam?
|
|
10
|
+
|
|
11
|
+
Agent Sam is a platform operator AI. It sits on top of your infrastructure and can:
|
|
12
|
+
|
|
13
|
+
- **Understand intent** — natural language in, real actions out
|
|
14
|
+
- **Execute across surfaces** — terminal, database, browser, CAD, deploy, MCP tools
|
|
15
|
+
- **Route intelligently** — local machine, cloud VM, or sandboxed environment based on context
|
|
16
|
+
- **Gate high-risk actions** — approval flows before anything destructive runs
|
|
17
|
+
- **Leave an audit trail** — every tool call, command run, and decision is logged
|
|
18
|
+
|
|
19
|
+
It is not a wrapper around a chat API. It is a command fabric with a conversation interface.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
\`\`\`bash
|
|
26
|
+
npx @inneranimalmedia/agentsam-sdk init
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Non-interactive (CI / scripts):
|
|
30
|
+
|
|
31
|
+
\`\`\`bash
|
|
32
|
+
npx @inneranimalmedia/agentsam-sdk init \\
|
|
33
|
+
--name my-agent \\
|
|
34
|
+
--lane fullstack \\
|
|
35
|
+
--provider cloudflare \\
|
|
36
|
+
--agent orchestrator \\
|
|
37
|
+
--yes
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
Scaffolded workers expose \`GET /health\` and \`POST /chat\` immediately (stub mode without an API key).
|
|
41
|
+
|
|
42
|
+
See [DEVELOPMENT.md](./DEVELOPMENT.md) for linking the SDK into Inner Animal Media locally.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
\`\`\`bash
|
|
49
|
+
npm install @inneranimalmedia/agentsam-sdk
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Lanes
|
|
55
|
+
|
|
56
|
+
| Lane | Best For |
|
|
57
|
+
|------|----------|
|
|
58
|
+
| **Full Stack** | End-to-end apps — agent chat, terminal, deploy, D1, R2, Durable Objects, KV |
|
|
59
|
+
| **CMS** | Content-managed websites — pages, assets, themes, navigation, live edit |
|
|
60
|
+
| **Data Solutions** | Database ops, migrations, queries, Supabase pgvector, Hyperdrive pipelines |
|
|
61
|
+
| **Customer Management** | CRM, contacts, billing, client workflows, multi-tenant isolation |
|
|
62
|
+
| **Creative & Design** | CAD, 3D, media generation, content pipelines |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Agents
|
|
67
|
+
|
|
68
|
+
| Agent | Role |
|
|
69
|
+
|-------|------|
|
|
70
|
+
| **Orchestrator** | General purpose — routes across all lanes and tools |
|
|
71
|
+
| **CMS Agent** | Pages, sections, assets, themes, publishing workflows |
|
|
72
|
+
| **Data Agent** | D1, Supabase, Hyperdrive, migrations, vector search |
|
|
73
|
+
| **CRM Agent** | Customer records, contacts, billing, client isolation |
|
|
74
|
+
| **Creative Agent** | Design commands, 3D generation, CAD, media pipelines |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## How It Works
|
|
79
|
+
|
|
80
|
+
\`\`\`
|
|
81
|
+
User Intent (chat or CLI)
|
|
82
|
+
↓
|
|
83
|
+
Agent Sam — intent classification + command match
|
|
84
|
+
↓
|
|
85
|
+
Tool Catalog (D1) — policy check + approval gate
|
|
86
|
+
↓
|
|
87
|
+
Execution — terminal / D1 / Supabase / R2 / KV / DO / browser / deploy / MCP
|
|
88
|
+
↓
|
|
89
|
+
Telemetry — every action logged, measured, improvable
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
Capabilities are data-driven — new tools are added via D1, not Worker redeployments. The same tool catalog powers the dashboard, the CLI, and any MCP-connected client like Cursor or Claude Desktop.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Infrastructure
|
|
97
|
+
|
|
98
|
+
Agent Sam scaffolds and operates across the full Cloudflare + Supabase stack:
|
|
99
|
+
|
|
100
|
+
| Layer | Technology | Role |
|
|
101
|
+
|-------|------------|------|
|
|
102
|
+
| **Compute** | Cloudflare Workers | Edge runtime, API, agent dispatch |
|
|
103
|
+
| **Relational DB** | D1 (SQLite) | Tool catalog, sessions, telemetry, CMS, auth |
|
|
104
|
+
| **Vector DB** | Supabase pgvector via Hyperdrive | RAG, semantic search, agent memory |
|
|
105
|
+
| **Object Storage** | R2 | Assets, media, bundles, CMS content |
|
|
106
|
+
| **Key-Value** | Workers KV | Cache, CMS drafts, feature flags |
|
|
107
|
+
| **Stateful Sessions** | Durable Objects | Terminal sessions, collab, real-time state |
|
|
108
|
+
| **Terminal** | ExecOS over cloudflared tunnel | Shell execution, deploy, git, wrangler |
|
|
109
|
+
| **AI Router** | Anthropic + OpenAI | Adaptive Thompson sampling across models |
|
|
110
|
+
| **Protocol** | MCP (Model Context Protocol) | External agent surface for Cursor, Claude, etc. |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Execution Lanes
|
|
115
|
+
|
|
116
|
+
Agent Sam routes work to the right environment automatically:
|
|
117
|
+
|
|
118
|
+
| Lane | Environment | When |
|
|
119
|
+
|------|-------------|------|
|
|
120
|
+
| Local | Your machine | Fastest dev loop |
|
|
121
|
+
| Cloud | Always-on VM via tunnel | Machine asleep or offsite |
|
|
122
|
+
| Sandbox | Isolated workspace | Safe experiments, tenant isolation |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## What Gets Scaffolded
|
|
127
|
+
|
|
128
|
+
Running \`agentsam init\` generates a production-ready project for your lane:
|
|
129
|
+
|
|
130
|
+
**All lanes include:**
|
|
131
|
+
- \`agentsam.config.js\` — project config, lane, provider, agent
|
|
132
|
+
- \`wrangler.toml\` — Worker, D1, R2, KV, Durable Object bindings
|
|
133
|
+
- \`.env.example\` — all required secrets pre-listed
|
|
134
|
+
- \`src/index.js\` — Worker entry point wired to your agent
|
|
135
|
+
- \`README.md\` — setup and deploy instructions
|
|
136
|
+
|
|
137
|
+
**CMS lane adds:**
|
|
138
|
+
- Page, section, asset, and theme schema migrations
|
|
139
|
+
- CMS worker with live edit, draft/publish, and R2 asset pipeline
|
|
140
|
+
|
|
141
|
+
**Data lane adds:**
|
|
142
|
+
- D1 + Supabase Hyperdrive connection config
|
|
143
|
+
- Vector embedding pipeline scaffold
|
|
144
|
+
- Migration templates for core data models
|
|
145
|
+
|
|
146
|
+
**Full Stack adds:**
|
|
147
|
+
- Durable Object session scaffold
|
|
148
|
+
- Auth tables and session management
|
|
149
|
+
- Full agent chat + tool loop worker
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Multi-Tenant & Client Policy
|
|
154
|
+
|
|
155
|
+
Agent Sam is built for isolation from the ground up:
|
|
156
|
+
|
|
157
|
+
- Each user or client gets a scoped workspace
|
|
158
|
+
- Terminal execution is path-isolated — no cross-tenant access
|
|
159
|
+
- AI usage is policy-gated — BYOK, managed, or disabled per client
|
|
160
|
+
- D1 and R2 are scoped per tenant at the binding level
|
|
161
|
+
- Every action produces an audit trail
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Roadmap
|
|
166
|
+
|
|
167
|
+
- [ ] \`agentsam deploy\` — push your project from CLI
|
|
168
|
+
- [ ] \`agentsam status\` — live agent and infrastructure health
|
|
169
|
+
- [ ] \`agentsam logs\` — tail tool call and command logs
|
|
170
|
+
- [ ] \`agentsam kit\` — install pre-built capability kits
|
|
171
|
+
- [ ] Kit marketplace — CMS, ecommerce, nonprofit, SaaS starters
|
|
172
|
+
- [ ] BYOK AI key support per project
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Built By
|
|
177
|
+
|
|
178
|
+
[Inner Animal Media](https://inneranimalmedia.com) — Agent Sam is the operator brain behind the Inner Animal Media platform. The SDK is how we share that infrastructure with other developers.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inneranimalmedia/agentsam-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Agent Sam is a full-stack AI agent SDK for autonomous task execution — covering data management, creative workflows, design commands, and multi-step agentic pipelines.",
|
|
5
|
-
"main": "src/index.js",
|
|
6
5
|
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
7
11
|
"bin": {
|
|
8
|
-
"agentsam": "src/cli.js"
|
|
12
|
+
"agentsam": "./src/cli.js"
|
|
9
13
|
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"test",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"DEVELOPMENT.md"
|
|
20
|
+
],
|
|
10
21
|
"scripts": {
|
|
11
|
-
"test": "node
|
|
22
|
+
"test": "node test/smoke.mjs",
|
|
23
|
+
"smoke": "node test/smoke.mjs",
|
|
24
|
+
"prepublishOnly": "npm test"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
12
31
|
},
|
|
13
32
|
"repository": {
|
|
14
33
|
"type": "git",
|
package/src/AgentSam.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { json, notFound, readJson } from './lib/responses.js';
|
|
2
|
+
import { createSession, getSession } from './lib/sessions.js';
|
|
3
|
+
import { routeIntent } from './lib/router.js';
|
|
4
|
+
import { getToolCatalog } from './lib/tools.js';
|
|
5
|
+
|
|
6
|
+
export class AgentSam {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.env = options.env ?? {};
|
|
9
|
+
this.agent = options.agent ?? 'orchestrator';
|
|
10
|
+
this.lane = options.lane ?? 'fullstack';
|
|
11
|
+
this.project = options.project ?? 'agentsam-project';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async handle(request) {
|
|
15
|
+
const url = new URL(request.url);
|
|
16
|
+
const path = url.pathname.replace(/\/$/, '') || '/';
|
|
17
|
+
|
|
18
|
+
if (request.method === 'GET' && path === '/api/health') {
|
|
19
|
+
return json({ ok: true, service: 'AgentSam', agent: this.agent, lane: this.lane, status: 'online' });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (request.method === 'GET' && path === '/api/agentsam/info') {
|
|
23
|
+
return json({
|
|
24
|
+
ok: true,
|
|
25
|
+
name: 'AgentSam SDK',
|
|
26
|
+
project: this.project,
|
|
27
|
+
agent: this.agent,
|
|
28
|
+
lane: this.lane,
|
|
29
|
+
capabilities: getToolCatalog(this.lane).map((tool) => tool.name),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (request.method === 'POST' && path === '/api/agentsam/session') {
|
|
34
|
+
const body = await readJson(request);
|
|
35
|
+
const session = await createSession({ env: this.env, agent: this.agent, lane: this.lane, goal: body.goal });
|
|
36
|
+
return json({ ok: true, session });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (request.method === 'GET' && path.startsWith('/api/agentsam/session/')) {
|
|
40
|
+
const sessionId = path.split('/').pop();
|
|
41
|
+
const session = await getSession({ env: this.env, sessionId });
|
|
42
|
+
if (!session) return json({ ok: false, error: 'session_not_found' }, 404);
|
|
43
|
+
return json({ ok: true, session });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (request.method === 'POST' && path === '/api/agentsam/message') {
|
|
47
|
+
const body = await readJson(request);
|
|
48
|
+
const result = routeIntent({
|
|
49
|
+
message: body.message ?? body.goal ?? '',
|
|
50
|
+
agent: body.agent ?? this.agent,
|
|
51
|
+
lane: body.lane ?? this.lane,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return json({
|
|
55
|
+
ok: true,
|
|
56
|
+
session_id: body.session_id ?? null,
|
|
57
|
+
agent: result.agent,
|
|
58
|
+
lane: result.lane,
|
|
59
|
+
intent: result.intent,
|
|
60
|
+
next_steps: result.next_steps,
|
|
61
|
+
requires_approval: result.requires_approval,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return notFound();
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -1,16 +1,265 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import readline from 'readline';
|
|
4
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
5
|
+
import { scaffoldProject } from './lib/scaffold.js';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const VERSION = pkg.version;
|
|
8
|
+
|
|
9
|
+
function createPrompt() {
|
|
10
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
+
return {
|
|
12
|
+
ask: (q) => new Promise((resolve) => rl.question(q, resolve)),
|
|
13
|
+
close: () => rl.close(),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const LANES = {
|
|
18
|
+
'1': 'Full Stack',
|
|
19
|
+
'2': 'CMS',
|
|
20
|
+
'3': 'Data Solutions',
|
|
21
|
+
'4': 'Customer Management',
|
|
22
|
+
'5': 'Creative & Design',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const LANE_BY_SLUG = {
|
|
26
|
+
fullstack: 'Full Stack',
|
|
27
|
+
cms: 'CMS',
|
|
28
|
+
data: 'Data Solutions',
|
|
29
|
+
crm: 'Customer Management',
|
|
30
|
+
creative: 'Creative & Design',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const PROVIDERS = {
|
|
34
|
+
'1': 'Cloudflare Workers',
|
|
35
|
+
'2': 'GitHub + Cloudflare',
|
|
36
|
+
'3': 'Local / Self-hosted',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const PROVIDER_BY_SLUG = {
|
|
40
|
+
cloudflare: 'Cloudflare Workers',
|
|
41
|
+
github: 'GitHub + Cloudflare',
|
|
42
|
+
local: 'Local / Self-hosted',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const AGENTS = {
|
|
46
|
+
'1': 'orchestrator',
|
|
47
|
+
'2': 'cms',
|
|
48
|
+
'3': 'data',
|
|
49
|
+
'4': 'crm',
|
|
50
|
+
'5': 'creative',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function laneKeyFromLabel(label) {
|
|
54
|
+
const entry = Object.entries(LANES).find(([, v]) => v === label);
|
|
55
|
+
return entry?.[0] ?? '1';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printHelp() {
|
|
8
59
|
console.log(`
|
|
9
|
-
Agent Sam SDK — CLI
|
|
60
|
+
Agent Sam SDK — CLI v${VERSION}
|
|
10
61
|
|
|
11
|
-
Usage:
|
|
12
|
-
|
|
62
|
+
Usage:
|
|
63
|
+
agentsam init [options] Scaffold a new Agent Sam project
|
|
64
|
+
agentsam --version Print version
|
|
65
|
+
agentsam --help Show this help
|
|
13
66
|
|
|
14
|
-
|
|
67
|
+
Init options (non-interactive):
|
|
68
|
+
--name <name> Project directory name
|
|
69
|
+
--lane <fullstack|cms|data|crm|creative>
|
|
70
|
+
--provider <cloudflare|github|local>
|
|
71
|
+
--agent <orchestrator|cms|data|crm|creative>
|
|
72
|
+
--cf-account <id> Cloudflare account ID
|
|
73
|
+
--yes Skip confirmation prompt
|
|
15
74
|
`);
|
|
16
75
|
}
|
|
76
|
+
|
|
77
|
+
function parseInitArgs(argv) {
|
|
78
|
+
const opts = {
|
|
79
|
+
projectName: '',
|
|
80
|
+
lane: '',
|
|
81
|
+
provider: '',
|
|
82
|
+
agent: '',
|
|
83
|
+
cfAccountId: '',
|
|
84
|
+
yes: false,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
88
|
+
const arg = argv[i];
|
|
89
|
+
if (arg === '--yes' || arg === '-y') opts.yes = true;
|
|
90
|
+
else if (arg === '--name') opts.projectName = argv[++i] || '';
|
|
91
|
+
else if (arg === '--lane') {
|
|
92
|
+
const slug = argv[++i] || '';
|
|
93
|
+
opts.lane = LANE_BY_SLUG[slug] || slug;
|
|
94
|
+
} else if (arg === '--provider') {
|
|
95
|
+
const slug = argv[++i] || '';
|
|
96
|
+
opts.provider = PROVIDER_BY_SLUG[slug] || slug;
|
|
97
|
+
} else if (arg === '--agent') opts.agent = argv[++i] || '';
|
|
98
|
+
else if (arg === '--cf-account') opts.cfAccountId = argv[++i] || '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return opts;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function runInit(config) {
|
|
105
|
+
const {
|
|
106
|
+
projectName,
|
|
107
|
+
lane,
|
|
108
|
+
provider,
|
|
109
|
+
agent,
|
|
110
|
+
cfAccountId = '',
|
|
111
|
+
yes = false,
|
|
112
|
+
prompt = null,
|
|
113
|
+
} = config;
|
|
114
|
+
|
|
115
|
+
const safeDir = projectName.trim();
|
|
116
|
+
console.log(`
|
|
117
|
+
┌─────────────────────────────────────┐
|
|
118
|
+
│ Agent Sam Project Config │
|
|
119
|
+
├─────────────────────────────────────┤
|
|
120
|
+
│ Project: ${safeDir.padEnd(25)}│
|
|
121
|
+
│ Lane: ${lane.padEnd(25)}│
|
|
122
|
+
│ Provider: ${provider.padEnd(25)}│
|
|
123
|
+
│ Agent: ${agent.padEnd(25)}│
|
|
124
|
+
│ CF Acct: ${(cfAccountId || 'not set').padEnd(25)}│
|
|
125
|
+
└─────────────────────────────────────┘
|
|
126
|
+
`);
|
|
127
|
+
|
|
128
|
+
let confirm = 'y';
|
|
129
|
+
if (!yes && prompt) {
|
|
130
|
+
confirm = await prompt.ask(' Scaffold project? (y/n): ');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (confirm.toLowerCase() === 'y') {
|
|
134
|
+
try {
|
|
135
|
+
const dir = scaffoldProject({
|
|
136
|
+
projectName: safeDir,
|
|
137
|
+
lane,
|
|
138
|
+
provider,
|
|
139
|
+
agent,
|
|
140
|
+
cfAccountId,
|
|
141
|
+
sdkVersion: VERSION,
|
|
142
|
+
});
|
|
143
|
+
console.log(`
|
|
144
|
+
✓ Project created at ${dir}
|
|
145
|
+
|
|
146
|
+
Next steps:
|
|
147
|
+
cd ${dir.split('/').pop()}
|
|
148
|
+
cp .env.example .env
|
|
149
|
+
npm install
|
|
150
|
+
npm run smoke
|
|
151
|
+
npm run dev
|
|
152
|
+
`);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(`\n ✗ ${error.message}\n`);
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
console.log('\n Cancelled.\n');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function initInteractive(partial = {}) {
|
|
163
|
+
const prompt = createPrompt();
|
|
164
|
+
|
|
165
|
+
console.log(`
|
|
166
|
+
╔═══════════════════════════════════╗
|
|
167
|
+
║ Agent Sam SDK — Init ║
|
|
168
|
+
╚═══════════════════════════════════╝
|
|
169
|
+
`);
|
|
170
|
+
|
|
171
|
+
const projectName = partial.projectName || (await prompt.ask(' Project name: '));
|
|
172
|
+
|
|
173
|
+
if (!partial.lane) {
|
|
174
|
+
console.log(`
|
|
175
|
+
Select a lane:
|
|
176
|
+
1) Full Stack
|
|
177
|
+
2) CMS
|
|
178
|
+
3) Data Solutions
|
|
179
|
+
4) Customer Management
|
|
180
|
+
5) Creative & Design
|
|
181
|
+
`);
|
|
182
|
+
}
|
|
183
|
+
const laneKey = partial.lane ? laneKeyFromLabel(partial.lane) : await prompt.ask(' Lane [1-5]: ');
|
|
184
|
+
const lane = partial.lane || LANES[laneKey] || 'Full Stack';
|
|
185
|
+
|
|
186
|
+
console.log(`
|
|
187
|
+
Select a provider:
|
|
188
|
+
1) Cloudflare Workers
|
|
189
|
+
2) GitHub + Cloudflare
|
|
190
|
+
3) Local / Self-hosted
|
|
191
|
+
`);
|
|
192
|
+
const providerKey = partial.provider
|
|
193
|
+
? Object.entries(PROVIDERS).find(([, v]) => v === partial.provider)?.[0] || '1'
|
|
194
|
+
: await prompt.ask(' Provider [1-3]: ');
|
|
195
|
+
const provider = partial.provider || PROVIDERS[providerKey] || 'Cloudflare Workers';
|
|
196
|
+
|
|
197
|
+
if (!partial.agent) {
|
|
198
|
+
console.log(`
|
|
199
|
+
Select your default Agent Sam:
|
|
200
|
+
1) Orchestrator — general purpose, routes to all lanes
|
|
201
|
+
2) CMS Agent — pages, sections, assets, publishing workflows
|
|
202
|
+
3) Data Agent — database ops, migrations, queries
|
|
203
|
+
4) CRM Agent — customer management, contacts, billing
|
|
204
|
+
5) Creative Agent — design, 3D, media, content
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
const agentKey = partial.agent
|
|
208
|
+
? Object.entries(AGENTS).find(([, v]) => v === partial.agent)?.[0] || '1'
|
|
209
|
+
: await prompt.ask(' Agent [1-5]: ');
|
|
210
|
+
const agent = partial.agent || AGENTS[agentKey] || 'orchestrator';
|
|
211
|
+
|
|
212
|
+
const cfAccountId =
|
|
213
|
+
partial.cfAccountId !== undefined && partial.cfAccountId !== ''
|
|
214
|
+
? partial.cfAccountId
|
|
215
|
+
: await prompt.ask(' Cloudflare Account ID (enter to skip): ');
|
|
216
|
+
|
|
217
|
+
await runInit({
|
|
218
|
+
projectName,
|
|
219
|
+
lane,
|
|
220
|
+
provider,
|
|
221
|
+
agent,
|
|
222
|
+
cfAccountId,
|
|
223
|
+
yes: partial.yes,
|
|
224
|
+
prompt,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
prompt.close();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function initFromArgs(argv) {
|
|
231
|
+
const opts = parseInitArgs(argv);
|
|
232
|
+
if (!opts.projectName) {
|
|
233
|
+
console.error('\n ✗ --name is required for non-interactive init.\n');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
await runInit({
|
|
237
|
+
projectName: opts.projectName,
|
|
238
|
+
lane: opts.lane || 'Full Stack',
|
|
239
|
+
provider: opts.provider || 'Cloudflare Workers',
|
|
240
|
+
agent: opts.agent || 'orchestrator',
|
|
241
|
+
cfAccountId: opts.cfAccountId,
|
|
242
|
+
yes: true,
|
|
243
|
+
prompt: null,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const command = process.argv[2];
|
|
248
|
+
const rest = process.argv.slice(3);
|
|
249
|
+
|
|
250
|
+
if (command === '--version' || command === '-v') {
|
|
251
|
+
console.log(VERSION);
|
|
252
|
+
} else if (command === '--help' || command === '-h' || !command) {
|
|
253
|
+
printHelp();
|
|
254
|
+
} else if (command === 'init') {
|
|
255
|
+
const hasFlags = rest.some((a) => a.startsWith('--'));
|
|
256
|
+
if (hasFlags) {
|
|
257
|
+
await initFromArgs(rest);
|
|
258
|
+
} else {
|
|
259
|
+
await initInteractive({});
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
console.error(`\n Unknown command: ${command}\n`);
|
|
263
|
+
printHelp();
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
// @inneranimalmedia/agentsam-sdk
|
|
2
|
-
// Agent Sam SDK — entry point
|
|
1
|
+
// @inneranimalmedia/agentsam-sdk — public API
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
4
|
+
|
|
5
|
+
export { AgentSam } from './AgentSam.js';
|
|
6
|
+
export { routeIntent } from './lib/router.js';
|
|
7
|
+
export { getToolCatalog } from './lib/tools.js';
|
|
8
|
+
export { scaffoldProject } from './lib/scaffold.js';
|
|
9
|
+
|
|
10
|
+
export const version = pkg.version;
|
|
11
|
+
export const name = pkg.name;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function json(data, status = 200, headers = {}) {
|
|
2
|
+
return new Response(JSON.stringify(data, null, 2), {
|
|
3
|
+
status,
|
|
4
|
+
headers: {
|
|
5
|
+
'content-type': 'application/json; charset=utf-8',
|
|
6
|
+
...headers,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function notFound() {
|
|
12
|
+
return json({ ok: false, error: 'not_found' }, 404);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function readJson(request) {
|
|
16
|
+
try {
|
|
17
|
+
return await request.json();
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function routeIntent({ message = '', agent = 'orchestrator', lane = 'fullstack' }) {
|
|
2
|
+
const text = String(message).toLowerCase();
|
|
3
|
+
|
|
4
|
+
if (/delete|drop table|destroy|wipe|purge|production/.test(text)) {
|
|
5
|
+
return {
|
|
6
|
+
agent,
|
|
7
|
+
lane,
|
|
8
|
+
intent: 'high_risk_action',
|
|
9
|
+
requires_approval: true,
|
|
10
|
+
next_steps: ['Summarize intended change', 'Request explicit approval', 'Run only after approval'],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (/page|cms|publish|section|hero|content/.test(text)) {
|
|
15
|
+
return {
|
|
16
|
+
agent: 'cms',
|
|
17
|
+
lane: 'cms',
|
|
18
|
+
intent: 'cms_build',
|
|
19
|
+
requires_approval: false,
|
|
20
|
+
next_steps: ['Create page outline', 'Attach visual assets', 'Prepare draft for preview'],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (/database|sql|query|migration|schema|supabase|d1/.test(text)) {
|
|
25
|
+
return {
|
|
26
|
+
agent: 'data',
|
|
27
|
+
lane: 'data',
|
|
28
|
+
intent: 'data_task',
|
|
29
|
+
requires_approval: false,
|
|
30
|
+
next_steps: ['Inspect schema', 'Draft safe query or migration', 'Return reviewable plan'],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (/design|3d|cad|media|video|image|creative/.test(text)) {
|
|
35
|
+
return {
|
|
36
|
+
agent: 'creative',
|
|
37
|
+
lane: 'creative',
|
|
38
|
+
intent: 'creative_task',
|
|
39
|
+
requires_approval: false,
|
|
40
|
+
next_steps: ['Create production brief', 'Identify assets needed', 'Prepare generation or build steps'],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
agent,
|
|
46
|
+
lane,
|
|
47
|
+
intent: 'general_build',
|
|
48
|
+
requires_approval: false,
|
|
49
|
+
next_steps: ['Understand goal', 'Plan the work', 'Route to the right specialist'],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const LANE_KEYS = {
|
|
5
|
+
'Full Stack': 'fullstack',
|
|
6
|
+
CMS: 'cms',
|
|
7
|
+
'Data Solutions': 'data',
|
|
8
|
+
'Customer Management': 'crm',
|
|
9
|
+
'Creative & Design': 'creative',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const AGENT_FOR_LANE = {
|
|
13
|
+
fullstack: 'orchestrator',
|
|
14
|
+
cms: 'cms',
|
|
15
|
+
data: 'data',
|
|
16
|
+
crm: 'crm',
|
|
17
|
+
creative: 'creative',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function write(file, content) {
|
|
21
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
22
|
+
fs.writeFileSync(file, content.trimStart());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function slugify(value) {
|
|
26
|
+
return String(value || 'agentsam-project')
|
|
27
|
+
.trim()
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
30
|
+
.replace(/^-+|-+$/g, '') || 'agentsam-project';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function workerTemplate({ projectName, laneKey, agent }) {
|
|
34
|
+
return `
|
|
35
|
+
import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
async fetch(request, env, ctx) {
|
|
39
|
+
const agent = new AgentSam({
|
|
40
|
+
env,
|
|
41
|
+
ctx,
|
|
42
|
+
project: '${projectName}',
|
|
43
|
+
lane: '${laneKey}',
|
|
44
|
+
agent: '${agent}',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return agent.handle(request);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function migrationTemplate({ projectName, laneKey }) {
|
|
54
|
+
return `
|
|
55
|
+
-- AgentSam core schema for ${projectName}
|
|
56
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
57
|
+
id TEXT PRIMARY KEY,
|
|
58
|
+
agent TEXT NOT NULL,
|
|
59
|
+
lane TEXT NOT NULL,
|
|
60
|
+
goal TEXT,
|
|
61
|
+
status TEXT NOT NULL DEFAULT 'created',
|
|
62
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
63
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
CREATE TABLE IF NOT EXISTS agent_messages (
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
session_id TEXT NOT NULL,
|
|
69
|
+
role TEXT NOT NULL,
|
|
70
|
+
content TEXT NOT NULL,
|
|
71
|
+
metadata_json TEXT,
|
|
72
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
73
|
+
FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS agent_tool_calls (
|
|
77
|
+
id TEXT PRIMARY KEY,
|
|
78
|
+
session_id TEXT NOT NULL,
|
|
79
|
+
tool_name TEXT NOT NULL,
|
|
80
|
+
status TEXT NOT NULL DEFAULT 'queued',
|
|
81
|
+
input_json TEXT,
|
|
82
|
+
output_json TEXT,
|
|
83
|
+
requires_approval INTEGER NOT NULL DEFAULT 0,
|
|
84
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
85
|
+
completed_at TEXT,
|
|
86
|
+
FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
${laneKey === 'cms' ? `
|
|
90
|
+
CREATE TABLE IF NOT EXISTS cms_pages (
|
|
91
|
+
id TEXT PRIMARY KEY,
|
|
92
|
+
slug TEXT NOT NULL UNIQUE,
|
|
93
|
+
title TEXT NOT NULL,
|
|
94
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
95
|
+
hero_asset_key TEXT,
|
|
96
|
+
content_json TEXT NOT NULL DEFAULT '{}',
|
|
97
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
98
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CREATE TABLE IF NOT EXISTS cms_assets (
|
|
102
|
+
id TEXT PRIMARY KEY,
|
|
103
|
+
r2_key TEXT NOT NULL UNIQUE,
|
|
104
|
+
title TEXT,
|
|
105
|
+
alt_text TEXT,
|
|
106
|
+
metadata_json TEXT,
|
|
107
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
108
|
+
);
|
|
109
|
+
` : ''}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readmeTemplate({ projectName, lane, provider, agent }) {
|
|
114
|
+
return `
|
|
115
|
+
# ${projectName}
|
|
116
|
+
|
|
117
|
+
Scaffolded with [@inneranimalmedia/agentsam-sdk](https://www.npmjs.com/package/@inneranimalmedia/agentsam-sdk).
|
|
118
|
+
|
|
119
|
+
## Lane
|
|
120
|
+
|
|
121
|
+
**${lane}** — ${agent} agent, ${provider}
|
|
122
|
+
|
|
123
|
+
## Setup
|
|
124
|
+
|
|
125
|
+
\`\`\`bash
|
|
126
|
+
cp .env.example .env
|
|
127
|
+
npm install
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
## Dev
|
|
131
|
+
|
|
132
|
+
\`\`\`bash
|
|
133
|
+
npm run dev
|
|
134
|
+
npm run smoke
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
## Deploy
|
|
138
|
+
|
|
139
|
+
\`\`\`bash
|
|
140
|
+
npm run deploy
|
|
141
|
+
\`\`\`
|
|
142
|
+
|
|
143
|
+
## Endpoints
|
|
144
|
+
|
|
145
|
+
- \`GET /api/health\`
|
|
146
|
+
- \`GET /api/agentsam/info\`
|
|
147
|
+
- \`POST /api/agentsam/session\`
|
|
148
|
+
- \`POST /api/agentsam/message\`
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function scaffoldProject({
|
|
153
|
+
projectName,
|
|
154
|
+
lane,
|
|
155
|
+
provider,
|
|
156
|
+
agent,
|
|
157
|
+
cfAccountId,
|
|
158
|
+
sdkVersion = '1.1.1',
|
|
159
|
+
}) {
|
|
160
|
+
const safeName = slugify(projectName);
|
|
161
|
+
const dir = path.resolve(process.cwd(), safeName);
|
|
162
|
+
const laneKey = LANE_KEYS[lane] ?? 'fullstack';
|
|
163
|
+
const selectedAgent = agent || AGENT_FOR_LANE[laneKey] || 'orchestrator';
|
|
164
|
+
const sdkRange = `^${sdkVersion.split('.').slice(0, 2).join('.')}.0`;
|
|
165
|
+
|
|
166
|
+
if (fs.existsSync(dir)) {
|
|
167
|
+
throw new Error(`Directory "${safeName}" already exists.`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
171
|
+
fs.mkdirSync(`${dir}/src`, { recursive: true });
|
|
172
|
+
fs.mkdirSync(`${dir}/migrations`, { recursive: true });
|
|
173
|
+
|
|
174
|
+
write(`${dir}/agentsam.config.js`, `
|
|
175
|
+
export default {
|
|
176
|
+
project: '${safeName}',
|
|
177
|
+
lane: '${laneKey}',
|
|
178
|
+
provider: '${provider}',
|
|
179
|
+
agent: '${selectedAgent}',
|
|
180
|
+
cloudflare: {
|
|
181
|
+
accountId: '${cfAccountId || ''}',
|
|
182
|
+
},
|
|
183
|
+
api: {
|
|
184
|
+
baseUrl: '/api/agentsam',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
`);
|
|
188
|
+
|
|
189
|
+
write(`${dir}/.env.example`, `
|
|
190
|
+
AGENTSAM_API_KEY=
|
|
191
|
+
CLOUDFLARE_ACCOUNT_ID=${cfAccountId || ''}
|
|
192
|
+
CLOUDFLARE_API_TOKEN=
|
|
193
|
+
`);
|
|
194
|
+
|
|
195
|
+
write(`${dir}/.gitignore`, `
|
|
196
|
+
node_modules/
|
|
197
|
+
.env
|
|
198
|
+
.dev.vars
|
|
199
|
+
dist/
|
|
200
|
+
.wrangler/
|
|
201
|
+
`);
|
|
202
|
+
|
|
203
|
+
write(`${dir}/package.json`, `${JSON.stringify({
|
|
204
|
+
name: safeName,
|
|
205
|
+
version: '0.1.0',
|
|
206
|
+
type: 'module',
|
|
207
|
+
private: true,
|
|
208
|
+
scripts: {
|
|
209
|
+
dev: 'wrangler dev',
|
|
210
|
+
deploy: 'wrangler deploy',
|
|
211
|
+
smoke: 'node ./scripts/smoke.mjs',
|
|
212
|
+
},
|
|
213
|
+
dependencies: {
|
|
214
|
+
'@inneranimalmedia/agentsam-sdk': sdkRange,
|
|
215
|
+
},
|
|
216
|
+
devDependencies: {
|
|
217
|
+
wrangler: '^4.0.0',
|
|
218
|
+
},
|
|
219
|
+
}, null, 2)}\n`);
|
|
220
|
+
|
|
221
|
+
write(`${dir}/wrangler.toml`, `
|
|
222
|
+
name = "${safeName}"
|
|
223
|
+
main = "src/index.js"
|
|
224
|
+
compatibility_date = "2026-06-27"
|
|
225
|
+
|
|
226
|
+
[[d1_databases]]
|
|
227
|
+
binding = "DB"
|
|
228
|
+
database_name = "${safeName}-db"
|
|
229
|
+
database_id = "REPLACE_WITH_D1_DATABASE_ID"
|
|
230
|
+
|
|
231
|
+
[[kv_namespaces]]
|
|
232
|
+
binding = "KV"
|
|
233
|
+
id = "REPLACE_WITH_KV_NAMESPACE_ID"
|
|
234
|
+
|
|
235
|
+
[[r2_buckets]]
|
|
236
|
+
binding = "R2"
|
|
237
|
+
bucket_name = "${safeName}"
|
|
238
|
+
`);
|
|
239
|
+
|
|
240
|
+
write(`${dir}/migrations/0001_agentsam_core.sql`, migrationTemplate({ projectName: safeName, laneKey }));
|
|
241
|
+
write(`${dir}/src/index.js`, workerTemplate({ projectName: safeName, laneKey, agent: selectedAgent }));
|
|
242
|
+
write(`${dir}/README.md`, readmeTemplate({ projectName: safeName, lane, provider, agent: selectedAgent }));
|
|
243
|
+
|
|
244
|
+
write(`${dir}/scripts/smoke.mjs`, `
|
|
245
|
+
import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
|
|
246
|
+
|
|
247
|
+
const app = new AgentSam({ project: '${safeName}', lane: '${laneKey}', agent: '${selectedAgent}' });
|
|
248
|
+
const res = await app.handle(new Request('https://example.com/api/health'));
|
|
249
|
+
const data = await res.json();
|
|
250
|
+
|
|
251
|
+
if (!data.ok) {
|
|
252
|
+
console.error(data);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log('AgentSam smoke test passed:', data);
|
|
257
|
+
`);
|
|
258
|
+
|
|
259
|
+
return dir;
|
|
260
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export async function createSession({ env, agent, lane, goal }) {
|
|
2
|
+
const now = new Date().toISOString();
|
|
3
|
+
const session = {
|
|
4
|
+
id: crypto.randomUUID(),
|
|
5
|
+
agent,
|
|
6
|
+
lane,
|
|
7
|
+
goal: goal ?? null,
|
|
8
|
+
status: 'created',
|
|
9
|
+
created_at: now,
|
|
10
|
+
updated_at: now,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (env?.KV?.put) {
|
|
14
|
+
await env.KV.put(`agentsam:session:${session.id}`, JSON.stringify(session), { expirationTtl: 60 * 60 * 24 * 7 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (env?.DB?.prepare) {
|
|
18
|
+
await env.DB.prepare(
|
|
19
|
+
`CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
agent TEXT NOT NULL,
|
|
22
|
+
lane TEXT NOT NULL,
|
|
23
|
+
goal TEXT,
|
|
24
|
+
status TEXT NOT NULL,
|
|
25
|
+
created_at TEXT NOT NULL,
|
|
26
|
+
updated_at TEXT NOT NULL
|
|
27
|
+
)`
|
|
28
|
+
).run();
|
|
29
|
+
|
|
30
|
+
await env.DB.prepare(
|
|
31
|
+
`INSERT INTO agent_sessions (id, agent, lane, goal, status, created_at, updated_at)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
33
|
+
).bind(session.id, session.agent, session.lane, session.goal, session.status, session.created_at, session.updated_at).run();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return session;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function getSession({ env, sessionId }) {
|
|
40
|
+
if (!sessionId) return null;
|
|
41
|
+
|
|
42
|
+
if (env?.KV?.get) {
|
|
43
|
+
const raw = await env.KV.get(`agentsam:session:${sessionId}`);
|
|
44
|
+
if (raw) return JSON.parse(raw);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (env?.DB?.prepare) {
|
|
48
|
+
const row = await env.DB.prepare('SELECT * FROM agent_sessions WHERE id = ?').bind(sessionId).first();
|
|
49
|
+
if (row) return row;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
package/src/lib/tools.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const CATALOG = {
|
|
2
|
+
fullstack: [
|
|
3
|
+
{ name: 'plan', description: 'Break a goal into implementation steps.' },
|
|
4
|
+
{ name: 'code', description: 'Generate or revise application code.' },
|
|
5
|
+
{ name: 'deploy', description: 'Prepare deployment steps and checks.' },
|
|
6
|
+
],
|
|
7
|
+
cms: [
|
|
8
|
+
{ name: 'page', description: 'Create and revise pages.' },
|
|
9
|
+
{ name: 'asset', description: 'Attach cover images and media.' },
|
|
10
|
+
{ name: 'publish', description: 'Draft, preview, and publish content.' },
|
|
11
|
+
],
|
|
12
|
+
data: [
|
|
13
|
+
{ name: 'schema', description: 'Inspect and plan database schemas.' },
|
|
14
|
+
{ name: 'query', description: 'Draft safe SQL queries.' },
|
|
15
|
+
{ name: 'migration', description: 'Plan D1 or Postgres migrations.' },
|
|
16
|
+
],
|
|
17
|
+
crm: [
|
|
18
|
+
{ name: 'contact', description: 'Organize contacts and accounts.' },
|
|
19
|
+
{ name: 'workflow', description: 'Coordinate customer workflows.' },
|
|
20
|
+
],
|
|
21
|
+
creative: [
|
|
22
|
+
{ name: 'brief', description: 'Turn creative intent into a production brief.' },
|
|
23
|
+
{ name: 'asset', description: 'Plan media, 3D, and design assets.' },
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function normalizeLane(lane = 'fullstack') {
|
|
28
|
+
return String(lane).toLowerCase().replace(/\s+/g, '').replace('&', '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getToolCatalog(lane = 'fullstack') {
|
|
32
|
+
const key = normalizeLane(lane);
|
|
33
|
+
if (key.includes('cms')) return CATALOG.cms;
|
|
34
|
+
if (key.includes('data')) return CATALOG.data;
|
|
35
|
+
if (key.includes('customer') || key.includes('crm')) return CATALOG.crm;
|
|
36
|
+
if (key.includes('creative') || key.includes('design')) return CATALOG.creative;
|
|
37
|
+
return CATALOG.fullstack;
|
|
38
|
+
}
|
package/test/smoke.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { AgentSam, routeIntent, getToolCatalog } from '../src/index.js';
|
|
6
|
+
import { scaffoldProject } from '../src/lib/scaffold.js';
|
|
7
|
+
|
|
8
|
+
const app = new AgentSam({ project: 'smoke', lane: 'cms', agent: 'cms' });
|
|
9
|
+
let res = await app.handle(new Request('https://example.com/api/health'));
|
|
10
|
+
assert.equal(res.status, 200);
|
|
11
|
+
assert.equal((await res.json()).ok, true);
|
|
12
|
+
|
|
13
|
+
res = await app.handle(new Request('https://example.com/api/agentsam/info'));
|
|
14
|
+
const info = await res.json();
|
|
15
|
+
assert.equal(info.agent, 'cms');
|
|
16
|
+
assert.ok(info.capabilities.includes('page'));
|
|
17
|
+
|
|
18
|
+
res = await app.handle(new Request('https://example.com/api/agentsam/message', {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
body: JSON.stringify({ message: 'create an analytics page in the cms' }),
|
|
21
|
+
}));
|
|
22
|
+
const message = await res.json();
|
|
23
|
+
assert.equal(message.agent, 'cms');
|
|
24
|
+
assert.equal(message.intent, 'cms_build');
|
|
25
|
+
|
|
26
|
+
assert.equal(routeIntent({ message: 'drop table users' }).requires_approval, true);
|
|
27
|
+
assert.ok(getToolCatalog('Data Solutions').some((tool) => tool.name === 'query'));
|
|
28
|
+
|
|
29
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-sdk-test-'));
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
process.chdir(tmp);
|
|
32
|
+
const dir = scaffoldProject({ projectName: 'CMS Demo', lane: 'CMS', provider: 'Cloudflare Workers', agent: 'cms' });
|
|
33
|
+
process.chdir(cwd);
|
|
34
|
+
assert.ok(fs.existsSync(path.join(dir, 'src/index.js')));
|
|
35
|
+
assert.ok(fs.readFileSync(path.join(dir, 'migrations/0001_agentsam_core.sql'), 'utf8').includes('cms_pages'));
|
|
36
|
+
assert.ok(fs.readFileSync(path.join(dir, 'package.json'), 'utf8').includes('wrangler'));
|
|
37
|
+
|
|
38
|
+
console.log('SDK smoke tests passed');
|