@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.
@@ -1,260 +1,8 @@
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;
1
+ /**
2
+ * @deprecated Local scaffold replaced by CORE POST /api/sdk/scaffold — use `agentsam init`.
3
+ */
4
+ export function scaffoldProject() {
5
+ throw new Error(
6
+ 'Local scaffold is disabled. Run: npx @inneranimalmedia/agentsam-sdk init — Agent Sam provisions resources in YOUR Cloudflare account via CORE.',
7
+ );
260
8
  }
@@ -26,7 +26,7 @@ export const SLASH_COMMANDS = [
26
26
  /** Shell UX rollout phases (gorilla-shell → SDK default CLI experience). */
27
27
  export const SHELL_PHASES = [
28
28
  { id: '0-prototype', label: 'Visual prototype + demo scenarios', status: 'complete' },
29
- { id: 'pty-connection', label: 'ExecOS / iam-pty WebSocket bridge', status: 'next' },
29
+ { id: 'pty-connection', label: 'Local PTY via agentsam start-local', status: 'current' },
30
30
  { id: 'hud-layer', label: 'Quest log, tool gate, XP HUD', status: 'planned' },
31
31
  { id: 'buddy-system', label: 'In-shell Agent Sam via MCP', status: 'planned' },
32
32
  { id: 'dashboard-embed', label: 'Embeddable shell for IAM dashboard', status: 'planned' },
@@ -0,0 +1,21 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Write Agent Sam scaffold files to the user's machine (their repo, not IAM's).
6
+ * @param {string} targetDir
7
+ * @param {{ path: string, content: string }[]} files
8
+ */
9
+ export function writeScaffoldFiles(targetDir, files) {
10
+ const root = path.resolve(targetDir);
11
+ if (fs.existsSync(root)) {
12
+ throw new Error(`Directory already exists: ${root}`);
13
+ }
14
+ fs.mkdirSync(root, { recursive: true });
15
+ for (const file of files) {
16
+ const dest = path.join(root, file.path);
17
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
18
+ fs.writeFileSync(dest, file.content, 'utf8');
19
+ }
20
+ return root;
21
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Agent Sam local PTY — localhost WebSocket shell, no tunnel, no IAM.
3
+ * Compatible with iam-pty wire format (raw bytes + JSON resize/slash).
4
+ */
5
+ import http from 'node:http';
6
+ import { WebSocketServer } from 'ws';
7
+
8
+ const DEFAULT_PORT = 3099;
9
+
10
+ function parsePort(value, fallback) {
11
+ const n = Number.parseInt(String(value || ''), 10);
12
+ return Number.isFinite(n) && n > 0 ? n : fallback;
13
+ }
14
+
15
+ function shellForPlatform() {
16
+ if (process.platform === 'win32') return process.env.COMSPEC || 'powershell.exe';
17
+ return process.env.SHELL || '/bin/zsh';
18
+ }
19
+
20
+ async function loadPty() {
21
+ try {
22
+ const mod = await import('node-pty');
23
+ return mod.default || mod;
24
+ } catch (e) {
25
+ throw new Error(
26
+ `node-pty is required for start-local. Run: npm install node-pty\n${e?.message || e}`,
27
+ );
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @param {{ cwd?: string, port?: number, host?: string }} [opts]
33
+ */
34
+ export async function startLocalPtyServer(opts = {}) {
35
+ const pty = await loadPty();
36
+ const cwd = opts.cwd || process.cwd();
37
+ const port = parsePort(opts.port ?? process.env.PTY_PORT, DEFAULT_PORT);
38
+ const host = opts.host || '127.0.0.1';
39
+ const shell = shellForPlatform();
40
+
41
+ const httpServer = http.createServer((req, res) => {
42
+ const path = (req.url || '/').split('?')[0];
43
+ if (path === '/health') {
44
+ res.writeHead(200, { 'Content-Type': 'application/json' });
45
+ res.end(
46
+ JSON.stringify({
47
+ ok: true,
48
+ service: 'agentsam-local-pty',
49
+ cwd,
50
+ port,
51
+ shell,
52
+ }),
53
+ );
54
+ return;
55
+ }
56
+ res.writeHead(404);
57
+ res.end('not found');
58
+ });
59
+
60
+ const wss = new WebSocketServer({ server: httpServer });
61
+
62
+ wss.on('connection', (ws, req) => {
63
+ const url = new URL(req.url || '/', `http://${host}`);
64
+ const sessionCwd = url.searchParams.get('cwd')?.trim() || cwd;
65
+ const cols = parsePort(url.searchParams.get('cols'), 80);
66
+ const rows = parsePort(url.searchParams.get('rows'), 24);
67
+
68
+ const term = pty.spawn(shell, [], {
69
+ name: 'xterm-256color',
70
+ cols,
71
+ rows,
72
+ cwd: sessionCwd,
73
+ env: { ...process.env, TERM: 'xterm-256color', AGENTSAM_LOCAL_PTY: '1' },
74
+ });
75
+
76
+ const sessionId = `local_${Date.now().toString(36)}`;
77
+ ws.send(JSON.stringify({ type: 'session_id', session_id: sessionId }));
78
+
79
+ term.onData((data) => {
80
+ if (ws.readyState === ws.OPEN) ws.send(data);
81
+ });
82
+
83
+ ws.on('message', (raw) => {
84
+ const text = raw.toString();
85
+ try {
86
+ const msg = JSON.parse(text);
87
+ if (msg.type === 'resize' && msg.cols && msg.rows) {
88
+ term.resize(msg.cols, msg.rows);
89
+ return;
90
+ }
91
+ if (msg.type === 'slash' && msg.line) {
92
+ term.write(`${msg.line}\r`);
93
+ return;
94
+ }
95
+ } catch {
96
+ /* raw PTY input */
97
+ }
98
+ term.write(text);
99
+ });
100
+
101
+ const cleanup = () => {
102
+ try {
103
+ term.kill();
104
+ } catch {
105
+ /* ignore */
106
+ }
107
+ };
108
+ term.onExit(() => {
109
+ if (ws.readyState === ws.OPEN) ws.close();
110
+ });
111
+ ws.on('close', cleanup);
112
+ ws.on('error', cleanup);
113
+ });
114
+
115
+ await new Promise((resolve) => {
116
+ httpServer.listen(port, host, resolve);
117
+ });
118
+
119
+ return {
120
+ port,
121
+ host,
122
+ cwd,
123
+ shell,
124
+ url: `ws://${host}:${port}`,
125
+ healthUrl: `http://${host}:${port}/health`,
126
+ close: () =>
127
+ new Promise((resolve, reject) => {
128
+ wss.close(() => {
129
+ httpServer.close((err) => (err ? reject(err) : resolve()));
130
+ });
131
+ }),
132
+ };
133
+ }