@inneranimalmedia/agentsam-sdk 1.2.0 → 1.5.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.
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Local-first scaffold — no IAM, no Cloudflare, no OAuth.
3
+ * Connor proves the pattern on localhost; `agentsam deploy` graduates to cloud.
4
+ */
5
+
6
+ const LANE_KEYS = {
7
+ '1': 'fullstack',
8
+ '2': 'cms',
9
+ '3': 'data',
10
+ '4': 'crm',
11
+ '5': 'creative',
12
+ fullstack: 'fullstack',
13
+ cms: 'cms',
14
+ data: 'data',
15
+ crm: 'crm',
16
+ creative: 'creative',
17
+ };
18
+
19
+ const LANE_LABELS = {
20
+ fullstack: 'Full Stack',
21
+ cms: 'CMS',
22
+ data: 'Data Solutions',
23
+ crm: 'Customer Management',
24
+ creative: 'Creative & Design',
25
+ };
26
+
27
+ const AGENT_FOR_LANE = {
28
+ fullstack: 'orchestrator',
29
+ cms: 'cms',
30
+ data: 'data',
31
+ crm: 'crm',
32
+ creative: 'creative',
33
+ };
34
+
35
+ const RUN_TARGETS = {
36
+ '1': 'local',
37
+ '2': 'cloudflare',
38
+ '3': 'gcp',
39
+ local: 'local',
40
+ cloudflare: 'cloudflare',
41
+ gcp: 'gcp',
42
+ };
43
+
44
+ export { LANE_KEYS, LANE_LABELS, RUN_TARGETS };
45
+
46
+ export function normalizeLane(raw) {
47
+ const k = String(raw || 'fullstack').trim().toLowerCase();
48
+ return LANE_KEYS[k] || (LANE_LABELS[k] ? k : 'fullstack');
49
+ }
50
+
51
+ export function normalizeRunTarget(raw) {
52
+ const k = String(raw || 'local').trim().toLowerCase();
53
+ return RUN_TARGETS[k] || 'local';
54
+ }
55
+
56
+ function migrationSql(laneKey) {
57
+ const cmsExtra =
58
+ laneKey === 'cms'
59
+ ? `
60
+ CREATE TABLE IF NOT EXISTS cms_pages (
61
+ id TEXT PRIMARY KEY,
62
+ slug TEXT NOT NULL UNIQUE,
63
+ title TEXT NOT NULL,
64
+ status TEXT NOT NULL DEFAULT 'draft',
65
+ hero_asset_key TEXT,
66
+ content_json TEXT NOT NULL DEFAULT '{}',
67
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
68
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
69
+ );
70
+
71
+ CREATE TABLE IF NOT EXISTS cms_assets (
72
+ id TEXT PRIMARY KEY,
73
+ r2_key TEXT NOT NULL UNIQUE,
74
+ title TEXT,
75
+ alt_text TEXT,
76
+ metadata_json TEXT,
77
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
78
+ );
79
+ `
80
+ : '';
81
+
82
+ return `-- AgentSam local schema (${laneKey})
83
+ CREATE TABLE IF NOT EXISTS agent_sessions (
84
+ id TEXT PRIMARY KEY,
85
+ agent TEXT NOT NULL,
86
+ lane TEXT NOT NULL,
87
+ goal TEXT,
88
+ status TEXT NOT NULL DEFAULT 'created',
89
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
90
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
91
+ );
92
+
93
+ CREATE TABLE IF NOT EXISTS agent_messages (
94
+ id TEXT PRIMARY KEY,
95
+ session_id TEXT NOT NULL,
96
+ role TEXT NOT NULL,
97
+ content TEXT NOT NULL,
98
+ metadata_json TEXT,
99
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
100
+ FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
101
+ );
102
+
103
+ CREATE TABLE IF NOT EXISTS agent_tool_calls (
104
+ id TEXT PRIMARY KEY,
105
+ session_id TEXT NOT NULL,
106
+ tool_name TEXT NOT NULL,
107
+ status TEXT NOT NULL DEFAULT 'queued',
108
+ input_json TEXT,
109
+ output_json TEXT,
110
+ requires_approval INTEGER NOT NULL DEFAULT 0,
111
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
112
+ completed_at TEXT,
113
+ FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
114
+ );
115
+ ${cmsExtra}
116
+ `.trim();
117
+ }
118
+
119
+ /**
120
+ * @param {{ projectName: string, laneKey: string, laneLabel: string, agent: string, runTarget: string, sdkVersion: string }}
121
+ */
122
+ export function buildLocalScaffoldFiles({
123
+ projectName,
124
+ laneKey,
125
+ laneLabel,
126
+ agent,
127
+ runTarget,
128
+ sdkVersion = '1.5.1',
129
+ }) {
130
+ const sdkRange = `^${sdkVersion.split('.').slice(0, 2).join('.')}.0`;
131
+ const migration = migrationSql(laneKey);
132
+
133
+ return [
134
+ {
135
+ path: 'agentsam.config.js',
136
+ content: `export default {
137
+ project: '${projectName}',
138
+ lane: '${laneKey}',
139
+ agent: '${agent}',
140
+ runTarget: '${runTarget}',
141
+ api: { baseUrl: '/api/agentsam' },
142
+ };
143
+ `,
144
+ },
145
+ {
146
+ path: '.agentsam/config.json',
147
+ content: `${JSON.stringify(
148
+ {
149
+ project: projectName,
150
+ lane: laneKey,
151
+ agent,
152
+ run_target: 'local',
153
+ deploy_target: runTarget === 'local' ? null : runTarget,
154
+ pty_port: 3099,
155
+ dev_port: 8787,
156
+ ui_port: 5173,
157
+ ui: 'gorilla',
158
+ scaffold_version: sdkVersion,
159
+ },
160
+ null,
161
+ 2,
162
+ )}\n`,
163
+ },
164
+ {
165
+ path: '.agentsam/start-local.md',
166
+ content: `# Local terminal (no Cloudflare tunnel)
167
+
168
+ Agent Sam runs a PTY on **your machine** — no accounts, no cloudflared, no IAM login required.
169
+
170
+ \`\`\`bash
171
+ # Terminal 1 — local PTY (Agent Sam shell bridge)
172
+ npx agentsam start-local
173
+
174
+ # Terminal 2 — Gorilla Mode UI + local Worker API
175
+ npm run dev
176
+ npm run db:migrate # first time only
177
+ \`\`\`
178
+
179
+ Open **http://localhost:5173** — pixel Gorilla shell proxies \`/api\` → Worker on :8787.
180
+
181
+ PTY listens on \`ws://127.0.0.1:3099\`. Health: \`curl http://127.0.0.1:3099/health\`
182
+
183
+ When you're ready to ship to Cloudflare or GCP:
184
+
185
+ \`\`\`bash
186
+ npx agentsam deploy
187
+ \`\`\`
188
+ `,
189
+ },
190
+ {
191
+ path: '.gitignore',
192
+ content: `node_modules/
193
+ .env
194
+ .dev.vars
195
+ dist/
196
+ .wrangler/
197
+ *.db
198
+ `,
199
+ },
200
+ {
201
+ path: 'package.json',
202
+ content: `${JSON.stringify(
203
+ {
204
+ name: projectName,
205
+ version: '0.1.0',
206
+ type: 'module',
207
+ private: true,
208
+ scripts: {
209
+ dev: 'concurrently -k "npm run dev:worker" "npm run dev:ui"',
210
+ 'dev:worker': 'wrangler dev --local --port 8787',
211
+ 'dev:ui': 'vite',
212
+ 'dev:node': 'node --watch src/dev-server.js',
213
+ deploy: 'wrangler deploy',
214
+ smoke: 'node ./scripts/smoke.mjs',
215
+ 'db:migrate': 'wrangler d1 migrations apply DB --local',
216
+ 'start:pty': 'agentsam start-local',
217
+ },
218
+ dependencies: {
219
+ '@inneranimalmedia/agentsam-sdk': sdkRange,
220
+ react: '^19.0.0',
221
+ 'react-dom': '^19.0.0',
222
+ },
223
+ devDependencies: {
224
+ wrangler: '^4.0.0',
225
+ vite: '^6.0.0',
226
+ '@vitejs/plugin-react': '^4.0.0',
227
+ concurrently: '^9.0.0',
228
+ },
229
+ },
230
+ null,
231
+ 2,
232
+ )}\n`,
233
+ },
234
+ {
235
+ path: '.env',
236
+ content: `VITE_PROJECT_NAME=${projectName}
237
+ VITE_LANE_KEY=${laneKey}
238
+ VITE_AGENT=${agent}
239
+ `,
240
+ },
241
+ {
242
+ path: 'wrangler.toml',
243
+ content: `name = "${projectName}"
244
+ main = "src/index.js"
245
+ compatibility_date = "2026-06-27"
246
+
247
+ [[d1_databases]]
248
+ binding = "DB"
249
+ database_name = "${projectName}-db"
250
+ database_id = "local-dev"
251
+
252
+ [dev]
253
+ port = 8787
254
+ local_protocol = "http"
255
+ `,
256
+ },
257
+ {
258
+ path: 'migrations/0001_agentsam_core.sql',
259
+ content: `${migration}\n`,
260
+ },
261
+ {
262
+ path: 'src/index.js',
263
+ content: `import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
264
+
265
+ export default {
266
+ async fetch(request, env, ctx) {
267
+ const agent = new AgentSam({
268
+ env,
269
+ ctx,
270
+ project: '${projectName}',
271
+ lane: '${laneKey}',
272
+ agent: '${agent}',
273
+ });
274
+ return agent.handle(request);
275
+ },
276
+ };
277
+ `,
278
+ },
279
+ {
280
+ path: 'src/dev-server.js',
281
+ content: `/**
282
+ * Plain Node dev server — no Cloudflare account required.
283
+ * Use when wrangler is unavailable: npm run dev:node
284
+ */
285
+ import { createServer } from 'node:http';
286
+ import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
287
+
288
+ const app = new AgentSam({
289
+ project: '${projectName}',
290
+ lane: '${laneKey}',
291
+ agent: '${agent}',
292
+ });
293
+
294
+ const port = Number(process.env.PORT || 8787);
295
+
296
+ createServer(async (req, res) => {
297
+ const url = \`http://127.0.0.1\${req.url || '/'}\`;
298
+ const headers = new Headers();
299
+ for (const [k, v] of Object.entries(req.headers)) {
300
+ if (v != null) headers.set(k, Array.isArray(v) ? v.join(', ') : v);
301
+ }
302
+ let body;
303
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
304
+ const chunks = [];
305
+ for await (const chunk of req) chunks.push(chunk);
306
+ body = Buffer.concat(chunks);
307
+ }
308
+ const response = await app.handle(new Request(url, { method: req.method, headers, body }));
309
+ res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
310
+ res.end(Buffer.from(await response.arrayBuffer()));
311
+ }).listen(port, '127.0.0.1', () => {
312
+ console.log(\`Agent Sam dev server http://127.0.0.1:\${port}\`);
313
+ });
314
+ `,
315
+ },
316
+ {
317
+ path: 'scripts/smoke.mjs',
318
+ content: `import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
319
+
320
+ const app = new AgentSam({ project: '${projectName}', lane: '${laneKey}', agent: '${agent}' });
321
+ const res = await app.handle(new Request('https://example.com/api/health'));
322
+ const data = await res.json();
323
+
324
+ if (!data.ok) {
325
+ console.error(data);
326
+ process.exit(1);
327
+ }
328
+
329
+ console.log('AgentSam smoke test passed:', data);
330
+ `,
331
+ },
332
+ {
333
+ path: 'README.md',
334
+ content: `# ${projectName}
335
+
336
+ Built locally with [Agent Sam SDK](https://inneranimalmedia.com) — **${laneLabel}** lane, \`${agent}\` agent.
337
+
338
+ ## Gorilla Mode (default UI)
339
+
340
+ \`\`\`bash
341
+ npm install
342
+ npm run smoke
343
+ npm run dev # Worker :8787 + Vite :5173
344
+ \`\`\`
345
+
346
+ Open **http://localhost:5173** — pixel Gorilla shell. Live \`/health\`, \`/samiam\`, and demo scenarios proxy to your local Worker.
347
+
348
+ \`\`\`bash
349
+ npx agentsam start-local # optional — local PTY on :3099
350
+ npm run db:migrate # apply local D1 schema
351
+ \`\`\`
352
+
353
+ Everything works offline. No Cloudflare account required to start.
354
+
355
+ ## Graduate to cloud
356
+
357
+ \`\`\`bash
358
+ npx agentsam deploy
359
+ \`\`\`
360
+
361
+ Cloudflare OAuth is prompted **only** when you deploy — not at init.
362
+
363
+ Run target selected at init: **${runTarget}**
364
+ `,
365
+ },
366
+ ];
367
+ }
368
+
369
+ export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.1') {
370
+ const projectName = String(body.projectName || body.project_name || 'agentsam-project')
371
+ .trim()
372
+ .toLowerCase()
373
+ .replace(/[^a-z0-9-]+/g, '-')
374
+ .replace(/^-+|-+$/g, '') || 'agentsam-project';
375
+ const laneKey = normalizeLane(body.lane);
376
+ const laneLabel = LANE_LABELS[laneKey] || 'Full Stack';
377
+ const agent = AGENT_FOR_LANE[laneKey] || 'orchestrator';
378
+ const runTarget = normalizeRunTarget(body.runTarget || body.run_target || 'local');
379
+
380
+ return {
381
+ projectName,
382
+ laneKey,
383
+ laneLabel,
384
+ agent,
385
+ runTarget,
386
+ files: buildLocalScaffoldFiles({ projectName, laneKey, laneLabel, agent, runTarget, sdkVersion }),
387
+ next_steps: [
388
+ 'npm install',
389
+ 'npm run smoke',
390
+ 'npm run dev',
391
+ 'Open http://localhost:5173 — Gorilla Mode UI',
392
+ 'npm run db:migrate',
393
+ 'Optional: npx agentsam start-local',
394
+ 'When ready: npx agentsam deploy',
395
+ ],
396
+ };
397
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Optional BYOK key paste during SDK init → CORE user_api_keys.
3
+ */
4
+ import { getJson, postJson } from './core-client.js';
5
+
6
+ const PROVIDERS = [
7
+ { id: 'openai', label: 'OpenAI' },
8
+ { id: 'anthropic', label: 'Anthropic' },
9
+ ];
10
+
11
+ /**
12
+ * @param {string} token SDK bearer
13
+ * @param {{ ask: (q: string) => Promise<string> } | null} prompt
14
+ */
15
+ export async function promptOptionalByokKeys(token, prompt) {
16
+ if (!token || !prompt) return;
17
+
18
+ let ctx;
19
+ try {
20
+ ctx = await getJson('/api/sdk/context', token);
21
+ } catch {
22
+ console.log('\n ⚠ Could not load BYOK status — skip key paste or add keys in Dashboard → Settings → Keys\n');
23
+ return;
24
+ }
25
+
26
+ const byok = ctx?.byok || {};
27
+ console.log('\n Provider keys (BYOK — optional, stored in YOUR IAM account):\n');
28
+
29
+ for (const p of PROVIDERS) {
30
+ const slot = byok[p.id];
31
+ if (slot?.configured) {
32
+ console.log(` ${p.label.padEnd(12)} ✓ connected ${slot.masked || ''}`);
33
+ continue;
34
+ }
35
+
36
+ const ans = await prompt.ask(` ${p.label} API key (Enter to skip): `);
37
+ const key = ans.trim();
38
+ if (!key) continue;
39
+
40
+ try {
41
+ await postJson(
42
+ '/api/sdk/keys',
43
+ {
44
+ provider: p.id,
45
+ api_key: key,
46
+ label: `${p.label} (SDK init)`,
47
+ validate: true,
48
+ },
49
+ token,
50
+ );
51
+ console.log(` ${p.label.padEnd(12)} ✓ saved to IAM BYOK`);
52
+ } catch (e) {
53
+ console.log(` ${p.label.padEnd(12)} ✗ ${e?.message || 'save failed'}`);
54
+ }
55
+ }
56
+ console.log('');
57
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Persist SDK session token locally for repeat inits (user's machine only).
3
+ */
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ export function saveSdkTokenHint(cwd, token) {
8
+ const t = String(token || '').trim();
9
+ if (!t || !t.startsWith('sdk_')) return null;
10
+ const dir = path.join(path.resolve(cwd), '.agentsam');
11
+ fs.mkdirSync(dir, { recursive: true });
12
+ const hintPath = path.join(dir, 'credentials.env.example');
13
+ const content = `# Agent Sam SDK — add to your shell profile (optional)
14
+ export AGENTSAM_SDK_TOKEN=${t}
15
+ export USER_GCP_PROJECT=YOUR_GCP_PROJECT_ID
16
+ `;
17
+ fs.writeFileSync(hintPath, content, 'utf8');
18
+ return hintPath;
19
+ }