@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 +2 -0
- package/package.json +2 -1
- package/src/cli.js +20 -3
- package/src/commands/http-server.js +97 -0
- package/src/commands/tunnel.js +232 -0
- package/src/lib/gorilla-template.js +54 -0
- package/src/lib/local-scaffold.js +32 -8
- package/src/ui/splash-xterm.js +290 -0
- package/src/ui/splash.js +426 -0
- package/src/ui/terminal-lanes.css +162 -0
- package/src/ui/theme.js +315 -0
- package/templates/gorilla-shell/App.tsx +639 -0
- package/templates/gorilla-shell/README.md +22 -0
- package/templates/gorilla-shell/index.html +15 -0
- package/templates/gorilla-shell/main.jsx +9 -0
- package/templates/gorilla-shell/package.json +19 -0
- package/templates/gorilla-shell/vite.config.js +21 -0
- package/test/smoke.mjs +16 -0
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.
|
|
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
|
-
|
|
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.
|
|
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 —
|
|
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: '
|
|
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
|
-
##
|
|
338
|
+
## Gorilla Mode (default UI)
|
|
321
339
|
|
|
322
340
|
\`\`\`bash
|
|
323
341
|
npm install
|
|
324
342
|
npm run smoke
|
|
325
|
-
|
|
326
|
-
|
|
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.
|
|
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
|
};
|