@struere/cli 0.2.3 → 0.2.6

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.
Files changed (81) hide show
  1. package/dist/commands/build.d.ts +3 -0
  2. package/dist/commands/build.d.ts.map +1 -0
  3. package/dist/commands/build.js +61 -0
  4. package/dist/commands/build.js.map +1 -0
  5. package/dist/commands/deploy.d.ts +3 -0
  6. package/dist/commands/deploy.d.ts.map +1 -0
  7. package/dist/commands/deploy.js +145 -0
  8. package/dist/commands/deploy.js.map +1 -0
  9. package/dist/commands/dev.d.ts +3 -0
  10. package/dist/commands/dev.d.ts.map +1 -0
  11. package/dist/commands/dev.js +418 -0
  12. package/dist/commands/dev.js.map +1 -0
  13. package/dist/commands/init.d.ts +5 -0
  14. package/dist/commands/init.d.ts.map +1 -0
  15. package/dist/commands/init.js +241 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/login.d.ts +5 -0
  18. package/dist/commands/login.d.ts.map +1 -0
  19. package/dist/commands/login.js +175 -0
  20. package/dist/commands/login.js.map +1 -0
  21. package/dist/commands/logout.d.ts +3 -0
  22. package/dist/commands/logout.d.ts.map +1 -0
  23. package/dist/commands/logout.js +19 -0
  24. package/dist/commands/logout.js.map +1 -0
  25. package/dist/commands/logs.d.ts +3 -0
  26. package/dist/commands/logs.d.ts.map +1 -0
  27. package/dist/commands/logs.js +103 -0
  28. package/dist/commands/logs.js.map +1 -0
  29. package/dist/commands/state.d.ts +3 -0
  30. package/dist/commands/state.d.ts.map +1 -0
  31. package/dist/commands/state.js +71 -0
  32. package/dist/commands/state.js.map +1 -0
  33. package/dist/commands/test.d.ts +3 -0
  34. package/dist/commands/test.d.ts.map +1 -0
  35. package/dist/commands/test.js +188 -0
  36. package/dist/commands/test.js.map +1 -0
  37. package/dist/commands/validate.d.ts +3 -0
  38. package/dist/commands/validate.d.ts.map +1 -0
  39. package/dist/commands/validate.js +71 -0
  40. package/dist/commands/validate.js.map +1 -0
  41. package/dist/commands/whoami.d.ts +3 -0
  42. package/dist/commands/whoami.d.ts.map +1 -0
  43. package/dist/commands/whoami.js +69 -0
  44. package/dist/commands/whoami.js.map +1 -0
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +78 -48
  48. package/dist/index.js.map +1 -0
  49. package/dist/templates/index.d.ts +13 -0
  50. package/dist/templates/index.d.ts.map +1 -0
  51. package/dist/templates/index.js +246 -0
  52. package/dist/templates/index.js.map +1 -0
  53. package/dist/utils/agent.d.ts +3 -0
  54. package/dist/utils/agent.d.ts.map +1 -0
  55. package/dist/utils/agent.js +25 -0
  56. package/dist/utils/agent.js.map +1 -0
  57. package/dist/utils/api.d.ts +150 -0
  58. package/dist/utils/api.d.ts.map +1 -0
  59. package/dist/utils/api.js +107 -0
  60. package/dist/utils/api.js.map +1 -0
  61. package/dist/utils/config.d.ts +3 -0
  62. package/dist/utils/config.d.ts.map +1 -0
  63. package/dist/utils/config.js +43 -0
  64. package/dist/utils/config.js.map +1 -0
  65. package/dist/utils/credentials.d.ts +23 -0
  66. package/dist/utils/credentials.d.ts.map +1 -0
  67. package/dist/utils/credentials.js +51 -0
  68. package/dist/utils/credentials.js.map +1 -0
  69. package/dist/utils/project.d.ts +12 -0
  70. package/dist/utils/project.d.ts.map +1 -0
  71. package/dist/utils/project.js +24 -0
  72. package/dist/utils/project.js.map +1 -0
  73. package/dist/utils/scaffold.d.ts +17 -0
  74. package/dist/utils/scaffold.d.ts.map +1 -0
  75. package/dist/utils/scaffold.js +106 -0
  76. package/dist/utils/scaffold.js.map +1 -0
  77. package/dist/utils/validate.d.ts +3 -0
  78. package/dist/utils/validate.d.ts.map +1 -0
  79. package/dist/utils/validate.js +79 -0
  80. package/dist/utils/validate.js.map +1 -0
  81. package/package.json +2 -2
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const buildCommand: Command;
3
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC,eAAO,MAAM,YAAY,SA+DrB,CAAA"}
@@ -0,0 +1,61 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { join } from 'path';
5
+ import { loadConfig } from '../utils/config';
6
+ import { loadAgent } from '../utils/agent';
7
+ import { validateAgent } from '../utils/validate';
8
+ export const buildCommand = new Command('build')
9
+ .description('Build and validate agent for production')
10
+ .option('-o, --outdir <dir>', 'Output directory', 'dist')
11
+ .action(async (options) => {
12
+ const spinner = ora();
13
+ const cwd = process.cwd();
14
+ console.log();
15
+ console.log(chalk.bold('Building Agent'));
16
+ console.log();
17
+ spinner.start('Loading configuration');
18
+ const config = await loadConfig(cwd);
19
+ spinner.succeed('Configuration loaded');
20
+ spinner.start('Loading agent');
21
+ const agent = await loadAgent(cwd);
22
+ spinner.succeed(`Agent "${agent.name}" loaded`);
23
+ spinner.start('Validating agent');
24
+ const errors = validateAgent(agent);
25
+ if (errors.length > 0) {
26
+ spinner.fail('Validation failed');
27
+ console.log();
28
+ for (const error of errors) {
29
+ console.log(chalk.red(' ✗'), error);
30
+ }
31
+ console.log();
32
+ process.exit(1);
33
+ }
34
+ spinner.succeed('Agent validated');
35
+ spinner.start('Building');
36
+ const outdir = join(cwd, options.outdir);
37
+ const result = await Bun.build({
38
+ entrypoints: [join(cwd, 'src/agent.ts')],
39
+ outdir,
40
+ target: 'node',
41
+ minify: true,
42
+ });
43
+ if (!result.success) {
44
+ spinner.fail('Build failed');
45
+ console.log();
46
+ for (const log of result.logs) {
47
+ console.log(chalk.red(' ✗'), log.message);
48
+ }
49
+ process.exit(1);
50
+ }
51
+ spinner.succeed('Build completed');
52
+ console.log();
53
+ console.log(chalk.green('Success!'), `Built to ${chalk.cyan(options.outdir)}`);
54
+ console.log();
55
+ console.log('Output files:');
56
+ for (const output of result.outputs) {
57
+ console.log(chalk.gray(' •'), output.path.replace(cwd, '.'));
58
+ }
59
+ console.log();
60
+ });
61
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const deployCommand: Command;
3
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAWnC,eAAO,MAAM,aAAa,SA+ItB,CAAA"}
@@ -0,0 +1,145 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { join } from 'path';
5
+ import { loadConfig } from '../utils/config';
6
+ import { loadAgent } from '../utils/agent';
7
+ import { validateAgent } from '../utils/validate';
8
+ import { loadCredentials, getApiKey } from '../utils/credentials';
9
+ import { ApiClient, ApiError } from '../utils/api';
10
+ import { hasProject, loadProject } from '../utils/project';
11
+ export const deployCommand = new Command('deploy')
12
+ .description('Deploy agent to Struere Cloud')
13
+ .option('-e, --env <environment>', 'Target environment (preview, staging, production)', 'preview')
14
+ .option('--dry-run', 'Show what would be deployed without deploying')
15
+ .action(async (options) => {
16
+ const spinner = ora();
17
+ const cwd = process.cwd();
18
+ console.log();
19
+ console.log(chalk.bold('Deploying Agent'));
20
+ console.log();
21
+ if (!hasProject(cwd)) {
22
+ console.log(chalk.yellow('No struere.json found'));
23
+ console.log();
24
+ console.log(chalk.gray('Run'), chalk.cyan('struere init'), chalk.gray('to initialize this project'));
25
+ console.log();
26
+ process.exit(1);
27
+ }
28
+ const project = loadProject(cwd);
29
+ if (!project) {
30
+ console.log(chalk.red('Failed to load struere.json'));
31
+ process.exit(1);
32
+ }
33
+ console.log(chalk.gray('Agent:'), chalk.cyan(project.agent.name));
34
+ console.log();
35
+ spinner.start('Loading configuration');
36
+ await loadConfig(cwd);
37
+ spinner.succeed('Configuration loaded');
38
+ spinner.start('Loading agent');
39
+ const agent = await loadAgent(cwd);
40
+ spinner.succeed(`Agent "${agent.name}" loaded`);
41
+ spinner.start('Validating agent');
42
+ const errors = validateAgent(agent);
43
+ if (errors.length > 0) {
44
+ spinner.fail('Validation failed');
45
+ console.log();
46
+ for (const error of errors) {
47
+ console.log(chalk.red(' x'), error);
48
+ }
49
+ console.log();
50
+ process.exit(1);
51
+ }
52
+ spinner.succeed('Agent validated');
53
+ if (options.dryRun) {
54
+ console.log();
55
+ console.log(chalk.yellow('Dry run mode - no changes will be made'));
56
+ console.log();
57
+ console.log('Would deploy:');
58
+ console.log(chalk.gray(' -'), `Agent: ${chalk.cyan(agent.name)}`);
59
+ console.log(chalk.gray(' -'), `Version: ${chalk.cyan(agent.version)}`);
60
+ console.log(chalk.gray(' -'), `Environment: ${chalk.cyan(options.env)}`);
61
+ console.log(chalk.gray(' -'), `Agent ID: ${chalk.cyan(project.agentId)}`);
62
+ console.log();
63
+ return;
64
+ }
65
+ const credentials = loadCredentials();
66
+ const apiKey = getApiKey();
67
+ if (!credentials && !apiKey) {
68
+ spinner.fail('Not authenticated');
69
+ console.log();
70
+ console.log(chalk.gray('Run'), chalk.cyan('struere login'), chalk.gray('to authenticate'));
71
+ console.log(chalk.gray('Or set'), chalk.cyan('STRUERE_API_KEY'), chalk.gray('environment variable'));
72
+ console.log();
73
+ process.exit(1);
74
+ }
75
+ spinner.start('Building agent bundle');
76
+ const result = await Bun.build({
77
+ entrypoints: [join(cwd, 'src', 'agent.ts')],
78
+ target: 'browser',
79
+ minify: true
80
+ });
81
+ if (!result.success) {
82
+ spinner.fail('Build failed');
83
+ console.log();
84
+ for (const log of result.logs) {
85
+ console.log(chalk.red(' -'), log);
86
+ }
87
+ console.log();
88
+ process.exit(1);
89
+ }
90
+ const bundle = await result.outputs[0].text();
91
+ spinner.succeed(`Bundle created (${formatBytes(bundle.length)})`);
92
+ spinner.start(`Deploying to ${options.env}`);
93
+ try {
94
+ const api = new ApiClient();
95
+ const { deployment } = await api.deployAgent(project.agentId, {
96
+ bundle,
97
+ version: agent.version,
98
+ environment: options.env,
99
+ metadata: {
100
+ modelProvider: agent.model?.provider || 'anthropic',
101
+ modelName: agent.model?.name || 'claude-sonnet-4-20250514',
102
+ toolCount: agent.tools?.length || 0,
103
+ bundleSize: bundle.length
104
+ }
105
+ });
106
+ spinner.succeed(`Deployed to ${options.env}`);
107
+ console.log();
108
+ console.log(chalk.green('Success!'), 'Agent deployed');
109
+ console.log();
110
+ console.log('Deployment details:');
111
+ console.log(chalk.gray(' -'), `ID: ${chalk.cyan(deployment.id)}`);
112
+ console.log(chalk.gray(' -'), `Version: ${chalk.cyan(deployment.version)}`);
113
+ console.log(chalk.gray(' -'), `Environment: ${chalk.cyan(deployment.environment)}`);
114
+ console.log(chalk.gray(' -'), `URL: ${chalk.cyan(deployment.url)}`);
115
+ console.log();
116
+ console.log(chalk.gray('Test your agent:'));
117
+ console.log(chalk.gray(' $'), chalk.cyan(`curl -X POST ${deployment.url}/chat -H "Authorization: Bearer YOUR_API_KEY" -d '{"message": "Hello"}'`));
118
+ console.log();
119
+ }
120
+ catch (error) {
121
+ spinner.fail('Deployment failed');
122
+ console.log();
123
+ if (error instanceof ApiError) {
124
+ console.log(chalk.red('Error:'), error.message);
125
+ if (error.status === 401) {
126
+ console.log();
127
+ console.log(chalk.gray('Try running'), chalk.cyan('struere login'), chalk.gray('to re-authenticate'));
128
+ }
129
+ }
130
+ else {
131
+ console.log(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
132
+ }
133
+ console.log();
134
+ process.exit(1);
135
+ }
136
+ });
137
+ function formatBytes(bytes) {
138
+ if (bytes === 0)
139
+ return '0 B';
140
+ const k = 1024;
141
+ const sizes = ['B', 'KB', 'MB', 'GB'];
142
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
143
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
144
+ }
145
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const devCommand: Command;
3
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAYnC,eAAO,MAAM,UAAU,SAwDnB,CAAA"}
@@ -0,0 +1,418 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import chokidar from 'chokidar';
5
+ import { join } from 'path';
6
+ import { loadConfig } from '../utils/config';
7
+ import { loadAgent } from '../utils/agent';
8
+ import { loadCredentials, getApiKey } from '../utils/credentials';
9
+ import { getSyncUrl } from '../utils/api';
10
+ import { hasProject, loadProject } from '../utils/project';
11
+ export const devCommand = new Command('dev')
12
+ .description('Start development server with cloud sync')
13
+ .option('-p, --port <port>', 'Port to run on', '3000')
14
+ .option('-c, --channel <channel>', 'Channel to open (web, api)', 'web')
15
+ .option('--no-open', 'Do not open browser')
16
+ .option('--local', 'Run locally without cloud sync')
17
+ .action(async (options) => {
18
+ const spinner = ora();
19
+ const cwd = process.cwd();
20
+ console.log();
21
+ console.log(chalk.bold('Struere Dev Server'));
22
+ console.log();
23
+ if (!hasProject(cwd)) {
24
+ console.log(chalk.yellow('No struere.json found'));
25
+ console.log();
26
+ console.log(chalk.gray('Run'), chalk.cyan('struere init'), chalk.gray('to initialize this project'));
27
+ console.log();
28
+ process.exit(1);
29
+ }
30
+ const project = loadProject(cwd);
31
+ if (!project) {
32
+ console.log(chalk.red('Failed to load struere.json'));
33
+ process.exit(1);
34
+ }
35
+ console.log(chalk.gray('Agent:'), chalk.cyan(project.agent.name));
36
+ console.log();
37
+ spinner.start('Loading configuration');
38
+ const config = await loadConfig(cwd);
39
+ const port = parseInt(options.port) || config.port || 3000;
40
+ spinner.succeed('Configuration loaded');
41
+ spinner.start('Loading agent');
42
+ let agent = await loadAgent(cwd);
43
+ spinner.succeed(`Agent "${agent.name}" loaded`);
44
+ const credentials = loadCredentials();
45
+ const apiKey = getApiKey();
46
+ if (!credentials && !apiKey) {
47
+ spinner.fail('Not logged in');
48
+ console.log();
49
+ console.log(chalk.gray('Run'), chalk.cyan('struere login'), chalk.gray('to authenticate'));
50
+ console.log();
51
+ process.exit(1);
52
+ }
53
+ await runCloudDev(agent, project, cwd, port, options, spinner);
54
+ });
55
+ async function runCloudDev(agent, project, cwd, port, options, spinner) {
56
+ const credentials = loadCredentials();
57
+ const apiKey = getApiKey();
58
+ spinner.start('Connecting to Struere Cloud');
59
+ const syncUrl = getSyncUrl();
60
+ const ws = new WebSocket(`${syncUrl}/v1/dev/sync`);
61
+ let cloudUrl = null;
62
+ let sessionId = null;
63
+ let isConnected = false;
64
+ ws.onopen = () => {
65
+ ws.send(JSON.stringify({
66
+ type: 'auth',
67
+ apiKey: apiKey || credentials?.token
68
+ }));
69
+ };
70
+ ws.onmessage = async (event) => {
71
+ const data = JSON.parse(event.data);
72
+ switch (data.type) {
73
+ case 'authenticated':
74
+ spinner.text = 'Syncing agent';
75
+ const bundle = await bundleAgent(cwd);
76
+ const configHash = hashString(bundle);
77
+ ws.send(JSON.stringify({
78
+ type: 'sync',
79
+ agentId: project.agentId,
80
+ agentSlug: project.agent.slug,
81
+ bundle,
82
+ configHash
83
+ }));
84
+ break;
85
+ case 'synced':
86
+ isConnected = true;
87
+ cloudUrl = data.url || null;
88
+ sessionId = data.agentId || null;
89
+ spinner.succeed('Connected to Struere Cloud');
90
+ console.log();
91
+ console.log(chalk.gray('Mode:'), chalk.green('Cloud'));
92
+ console.log(chalk.green('Agent running at'), chalk.cyan(cloudUrl));
93
+ console.log(chalk.green('Local server at'), chalk.cyan(`http://localhost:${port}`));
94
+ console.log();
95
+ spinner.start('Watching for changes');
96
+ break;
97
+ case 'log':
98
+ const logColor = data.level === 'error' ? chalk.red
99
+ : data.level === 'warn' ? chalk.yellow
100
+ : data.level === 'debug' ? chalk.gray
101
+ : chalk.blue;
102
+ spinner.stop();
103
+ console.log(logColor(`[${data.level}]`), data.message);
104
+ spinner.start('Watching for changes');
105
+ break;
106
+ case 'error':
107
+ spinner.fail(`Cloud error: ${data.message}`);
108
+ if (data.code === 'INVALID_API_KEY' || data.code === 'NOT_AUTHENTICATED') {
109
+ console.log();
110
+ console.log(chalk.gray('Run'), chalk.cyan('struere login'), chalk.gray('to authenticate'));
111
+ }
112
+ break;
113
+ }
114
+ };
115
+ ws.onerror = () => {
116
+ spinner.fail('WebSocket error');
117
+ console.log(chalk.red('Connection error'));
118
+ };
119
+ ws.onclose = () => {
120
+ if (isConnected) {
121
+ spinner.stop();
122
+ console.log(chalk.yellow('Disconnected from cloud'));
123
+ }
124
+ };
125
+ const server = Bun.serve({
126
+ port,
127
+ async fetch(req) {
128
+ const url = new URL(req.url);
129
+ if (url.pathname === '/health') {
130
+ return Response.json({
131
+ status: 'ok',
132
+ agent: agent.name,
133
+ mode: 'cloud',
134
+ cloudUrl
135
+ });
136
+ }
137
+ if (url.pathname === '/api/chat' && req.method === 'POST') {
138
+ if (!cloudUrl || !sessionId) {
139
+ return Response.json({ error: 'Not connected to cloud' }, { status: 503 });
140
+ }
141
+ const body = await req.json();
142
+ const response = await fetch(`${process.env.STRUERE_GATEWAY_URL || 'https://gateway.struere.dev'}/v1/dev/${sessionId}/chat`, {
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/json',
146
+ 'Authorization': `Bearer ${apiKey || credentials?.token}`
147
+ },
148
+ body: JSON.stringify(body)
149
+ });
150
+ if (body.stream) {
151
+ return new Response(response.body, {
152
+ headers: {
153
+ 'Content-Type': 'text/event-stream',
154
+ 'Cache-Control': 'no-cache',
155
+ 'Connection': 'keep-alive'
156
+ }
157
+ });
158
+ }
159
+ const responseData = await response.json();
160
+ return Response.json(responseData);
161
+ }
162
+ if (url.pathname === '/' && options.channel === 'web') {
163
+ return new Response(getDevHtml(agent.name, 'cloud', cloudUrl), {
164
+ headers: { 'Content-Type': 'text/html' },
165
+ });
166
+ }
167
+ return new Response('Not Found', { status: 404 });
168
+ }
169
+ });
170
+ if (options.channel === 'web' && options.open) {
171
+ const openUrl = `http://localhost:${port}`;
172
+ if (process.platform === 'darwin') {
173
+ Bun.spawn(['open', openUrl]);
174
+ }
175
+ else if (process.platform === 'linux') {
176
+ Bun.spawn(['xdg-open', openUrl]);
177
+ }
178
+ }
179
+ const watcher = chokidar.watch([join(cwd, 'src'), join(cwd, 'struere.config.ts')], {
180
+ ignoreInitial: true,
181
+ ignored: /node_modules/,
182
+ });
183
+ watcher.on('change', async (path) => {
184
+ spinner.text = `Syncing (${path.replace(cwd, '.')})`;
185
+ try {
186
+ agent = await loadAgent(cwd);
187
+ const bundle = await bundleAgent(cwd);
188
+ const configHash = hashString(bundle);
189
+ if (ws.readyState === WebSocket.OPEN) {
190
+ ws.send(JSON.stringify({
191
+ type: 'sync',
192
+ agentId: project.agentId,
193
+ agentSlug: project.agent.slug,
194
+ bundle,
195
+ configHash
196
+ }));
197
+ }
198
+ }
199
+ catch (error) {
200
+ spinner.fail(`Sync failed: ${error}`);
201
+ spinner.start('Watching for changes');
202
+ }
203
+ });
204
+ process.on('SIGINT', () => {
205
+ console.log();
206
+ spinner.stop();
207
+ if (ws.readyState === WebSocket.OPEN) {
208
+ ws.send(JSON.stringify({ type: 'unsync' }));
209
+ ws.close();
210
+ }
211
+ watcher.close();
212
+ server.stop();
213
+ console.log(chalk.gray('Server stopped'));
214
+ process.exit(0);
215
+ });
216
+ }
217
+ async function bundleAgent(cwd) {
218
+ const result = await Bun.build({
219
+ entrypoints: [join(cwd, 'src', 'agent.ts')],
220
+ target: 'browser',
221
+ minify: true
222
+ });
223
+ if (!result.success) {
224
+ throw new Error('Bundle failed: ' + result.logs.join('\n'));
225
+ }
226
+ return await result.outputs[0].text();
227
+ }
228
+ function hashString(str) {
229
+ let hash = 0;
230
+ for (let i = 0; i < str.length; i++) {
231
+ const char = str.charCodeAt(i);
232
+ hash = ((hash << 5) - hash) + char;
233
+ hash = hash & hash;
234
+ }
235
+ return Math.abs(hash).toString(16);
236
+ }
237
+ function getDevHtml(agentName, mode, cloudUrl) {
238
+ const modeLabel = mode === 'cloud'
239
+ ? `<span style="color: #22c55e;">Cloud</span>${cloudUrl ? ` - <a href="${cloudUrl}" target="_blank" style="color: #60a5fa;">${cloudUrl}</a>` : ''}`
240
+ : '<span style="color: #eab308;">Local</span>';
241
+ return `<!DOCTYPE html>
242
+ <html lang="en">
243
+ <head>
244
+ <meta charset="UTF-8">
245
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
246
+ <title>${agentName} - Dev</title>
247
+ <style>
248
+ * { box-sizing: border-box; margin: 0; padding: 0; }
249
+ body { font-family: system-ui, -apple-system, sans-serif; background: #0a0a0a; color: #fafafa; height: 100vh; display: flex; flex-direction: column; }
250
+ header { padding: 1rem; border-bottom: 1px solid #333; display: flex; justify-content: space-between; align-items: center; }
251
+ header h1 { font-size: 1rem; font-weight: 500; }
252
+ header .mode { font-size: 0.875rem; }
253
+ header a { text-decoration: none; }
254
+ #messages { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.75rem; }
255
+ .message { max-width: 80%; padding: 0.75rem 1rem; border-radius: 0.75rem; line-height: 1.5; white-space: pre-wrap; }
256
+ .message.user { align-self: flex-end; background: #2563eb; }
257
+ .message.assistant { align-self: flex-start; background: #27272a; }
258
+ .message.tool { align-self: flex-start; background: #1e3a5f; font-family: monospace; font-size: 0.875rem; border-left: 3px solid #3b82f6; }
259
+ .message.streaming { opacity: 0.9; }
260
+ form { padding: 1rem; border-top: 1px solid #333; display: flex; gap: 0.5rem; }
261
+ input { flex: 1; padding: 0.75rem 1rem; background: #18181b; border: 1px solid #333; border-radius: 0.5rem; color: #fafafa; font-size: 1rem; outline: none; }
262
+ input:focus { border-color: #2563eb; }
263
+ input:disabled { opacity: 0.5; }
264
+ button { padding: 0.75rem 1.5rem; background: #2563eb; border: none; border-radius: 0.5rem; color: white; font-size: 1rem; cursor: pointer; }
265
+ button:hover { background: #1d4ed8; }
266
+ button:disabled { opacity: 0.5; cursor: not-allowed; }
267
+ .toggle-container { padding: 0.5rem 1rem; display: flex; align-items: center; gap: 0.5rem; border-top: 1px solid #333; }
268
+ .toggle-container label { font-size: 0.875rem; color: #888; }
269
+ .toggle-container input[type="checkbox"] { width: 1rem; height: 1rem; }
270
+ </style>
271
+ </head>
272
+ <body>
273
+ <header>
274
+ <h1>${agentName}</h1>
275
+ <span class="mode">${modeLabel}</span>
276
+ </header>
277
+ <div id="messages"></div>
278
+ <div class="toggle-container">
279
+ <input type="checkbox" id="stream-toggle" checked />
280
+ <label for="stream-toggle">Enable streaming</label>
281
+ </div>
282
+ <form id="chat-form">
283
+ <input type="text" id="input" placeholder="Type a message..." autocomplete="off" />
284
+ <button type="submit">Send</button>
285
+ </form>
286
+ <script>
287
+ const messages = document.getElementById('messages');
288
+ const form = document.getElementById('chat-form');
289
+ const input = document.getElementById('input');
290
+ const button = form.querySelector('button');
291
+ const streamToggle = document.getElementById('stream-toggle');
292
+ let conversationId = null;
293
+ let isProcessing = false;
294
+
295
+ function addMessage(role, content, isStreaming = false) {
296
+ const div = document.createElement('div');
297
+ div.className = 'message ' + role + (isStreaming ? ' streaming' : '');
298
+ div.textContent = content;
299
+ messages.appendChild(div);
300
+ messages.scrollTop = messages.scrollHeight;
301
+ return div;
302
+ }
303
+
304
+ function setProcessing(processing) {
305
+ isProcessing = processing;
306
+ input.disabled = processing;
307
+ button.disabled = processing;
308
+ }
309
+
310
+ async function sendWithStreaming(message) {
311
+ const assistantDiv = addMessage('assistant', '', true);
312
+
313
+ const response = await fetch('/api/chat', {
314
+ method: 'POST',
315
+ headers: { 'Content-Type': 'application/json' },
316
+ body: JSON.stringify({ message, conversationId, stream: true }),
317
+ });
318
+
319
+ const reader = response.body.getReader();
320
+ const decoder = new TextDecoder();
321
+ let buffer = '';
322
+ let fullText = '';
323
+
324
+ while (true) {
325
+ const { done, value } = await reader.read();
326
+ if (done) break;
327
+
328
+ buffer += decoder.decode(value, { stream: true });
329
+ const lines = buffer.split('\\n');
330
+ buffer = lines.pop() || '';
331
+
332
+ for (const line of lines) {
333
+ if (line.startsWith('data: ')) {
334
+ try {
335
+ const data = JSON.parse(line.slice(6));
336
+
337
+ if (data.conversationId) {
338
+ conversationId = data.conversationId;
339
+ }
340
+
341
+ if (data.type === 'text-delta' && (data.textDelta || data.content)) {
342
+ fullText += data.textDelta || data.content;
343
+ assistantDiv.textContent = fullText;
344
+ messages.scrollTop = messages.scrollHeight;
345
+ } else if (data.type === 'tool-call-start') {
346
+ addMessage('tool', 'Calling tool: ' + (data.toolName || data.toolCall?.name));
347
+ } else if (data.type === 'tool-result') {
348
+ const resultText = typeof data.toolResult === 'string'
349
+ ? data.toolResult
350
+ : JSON.stringify(data.toolResult || data.toolCall?.result, null, 2);
351
+ addMessage('tool', 'Result: ' + resultText);
352
+ } else if (data.type === 'finish') {
353
+ assistantDiv.classList.remove('streaming');
354
+ } else if (data.type === 'error') {
355
+ assistantDiv.textContent = 'Error: ' + (data.error || data.message);
356
+ assistantDiv.classList.remove('streaming');
357
+ }
358
+ } catch (e) {}
359
+ }
360
+ }
361
+ }
362
+
363
+ if (!fullText) {
364
+ assistantDiv.remove();
365
+ }
366
+ }
367
+
368
+ async function sendWithoutStreaming(message) {
369
+ const res = await fetch('/api/chat', {
370
+ method: 'POST',
371
+ headers: { 'Content-Type': 'application/json' },
372
+ body: JSON.stringify({ message, conversationId }),
373
+ });
374
+ const data = await res.json();
375
+ conversationId = data.conversationId;
376
+
377
+ if (data.toolCalls && data.toolCalls.length > 0) {
378
+ for (const tc of data.toolCalls) {
379
+ const resultText = typeof tc.result === 'string'
380
+ ? tc.result
381
+ : JSON.stringify(tc.result, null, 2);
382
+ addMessage('tool', tc.name + ': ' + resultText);
383
+ }
384
+ }
385
+
386
+ addMessage('assistant', data.response || data.content);
387
+ }
388
+
389
+ form.addEventListener('submit', async (e) => {
390
+ e.preventDefault();
391
+ if (isProcessing) return;
392
+
393
+ const message = input.value.trim();
394
+ if (!message) return;
395
+
396
+ addMessage('user', message);
397
+ input.value = '';
398
+ setProcessing(true);
399
+
400
+ try {
401
+ if (streamToggle.checked) {
402
+ await sendWithStreaming(message);
403
+ } else {
404
+ await sendWithoutStreaming(message);
405
+ }
406
+ } catch (err) {
407
+ addMessage('assistant', 'Error: ' + err.message);
408
+ } finally {
409
+ setProcessing(false);
410
+ }
411
+ });
412
+
413
+ input.focus();
414
+ </script>
415
+ </body>
416
+ </html>`;
417
+ }
418
+ //# sourceMappingURL=dev.js.map