@inneranimalmedia/agentsam-sdk 1.1.0 → 1.2.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.
@@ -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,34 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import readline from 'readline';
4
+ import pkg from '../package.json' with { type: 'json' };
4
5
  import { scaffoldProject } from './lib/scaffold.js';
6
+ import { SLASH_COMMANDS, SHELL_PHASES } from './lib/slash-commands.js';
5
7
 
6
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7
- const ask = (q) => new Promise(resolve => rl.question(q, resolve));
8
+ const VERSION = pkg.version;
9
+
10
+ function createPrompt() {
11
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
12
+ return {
13
+ ask: (q) => new Promise((resolve) => rl.question(q, resolve)),
14
+ close: () => rl.close(),
15
+ };
16
+ }
8
17
 
9
18
  const LANES = {
10
19
  '1': 'Full Stack',
11
- '2': 'Data Solutions',
12
- '3': 'Customer Management',
13
- '4': 'Creative & Design',
20
+ '2': 'CMS',
21
+ '3': 'Data Solutions',
22
+ '4': 'Customer Management',
23
+ '5': 'Creative & Design',
24
+ };
25
+
26
+ const LANE_BY_SLUG = {
27
+ fullstack: 'Full Stack',
28
+ cms: 'CMS',
29
+ data: 'Data Solutions',
30
+ crm: 'Customer Management',
31
+ creative: 'Creative & Design',
14
32
  };
15
33
 
16
34
  const PROVIDERS = {
@@ -19,61 +37,89 @@ const PROVIDERS = {
19
37
  '3': 'Local / Self-hosted',
20
38
  };
21
39
 
40
+ const PROVIDER_BY_SLUG = {
41
+ cloudflare: 'Cloudflare Workers',
42
+ github: 'GitHub + Cloudflare',
43
+ local: 'Local / Self-hosted',
44
+ };
45
+
22
46
  const AGENTS = {
23
47
  '1': 'orchestrator',
24
- '2': 'data',
25
- '3': 'crm',
26
- '4': 'creative',
48
+ '2': 'cms',
49
+ '3': 'data',
50
+ '4': 'crm',
51
+ '5': 'creative',
27
52
  };
28
53
 
29
- async function init() {
54
+ function laneKeyFromLabel(label) {
55
+ const entry = Object.entries(LANES).find(([, v]) => v === label);
56
+ return entry?.[0] ?? '1';
57
+ }
58
+
59
+ function printHelp() {
30
60
  console.log(`
31
- ╔═══════════════════════════════════╗
32
- ║ Agent Sam SDK — Init ║
33
- ╚═══════════════════════════════════╝
34
- `);
61
+ Agent Sam SDK — CLI v${VERSION}
35
62
 
36
- const projectName = await ask(' Project name: ');
63
+ Usage:
64
+ agentsam init [options] Scaffold a new Agent Sam project
65
+ agentsam shell CLI shell UX info + slash commands (Gorilla Shell)
66
+ agentsam --version Print version
67
+ agentsam --help Show this help
37
68
 
38
- console.log(`
39
- Select a lane:
40
- 1) Full Stack
41
- 2) Data Solutions
42
- 3) Customer Management
43
- 4) Creative & Design
69
+ Init options (non-interactive):
70
+ --name <name> Project directory name
71
+ --lane <fullstack|cms|data|crm|creative>
72
+ --provider <cloudflare|github|local>
73
+ --agent <orchestrator|cms|data|crm|creative>
74
+ --cf-account <id> Cloudflare account ID
75
+ --yes Skip confirmation prompt
44
76
  `);
45
- const laneKey = await ask(' Lane [1-4]: ');
46
- const lane = LANES[laneKey] ?? 'Full Stack';
77
+ }
47
78
 
48
- let provider = 'Cloudflare Workers';
49
- if (laneKey === '1') {
50
- console.log(`
51
- Select a provider:
52
- 1) Cloudflare Workers
53
- 2) GitHub + Cloudflare
54
- 3) Local / Self-hosted
55
- `);
56
- const providerKey = await ask(' Provider [1-3]: ');
57
- provider = PROVIDERS[providerKey] ?? 'Cloudflare Workers';
79
+ function parseInitArgs(argv) {
80
+ const opts = {
81
+ projectName: '',
82
+ lane: '',
83
+ provider: '',
84
+ agent: '',
85
+ cfAccountId: '',
86
+ yes: false,
87
+ };
88
+
89
+ for (let i = 0; i < argv.length; i += 1) {
90
+ const arg = argv[i];
91
+ if (arg === '--yes' || arg === '-y') opts.yes = true;
92
+ else if (arg === '--name') opts.projectName = argv[++i] || '';
93
+ else if (arg === '--lane') {
94
+ const slug = argv[++i] || '';
95
+ opts.lane = LANE_BY_SLUG[slug] || slug;
96
+ } else if (arg === '--provider') {
97
+ const slug = argv[++i] || '';
98
+ opts.provider = PROVIDER_BY_SLUG[slug] || slug;
99
+ } else if (arg === '--agent') opts.agent = argv[++i] || '';
100
+ else if (arg === '--cf-account') opts.cfAccountId = argv[++i] || '';
58
101
  }
59
102
 
60
- console.log(`
61
- Select your default Agent Sam:
62
- 1) Orchestrator — general purpose, routes to all lanes
63
- 2) Data Agent — database ops, migrations, queries
64
- 3) CRM Agent — customer management, contacts, billing
65
- 4) Creative Agent — design, 3D, media, content
66
- `);
67
- const agentKey = await ask(' Agent [1-4]: ');
68
- const agent = AGENTS[agentKey] ?? 'orchestrator';
103
+ return opts;
104
+ }
69
105
 
70
- const cfAccountId = await ask(' Cloudflare Account ID (enter to skip): ');
106
+ async function runInit(config) {
107
+ const {
108
+ projectName,
109
+ lane,
110
+ provider,
111
+ agent,
112
+ cfAccountId = '',
113
+ yes = false,
114
+ prompt = null,
115
+ } = config;
71
116
 
117
+ const safeDir = projectName.trim();
72
118
  console.log(`
73
119
  ┌─────────────────────────────────────┐
74
120
  │ Agent Sam Project Config │
75
121
  ├─────────────────────────────────────┤
76
- │ Project: ${projectName.padEnd(25)}│
122
+ │ Project: ${safeDir.padEnd(25)}│
77
123
  │ Lane: ${lane.padEnd(25)}│
78
124
  │ Provider: ${provider.padEnd(25)}│
79
125
  │ Agent: ${agent.padEnd(25)}│
@@ -81,37 +127,167 @@ async function init() {
81
127
  └─────────────────────────────────────┘
82
128
  `);
83
129
 
84
- const confirm = await ask(' Scaffold project? (y/n): ');
130
+ let confirm = 'y';
131
+ if (!yes && prompt) {
132
+ confirm = await prompt.ask(' Scaffold project? (y/n): ');
133
+ }
85
134
 
86
135
  if (confirm.toLowerCase() === 'y') {
87
- const dir = scaffoldProject({ projectName, lane, provider, agent, cfAccountId });
88
- console.log(`
136
+ try {
137
+ const dir = scaffoldProject({
138
+ projectName: safeDir,
139
+ lane,
140
+ provider,
141
+ agent,
142
+ cfAccountId,
143
+ sdkVersion: VERSION,
144
+ });
145
+ console.log(`
89
146
  ✓ Project created at ${dir}
90
147
 
91
148
  Next steps:
92
- cd ${projectName}
149
+ cd ${dir.split('/').pop()}
93
150
  cp .env.example .env
94
151
  npm install
152
+ npm run smoke
95
153
  npm run dev
96
154
  `);
155
+ } catch (error) {
156
+ console.error(`\n ✗ ${error.message}\n`);
157
+ process.exitCode = 1;
158
+ }
97
159
  } else {
98
160
  console.log('\n Cancelled.\n');
99
161
  }
162
+ }
163
+
164
+ async function initInteractive(partial = {}) {
165
+ const prompt = createPrompt();
166
+
167
+ console.log(`
168
+ ╔═══════════════════════════════════╗
169
+ ║ Agent Sam SDK — Init ║
170
+ ╚═══════════════════════════════════╝
171
+ `);
172
+
173
+ const projectName = partial.projectName || (await prompt.ask(' Project name: '));
174
+
175
+ if (!partial.lane) {
176
+ console.log(`
177
+ Select a lane:
178
+ 1) Full Stack
179
+ 2) CMS
180
+ 3) Data Solutions
181
+ 4) Customer Management
182
+ 5) Creative & Design
183
+ `);
184
+ }
185
+ const laneKey = partial.lane ? laneKeyFromLabel(partial.lane) : await prompt.ask(' Lane [1-5]: ');
186
+ const lane = partial.lane || LANES[laneKey] || 'Full Stack';
187
+
188
+ console.log(`
189
+ Select a provider:
190
+ 1) Cloudflare Workers
191
+ 2) GitHub + Cloudflare
192
+ 3) Local / Self-hosted
193
+ `);
194
+ const providerKey = partial.provider
195
+ ? Object.entries(PROVIDERS).find(([, v]) => v === partial.provider)?.[0] || '1'
196
+ : await prompt.ask(' Provider [1-3]: ');
197
+ const provider = partial.provider || PROVIDERS[providerKey] || 'Cloudflare Workers';
198
+
199
+ if (!partial.agent) {
200
+ console.log(`
201
+ Select your default Agent Sam:
202
+ 1) Orchestrator — general purpose, routes to all lanes
203
+ 2) CMS Agent — pages, sections, assets, publishing workflows
204
+ 3) Data Agent — database ops, migrations, queries
205
+ 4) CRM Agent — customer management, contacts, billing
206
+ 5) Creative Agent — design, 3D, media, content
207
+ `);
208
+ }
209
+ const agentKey = partial.agent
210
+ ? Object.entries(AGENTS).find(([, v]) => v === partial.agent)?.[0] || '1'
211
+ : await prompt.ask(' Agent [1-5]: ');
212
+ const agent = partial.agent || AGENTS[agentKey] || 'orchestrator';
100
213
 
101
- rl.close();
214
+ const cfAccountId =
215
+ partial.cfAccountId !== undefined && partial.cfAccountId !== ''
216
+ ? partial.cfAccountId
217
+ : await prompt.ask(' Cloudflare Account ID (enter to skip): ');
218
+
219
+ await runInit({
220
+ projectName,
221
+ lane,
222
+ provider,
223
+ agent,
224
+ cfAccountId,
225
+ yes: partial.yes,
226
+ prompt,
227
+ });
228
+
229
+ prompt.close();
102
230
  }
103
231
 
104
- const command = process.argv[2];
232
+ async function initFromArgs(argv) {
233
+ const opts = parseInitArgs(argv);
234
+ if (!opts.projectName) {
235
+ console.error('\n ✗ --name is required for non-interactive init.\n');
236
+ process.exit(1);
237
+ }
238
+ await runInit({
239
+ projectName: opts.projectName,
240
+ lane: opts.lane || 'Full Stack',
241
+ provider: opts.provider || 'Cloudflare Workers',
242
+ agent: opts.agent || 'orchestrator',
243
+ cfAccountId: opts.cfAccountId,
244
+ yes: true,
245
+ prompt: null,
246
+ });
247
+ }
105
248
 
106
- if (command === 'init') {
107
- init();
108
- } else {
249
+ async function runShellInfo() {
250
+ const next = SHELL_PHASES.find((p) => p.status === 'next');
109
251
  console.log(`
110
- Agent Sam SDK — CLI
252
+ ╔═══════════════════════════════════╗
253
+ ║ Agent Sam Shell (Gorilla) ║
254
+ ╚═══════════════════════════════════╝
111
255
 
112
- Usage:
113
- agentsam init Initialize a new Agent Sam project
256
+ Game-feel terminal UX for SDK installs — HUD, themes, slash commands, PTY bridge.
257
+
258
+ Phase 0 prototype: examples/gorilla-shell/
259
+ Docs: docs/CLI_SHELL.md
260
+ Next milestone: ${next?.label ?? 'PTY connection'}
114
261
 
115
- Version: 1.0.0
262
+ Slash commands (${SLASH_COMMANDS.length} registered):
263
+ `);
264
+ for (const row of SLASH_COMMANDS) {
265
+ console.log(` ${row.cmd.padEnd(14)} ${row.description}`);
266
+ }
267
+ console.log(`
268
+ Run the visual prototype:
269
+ cd examples/gorilla-shell && npm install && npm run dev
116
270
  `);
117
271
  }
272
+
273
+ const command = process.argv[2];
274
+ const rest = process.argv.slice(3);
275
+
276
+ if (command === '--version' || command === '-v') {
277
+ console.log(VERSION);
278
+ } else if (command === '--help' || command === '-h' || !command) {
279
+ printHelp();
280
+ } else if (command === 'shell') {
281
+ await runShellInfo();
282
+ } else if (command === 'init') {
283
+ const hasFlags = rest.some((a) => a.startsWith('--'));
284
+ if (hasFlags) {
285
+ await initFromArgs(rest);
286
+ } else {
287
+ await initInteractive({});
288
+ }
289
+ } else {
290
+ console.error(`\n Unknown command: ${command}\n`);
291
+ printHelp();
292
+ process.exit(1);
293
+ }
package/src/index.js CHANGED
@@ -1,5 +1,17 @@
1
- // @inneranimalmedia/agentsam-sdk
2
- // Agent Sam SDK — entry point
1
+ // @inneranimalmedia/agentsam-sdk — public API
3
2
 
4
- export const version = '1.0.0';
5
- export const name = 'agentsam-sdk';
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
+ export {
10
+ SLASH_COMMANDS,
11
+ SHELL_THEMES,
12
+ SHELL_PHASES,
13
+ listSlashCommands,
14
+ } from './lib/slash-commands.js';
15
+
16
+ export const version = pkg.version;
17
+ 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
+ }