@inneranimalmedia/agentsam-sdk 1.5.0 → 1.6.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 CHANGED
@@ -35,10 +35,12 @@ cd my-project
35
35
  npm install
36
36
  npm run smoke
37
37
  npx agentsam start-local # local PTY on ws://127.0.0.1:3099
38
+ npx agentsam tunnel # cloudflared → register with IAM (dashboard Local lane)
38
39
  npm run dev # http://127.0.0.1:8787
39
40
  npm run db:migrate # local D1 schema
40
41
  ```
41
42
 
43
+ `agentsam tunnel` (default `--quick`) starts a Cloudflare quick tunnel to `:3099` and POSTs the `wss://` URL to `/api/sdk/terminal/register-local` so `agentsam_terminal_local` can reach your machine. Use `--named --tunnel-name … --hostname … --zone-id …` for a stable BYOK named tunnel.
42
44
  When you're ready to ship to **your** Cloudflare account:
43
45
 
44
46
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
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
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "files": [
15
15
  "src",
16
+ "templates",
16
17
  "docs",
17
18
  "examples",
18
19
  "test",
package/src/cli.js CHANGED
@@ -4,9 +4,11 @@ import pkg from '../package.json' with { type: 'json' };
4
4
  import readline from 'readline';
5
5
  import { buildLocalScaffoldMeta, LANE_KEYS, RUN_TARGETS } from './lib/local-scaffold.js';
6
6
  import { writeScaffoldFiles } from './lib/write-files.js';
7
+ import { copyGorillaTemplate } from './lib/gorilla-template.js';
7
8
  import { printContextSummary } from './lib/detect-context.js';
8
9
  import { promptOptionalByokKeys } from './lib/prompt-byok.js';
9
10
  import { runStartLocal } from './commands/start-local.js';
11
+ import { runTunnel } from './commands/tunnel.js';
10
12
  import { runDeploy } from './commands/deploy.js';
11
13
  import { SLASH_COMMANDS, SHELL_PHASES } from './lib/slash-commands.js';
12
14
 
@@ -27,6 +29,7 @@ function printHelp() {
27
29
  Usage:
28
30
  agentsam init Local-first project scaffold (default: localhost, no accounts)
29
31
  agentsam start-local Local PTY on ws://127.0.0.1:3099 (no tunnel, no Cloudflare)
32
+ agentsam tunnel Expose local PTY to IAM (cloudflared + register)
30
33
  agentsam deploy Graduate to Cloudflare / GCP when ready
31
34
  agentsam shell Slash commands + shell UX info
32
35
  agentsam --version
@@ -35,6 +38,12 @@ function printHelp() {
35
38
  Init is completable with Node only — no IAM login, no OAuth, no Cloudflare.
36
39
  Prove locally first; deploy prompts for accounts only when you choose to ship.
37
40
 
41
+ Tunnel options:
42
+ --quick Quick tunnel (default) — trycloudflare.com URL
43
+ --named Named CF tunnel (needs --tunnel-name --hostname --zone-id)
44
+ --port <n> Local PTY port (default 3099)
45
+ --token <sdk_…> Use existing AGENTSAM_SDK_TOKEN (skip browser auth)
46
+
38
47
  Init options:
39
48
  --name <name> Project directory name
40
49
  --lane <fullstack|cms|data|crm|creative>
@@ -89,9 +98,11 @@ async function runLocalInit(config) {
89
98
  `);
90
99
 
91
100
  const dir = writeScaffoldFiles(meta.projectName, meta.files);
101
+ copyGorillaTemplate(dir, meta);
92
102
 
93
103
  console.log(`
94
104
  ✓ Project ready: ${dir}
105
+ ✓ Gorilla Mode UI → gorilla/ (http://localhost:5173 after npm run dev)
95
106
 
96
107
  Next steps:`);
97
108
  for (const step of meta.next_steps) {
@@ -105,9 +116,8 @@ async function runLocalInit(config) {
105
116
 
106
117
  console.log(`
107
118
  Local in ~60 seconds:
108
- cd ${meta.projectName} && npm install && npm run smoke
109
- npx agentsam start-local
110
- npm run dev
119
+ cd ${meta.projectName} && npm install && npm run smoke && npm run dev
120
+ open http://localhost:5173
111
121
  `);
112
122
  }
113
123
 
@@ -191,6 +201,13 @@ if (command === '--version' || command === '-v') {
191
201
  await runShellInfo();
192
202
  } else if (command === 'start-local') {
193
203
  await runStartLocal({});
204
+ } else if (command === 'tunnel') {
205
+ try {
206
+ await runTunnel(rest);
207
+ } catch (e) {
208
+ console.error(`\n ✗ ${e?.message || e}\n`);
209
+ process.exit(1);
210
+ }
194
211
  } else if (command === 'deploy') {
195
212
  try {
196
213
  await runDeploy(parseDeployArgs(rest));
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentSam HTTP API server for containerized deployments
4
+ * Exposes the AgentSam SDK as HTTP endpoints (compatible with Cloudflare Workers pattern)
5
+ */
6
+ import http from 'node:http';
7
+ import { AgentSam } from './AgentSam.js';
8
+
9
+ const PORT = process.env.PORT || 8080;
10
+ const HOST = process.env.HOST || '0.0.0.0';
11
+
12
+ const agentSam = new AgentSam({
13
+ env: process.env,
14
+ agent: process.env.AGENT || 'orchestrator',
15
+ lane: process.env.LANE || 'fullstack',
16
+ project: process.env.PROJECT || 'agentsam-docker',
17
+ });
18
+
19
+ /**
20
+ * Adapt Web API Request to Node HTTP
21
+ */
22
+ class NodeRequest {
23
+ constructor(req, body) {
24
+ this.url = `http://${req.headers.host || 'localhost'}${req.url}`;
25
+ this.method = req.method;
26
+ this.headers = req.headers;
27
+ this.body = body;
28
+ }
29
+
30
+ async json() {
31
+ if (!this.body) return {};
32
+ try {
33
+ return JSON.parse(this.body);
34
+ } catch {
35
+ return {};
36
+ }
37
+ }
38
+ }
39
+
40
+ const server = http.createServer(async (req, res) => {
41
+ let body = '';
42
+
43
+ // Read request body
44
+ req.on('data', (chunk) => {
45
+ body += chunk.toString();
46
+ });
47
+
48
+ req.on('end', async () => {
49
+ try {
50
+ const request = new NodeRequest(req, body);
51
+ const response = await agentSam.handle(request);
52
+
53
+ // Write response headers
54
+ const headers = {};
55
+ if (response.headers && response.headers.entries) {
56
+ for (const [key, value] of response.headers.entries()) {
57
+ headers[key] = value;
58
+ }
59
+ }
60
+ res.writeHead(response.status, headers);
61
+
62
+ // Write response body
63
+ const text = await response.text();
64
+ res.end(text);
65
+ } catch (error) {
66
+ console.error('Error handling request:', error);
67
+ res.writeHead(500, { 'Content-Type': 'application/json' });
68
+ res.end(JSON.stringify({ ok: false, error: error.message }));
69
+ }
70
+ });
71
+ });
72
+
73
+ server.listen(PORT, HOST, () => {
74
+ console.log(`
75
+ ✓ Agent Sam API Server listening on http://${HOST === '0.0.0.0' ? 'localhost' : HOST}:${PORT}
76
+
77
+ Routes:
78
+ GET /api/health → service health
79
+ GET /api/agentsam/info → agent capabilities
80
+ POST /api/agentsam/session → create session
81
+ GET /api/agentsam/session/:id → get session
82
+ POST /api/agentsam/message → route message
83
+
84
+ Environment: AGENT=${process.env.AGENT || 'orchestrator'} LANE=${process.env.LANE || 'fullstack'}
85
+ `);
86
+ });
87
+
88
+ // Graceful shutdown
89
+ const shutdown = async () => {
90
+ console.log('\nShutting down...');
91
+ server.close(() => {
92
+ process.exit(0);
93
+ });
94
+ setTimeout(() => process.exit(1), 5000);
95
+ };
96
+ process.on('SIGINT', shutdown);
97
+ process.on('SIGTERM', shutdown);
@@ -0,0 +1,232 @@
1
+ /**
2
+ * agentsam tunnel — expose local PTY (:3099) to the IAM platform.
3
+ *
4
+ * Default (--quick): cloudflared quick tunnel → register ws_url via SDK API.
5
+ * Named (--named): platform provisions CF named tunnel + DNS; run with --token.
6
+ */
7
+ import { spawn, spawnSync } from 'node:child_process';
8
+ import { authenticateViaBrowser } from '../lib/auth.js';
9
+ import { postJson } from '../lib/core-client.js';
10
+
11
+ const DEFAULT_PORT = 3099;
12
+
13
+ function parseArgs(argv) {
14
+ const opts = {
15
+ mode: 'quick',
16
+ port: DEFAULT_PORT,
17
+ tunnelName: '',
18
+ hostname: '',
19
+ zoneId: '',
20
+ platform: process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'macos' : 'linux',
21
+ shell: process.platform === 'win32' ? 'powershell' : process.env.SHELL || '/bin/zsh',
22
+ skipAuth: false,
23
+ };
24
+ for (let i = 0; i < argv.length; i += 1) {
25
+ const a = argv[i];
26
+ if (a === '--quick') opts.mode = 'quick';
27
+ else if (a === '--named') opts.mode = 'named';
28
+ else if (a === '--port') opts.port = Number(argv[++i]) || DEFAULT_PORT;
29
+ else if (a === '--tunnel-name') opts.tunnelName = argv[++i] || '';
30
+ else if (a === '--hostname') opts.hostname = argv[++i] || '';
31
+ else if (a === '--zone-id') opts.zoneId = argv[++i] || '';
32
+ else if (a === '--platform') opts.platform = argv[++i] || opts.platform;
33
+ else if (a === '--shell') opts.shell = argv[++i] || opts.shell;
34
+ else if (a === '--token' && argv[i + 1]) {
35
+ process.env.AGENTSAM_SDK_TOKEN = argv[++i];
36
+ }
37
+ }
38
+ return opts;
39
+ }
40
+
41
+ function ensureCloudflared() {
42
+ const which = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['cloudflared'], {
43
+ encoding: 'utf8',
44
+ });
45
+ if (which.status !== 0) {
46
+ throw new Error(
47
+ 'cloudflared not found. Install: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/',
48
+ );
49
+ }
50
+ }
51
+
52
+ async function resolveToken() {
53
+ const existing = String(process.env.AGENTSAM_SDK_TOKEN || '').trim();
54
+ if (existing.startsWith('sdk_')) return existing;
55
+ const session = await authenticateViaBrowser();
56
+ const tok = String(session?.access_token || '').trim();
57
+ if (!tok.startsWith('sdk_')) throw new Error('IAM auth did not return an sdk_ bearer token');
58
+ process.env.AGENTSAM_SDK_TOKEN = tok;
59
+ return tok;
60
+ }
61
+
62
+ async function assertLocalPty(port) {
63
+ const url = `http://127.0.0.1:${port}/health`;
64
+ try {
65
+ const res = await fetch(url, { signal: AbortSignal.timeout(2500) });
66
+ if (!res.ok) throw new Error(`health ${res.status}`);
67
+ return true;
68
+ } catch {
69
+ throw new Error(
70
+ `Local PTY not reachable at ${url}. In another terminal run: npx agentsam start-local`,
71
+ );
72
+ }
73
+ }
74
+
75
+ function httpsToWss(url) {
76
+ const u = String(url || '').trim();
77
+ if (!u) return '';
78
+ if (u.startsWith('wss://') || u.startsWith('ws://')) return u.replace(/\/$/, '');
79
+ if (u.startsWith('https://')) return `wss://${u.slice(8)}`.replace(/\/$/, '');
80
+ if (u.startsWith('http://')) return `ws://${u.slice(7)}`.replace(/\/$/, '');
81
+ return `wss://${u.replace(/^\/+/, '')}`.replace(/\/$/, '');
82
+ }
83
+
84
+ /**
85
+ * Parse trycloudflare.com URL from cloudflared stderr/stdout.
86
+ * @param {import('node:child_process').ChildProcessWithoutNullStreams} child
87
+ * @returns {Promise<string>}
88
+ */
89
+ function waitForQuickTunnelUrl(child) {
90
+ return new Promise((resolve, reject) => {
91
+ let buf = '';
92
+ const timer = setTimeout(() => {
93
+ reject(new Error('Timed out waiting for cloudflared quick tunnel URL (60s)'));
94
+ }, 60_000);
95
+
96
+ const onData = (chunk) => {
97
+ const text = chunk.toString();
98
+ buf += text;
99
+ process.stderr.write(text);
100
+ const m =
101
+ buf.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i) ||
102
+ buf.match(/https:\/\/[a-z0-9.-]+\.cfargotunnel\.com/i);
103
+ if (m) {
104
+ clearTimeout(timer);
105
+ child.stdout?.off('data', onData);
106
+ child.stderr?.off('data', onData);
107
+ resolve(m[0]);
108
+ }
109
+ };
110
+
111
+ child.stdout?.on('data', onData);
112
+ child.stderr?.on('data', onData);
113
+ child.on('error', (e) => {
114
+ clearTimeout(timer);
115
+ reject(e);
116
+ });
117
+ child.on('exit', (code) => {
118
+ clearTimeout(timer);
119
+ reject(new Error(`cloudflared exited early (code ${code})`));
120
+ });
121
+ });
122
+ }
123
+
124
+ async function runQuick(opts, token) {
125
+ await assertLocalPty(opts.port);
126
+ ensureCloudflared();
127
+
128
+ console.log(`
129
+ Agent Sam — tunnel (quick)
130
+ Local PTY http://127.0.0.1:${opts.port}
131
+ Mode cloudflared quick tunnel → IAM register-local
132
+ `);
133
+
134
+ const child = spawn(
135
+ 'cloudflared',
136
+ ['tunnel', '--url', `http://127.0.0.1:${opts.port}`, '--no-autoupdate'],
137
+ { stdio: ['ignore', 'pipe', 'pipe'] },
138
+ );
139
+
140
+ const publicUrl = await waitForQuickTunnelUrl(child);
141
+ const wsUrl = httpsToWss(publicUrl);
142
+ console.log(`\n ✓ Public URL ${publicUrl}`);
143
+ console.log(` ✓ Registering ${wsUrl}\n`);
144
+
145
+ const registered = await postJson(
146
+ '/api/sdk/terminal/register-local',
147
+ {
148
+ ws_url: wsUrl,
149
+ platform: opts.platform,
150
+ shell: opts.shell,
151
+ },
152
+ token,
153
+ );
154
+
155
+ console.log(` ✓ IAM local lane active`);
156
+ if (registered?.connection?.id) {
157
+ console.log(` ✓ connection ${registered.connection.id}`);
158
+ }
159
+ console.log(`
160
+ Keep this process running. In the dashboard: Terminal → Local.
161
+
162
+ Ctrl+C stops the tunnel.
163
+ `);
164
+
165
+ await new Promise((resolve) => {
166
+ child.on('exit', resolve);
167
+ process.on('SIGINT', () => {
168
+ child.kill('SIGINT');
169
+ });
170
+ process.on('SIGTERM', () => {
171
+ child.kill('SIGTERM');
172
+ });
173
+ });
174
+ }
175
+
176
+ async function runNamed(opts, token) {
177
+ if (!opts.tunnelName || !opts.hostname || !opts.zoneId) {
178
+ throw new Error(
179
+ 'Named mode requires --tunnel-name, --hostname, and --zone-id (from your CF zone).',
180
+ );
181
+ }
182
+ await assertLocalPty(opts.port);
183
+ ensureCloudflared();
184
+
185
+ console.log(`
186
+ Agent Sam — tunnel (named)
187
+ Provisioning Cloudflare tunnel ${opts.tunnelName} → ${opts.hostname}
188
+ `);
189
+
190
+ const provisioned = await postJson(
191
+ '/api/sdk/terminal/tunnel/provision',
192
+ {
193
+ tunnel_name: opts.tunnelName,
194
+ hostname: opts.hostname,
195
+ zone_id: opts.zoneId,
196
+ port: opts.port,
197
+ platform: opts.platform,
198
+ shell: opts.shell,
199
+ },
200
+ token,
201
+ );
202
+
203
+ const runToken = String(provisioned?.run_token || '').trim();
204
+ if (!runToken) throw new Error('Platform did not return a cloudflared run_token');
205
+
206
+ console.log(` ✓ ws_url ${provisioned.ws_url || `wss://${opts.hostname}`}`);
207
+ console.log(` ✓ Starting cloudflared tunnel run --token …\n`);
208
+
209
+ const child = spawn('cloudflared', ['tunnel', 'run', '--token', runToken, '--no-autoupdate'], {
210
+ stdio: 'inherit',
211
+ });
212
+
213
+ await new Promise((resolve, reject) => {
214
+ child.on('error', reject);
215
+ child.on('exit', resolve);
216
+ process.on('SIGINT', () => child.kill('SIGINT'));
217
+ });
218
+ }
219
+
220
+ /**
221
+ * @param {string[]} [argv]
222
+ */
223
+ export async function runTunnel(argv = []) {
224
+ const opts = parseArgs(argv);
225
+ const token = await resolveToken();
226
+
227
+ if (opts.mode === 'named') {
228
+ await runNamed(opts, token);
229
+ } else {
230
+ await runQuick(opts, token);
231
+ }
232
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copy Gorilla Mode pixel UI into scaffolded projects (fully local).
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const TEMPLATE_FILES = ['App.tsx', 'main.jsx', 'index.html'];
9
+ const ROOT_FILES = ['vite.config.js'];
10
+
11
+ export function resolveGorillaTemplateDir() {
12
+ const here = path.dirname(fileURLToPath(import.meta.url));
13
+ return path.resolve(here, '..', '..', 'templates', 'gorilla-shell');
14
+ }
15
+
16
+ /**
17
+ * @param {string} projectRoot
18
+ * @param {{ projectName: string, laneKey: string, agent: string, laneLabel: string }} meta
19
+ */
20
+ export function copyGorillaTemplate(projectRoot, meta) {
21
+ const srcDir = resolveGorillaTemplateDir();
22
+ if (!fs.existsSync(srcDir)) {
23
+ throw new Error(`Gorilla template not found at ${srcDir}`);
24
+ }
25
+
26
+ const destDir = path.join(path.resolve(projectRoot), 'gorilla');
27
+ fs.mkdirSync(destDir, { recursive: true });
28
+
29
+ const vars = {
30
+ '{{PROJECT_NAME}}': meta.projectName,
31
+ '{{LANE_KEY}}': meta.laneKey,
32
+ '{{LANE_LABEL}}': meta.laneLabel,
33
+ '{{AGENT}}': meta.agent,
34
+ };
35
+
36
+ for (const name of TEMPLATE_FILES) {
37
+ const src = path.join(srcDir, name);
38
+ if (!fs.existsSync(src)) continue;
39
+ let content = fs.readFileSync(src, 'utf8');
40
+ for (const [token, value] of Object.entries(vars)) {
41
+ content = content.split(token).join(value);
42
+ }
43
+ fs.writeFileSync(path.join(destDir, name), content, 'utf8');
44
+ }
45
+
46
+ const root = path.resolve(projectRoot);
47
+ for (const name of ROOT_FILES) {
48
+ const src = path.join(srcDir, name);
49
+ if (!fs.existsSync(src)) continue;
50
+ fs.copyFileSync(src, path.join(root, name));
51
+ }
52
+
53
+ return destDir;
54
+ }
@@ -125,7 +125,7 @@ export function buildLocalScaffoldFiles({
125
125
  laneLabel,
126
126
  agent,
127
127
  runTarget,
128
- sdkVersion = '1.5.0',
128
+ sdkVersion = '1.5.1',
129
129
  }) {
130
130
  const sdkRange = `^${sdkVersion.split('.').slice(0, 2).join('.')}.0`;
131
131
  const migration = migrationSql(laneKey);
@@ -153,6 +153,8 @@ export function buildLocalScaffoldFiles({
153
153
  deploy_target: runTarget === 'local' ? null : runTarget,
154
154
  pty_port: 3099,
155
155
  dev_port: 8787,
156
+ ui_port: 5173,
157
+ ui: 'gorilla',
156
158
  scaffold_version: sdkVersion,
157
159
  },
158
160
  null,
@@ -169,11 +171,13 @@ Agent Sam runs a PTY on **your machine** — no accounts, no cloudflared, no IAM
169
171
  # Terminal 1 — local PTY (Agent Sam shell bridge)
170
172
  npx agentsam start-local
171
173
 
172
- # Terminal 2 — app dev server
174
+ # Terminal 2 — Gorilla Mode UI + local Worker API
173
175
  npm run dev
174
176
  npm run db:migrate # first time only
175
177
  \`\`\`
176
178
 
179
+ Open **http://localhost:5173** — pixel Gorilla shell proxies \`/api\` → Worker on :8787.
180
+
177
181
  PTY listens on \`ws://127.0.0.1:3099\`. Health: \`curl http://127.0.0.1:3099/health\`
178
182
 
179
183
  When you're ready to ship to Cloudflare or GCP:
@@ -202,7 +206,9 @@ dist/
202
206
  type: 'module',
203
207
  private: true,
204
208
  scripts: {
205
- dev: 'wrangler dev --local',
209
+ dev: 'concurrently -k "npm run dev:worker" "npm run dev:ui"',
210
+ 'dev:worker': 'wrangler dev --local --port 8787',
211
+ 'dev:ui': 'vite',
206
212
  'dev:node': 'node --watch src/dev-server.js',
207
213
  deploy: 'wrangler deploy',
208
214
  smoke: 'node ./scripts/smoke.mjs',
@@ -211,15 +217,27 @@ dist/
211
217
  },
212
218
  dependencies: {
213
219
  '@inneranimalmedia/agentsam-sdk': sdkRange,
220
+ react: '^19.0.0',
221
+ 'react-dom': '^19.0.0',
214
222
  },
215
223
  devDependencies: {
216
224
  wrangler: '^4.0.0',
225
+ vite: '^6.0.0',
226
+ '@vitejs/plugin-react': '^4.0.0',
227
+ concurrently: '^9.0.0',
217
228
  },
218
229
  },
219
230
  null,
220
231
  2,
221
232
  )}\n`,
222
233
  },
234
+ {
235
+ path: '.env',
236
+ content: `VITE_PROJECT_NAME=${projectName}
237
+ VITE_LANE_KEY=${laneKey}
238
+ VITE_AGENT=${agent}
239
+ `,
240
+ },
223
241
  {
224
242
  path: 'wrangler.toml',
225
243
  content: `name = "${projectName}"
@@ -317,13 +335,18 @@ console.log('AgentSam smoke test passed:', data);
317
335
 
318
336
  Built locally with [Agent Sam SDK](https://inneranimalmedia.com) — **${laneLabel}** lane, \`${agent}\` agent.
319
337
 
320
- ## Local-first (default)
338
+ ## Gorilla Mode (default UI)
321
339
 
322
340
  \`\`\`bash
323
341
  npm install
324
342
  npm run smoke
325
- npx agentsam start-local # Terminal 1 local PTY
326
- npm run dev # Terminal 2 — http://127.0.0.1:8787
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
327
350
  npm run db:migrate # apply local D1 schema
328
351
  \`\`\`
329
352
 
@@ -343,7 +366,7 @@ Run target selected at init: **${runTarget}**
343
366
  ];
344
367
  }
345
368
 
346
- export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.0') {
369
+ export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.1') {
347
370
  const projectName = String(body.projectName || body.project_name || 'agentsam-project')
348
371
  .trim()
349
372
  .toLowerCase()
@@ -364,9 +387,10 @@ export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.0') {
364
387
  next_steps: [
365
388
  'npm install',
366
389
  'npm run smoke',
367
- 'npx agentsam start-local',
368
390
  'npm run dev',
391
+ 'Open http://localhost:5173 — Gorilla Mode UI',
369
392
  'npm run db:migrate',
393
+ 'Optional: npx agentsam start-local',
370
394
  'When ready: npx agentsam deploy',
371
395
  ],
372
396
  };