agent-stage 0.2.14 → 0.2.17

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 (68) hide show
  1. package/dist/commands/guide.js +5 -5
  2. package/dist/commands/init.d.ts +1 -1
  3. package/dist/commands/init.js +94 -138
  4. package/dist/commands/page/add.js +5 -40
  5. package/dist/commands/run/exec.js +1 -1
  6. package/dist/commands/run/inspect.js +1 -1
  7. package/dist/commands/run/watch.js +1 -1
  8. package/dist/commands/serve.d.ts +2 -0
  9. package/dist/commands/serve.js +238 -0
  10. package/dist/commands/status.d.ts +1 -1
  11. package/dist/commands/status.js +41 -40
  12. package/dist/commands/stop.d.ts +1 -1
  13. package/dist/commands/stop.js +26 -44
  14. package/dist/index.js +16 -30
  15. package/dist/utils/agent-helper.js +5 -5
  16. package/dist/utils/paths.js +5 -5
  17. package/dist/utils/tunnel.d.ts +1 -1
  18. package/dist/utils/tunnel.js +1 -1
  19. package/package.json +8 -5
  20. package/dist/commands/dev/index.d.ts +0 -2
  21. package/dist/commands/dev/index.js +0 -11
  22. package/dist/commands/dev/init.d.ts +0 -2
  23. package/dist/commands/dev/init.js +0 -215
  24. package/dist/commands/dev/start.d.ts +0 -2
  25. package/dist/commands/dev/start.js +0 -145
  26. package/dist/commands/dev/status.d.ts +0 -2
  27. package/dist/commands/dev/status.js +0 -55
  28. package/dist/commands/dev/stop.d.ts +0 -2
  29. package/dist/commands/dev/stop.js +0 -45
  30. package/dist/commands/exec.d.ts +0 -2
  31. package/dist/commands/exec.js +0 -75
  32. package/dist/commands/inspect.d.ts +0 -2
  33. package/dist/commands/inspect.js +0 -62
  34. package/dist/commands/ls.d.ts +0 -2
  35. package/dist/commands/ls.js +0 -132
  36. package/dist/commands/restart.d.ts +0 -2
  37. package/dist/commands/restart.js +0 -90
  38. package/dist/commands/rm-page.d.ts +0 -2
  39. package/dist/commands/rm-page.js +0 -32
  40. package/dist/commands/start.d.ts +0 -2
  41. package/dist/commands/start.js +0 -82
  42. package/dist/commands/watch.d.ts +0 -2
  43. package/dist/commands/watch.js +0 -54
  44. package/template/components.json +0 -17
  45. package/template/index.html +0 -13
  46. package/template/package.json +0 -41
  47. package/template/postcss.config.js +0 -6
  48. package/template/src/components/PageRenderer.tsx +0 -108
  49. package/template/src/components/bridge-state-provider.tsx +0 -87
  50. package/template/src/components/ui/button.tsx +0 -55
  51. package/template/src/components/ui/card.tsx +0 -78
  52. package/template/src/components/ui/input.tsx +0 -24
  53. package/template/src/index.css +0 -59
  54. package/template/src/lib/bridge.ts +0 -53
  55. package/template/src/lib/utils.ts +0 -6
  56. package/template/src/main.tsx +0 -23
  57. package/template/src/pages/counter/store.json +0 -8
  58. package/template/src/pages/counter/ui.json +0 -108
  59. package/template/src/pages/test-page/store.json +0 -8
  60. package/template/src/routeTree.gen.ts +0 -77
  61. package/template/src/routes/__root.tsx +0 -11
  62. package/template/src/routes/counter.tsx +0 -19
  63. package/template/src/routes/index.tsx +0 -46
  64. package/template/src/vite-env.d.ts +0 -1
  65. package/template/tailwind.config.js +0 -53
  66. package/template/tsconfig.json +0 -25
  67. package/template/tsconfig.node.json +0 -11
  68. package/template/vite.config.ts +0 -22
@@ -17,9 +17,9 @@ const guides = {
17
17
  explanation: 'Creates a page with UI and initial state'
18
18
  },
19
19
  {
20
- scenario: '3. Start dev server',
21
- command: 'agentstage dev start',
22
- explanation: 'Starts the development server'
20
+ scenario: '3. Start page runtime',
21
+ command: 'agentstage serve mypage',
22
+ explanation: 'Starts the page runtime process'
23
23
  }
24
24
  ],
25
25
  commonErrors: [
@@ -160,8 +160,8 @@ push/pop: Navigate forward/back`
160
160
  },
161
161
  {
162
162
  error: 'Runtime is not running',
163
- cause: 'Dev server is not started',
164
- fix: 'Run: agentstage dev start'
163
+ cause: 'Page runtime is not started',
164
+ fix: 'Run: agentstage serve <pageId>'
165
165
  }
166
166
  ]
167
167
  },
@@ -1,2 +1,2 @@
1
- import { Command } from 'commander';
1
+ import { Command } from "commander";
2
2
  export declare const initCommand: Command;
@@ -1,146 +1,156 @@
1
- import { Command } from 'commander';
2
- import * as p from '@clack/prompts';
3
- import consola from 'consola';
4
- import c from 'picocolors';
5
- import { execa } from 'execa';
6
- import { mkdir, writeFile, readdir, readFile, cp } from 'fs/promises';
7
- import { existsSync } from 'fs';
8
- import { resolve, join, dirname } from 'pathe';
9
- import { homedir } from 'os';
10
- import { fileURLToPath } from 'url';
11
- import { setWorkspaceDir } from '../utils/paths.js';
12
- const PROJECT_NAME = 'webapp';
13
- // Get the template directory path (works in both dev and prod)
14
- function getTemplateDir() {
15
- const currentFilePath = fileURLToPath(import.meta.url);
16
- const currentDir = dirname(currentFilePath);
17
- // Production: template is next to dist
18
- const prodPath = join(currentDir, '..', '..', 'template');
19
- // Development: template is in the source
20
- const devPath = join(currentDir, '..', '..', '..', 'template');
21
- if (existsSync(prodPath)) {
22
- return prodPath;
1
+ import { Command } from "commander";
2
+ import * as p from "@clack/prompts";
3
+ import consola from "consola";
4
+ import c from "picocolors";
5
+ import { execa } from "execa";
6
+ import { mkdir, readdir, writeFile } from "fs/promises";
7
+ import { existsSync } from "fs";
8
+ import { resolve, join } from "pathe";
9
+ import { homedir } from "os";
10
+ import { setWorkspaceDir } from "../utils/paths.js";
11
+ import { checkCloudflared, printInstallInstructions, } from "../utils/cloudflared.js";
12
+ const PROJECT_NAME = "webapp";
13
+ const workspaceDependencies = {
14
+ "@agentstage/render": "^0.2.2",
15
+ "@agentstage/bridge": "^0.1.2",
16
+ react: "^19.0.0",
17
+ "react-dom": "^19.0.0",
18
+ zod: "^3.23.0",
19
+ };
20
+ export const initCommand = new Command("init")
21
+ .description("Initialize a new Agentstage runtime workspace")
22
+ .option("-y, --yes", "Use default settings (non-interactive)", false)
23
+ .option("--skip-cloudflared-check", "Skip cloudflared installation check", false)
24
+ .action(async (options) => {
25
+ const useDefault = options.yes;
26
+ try {
27
+ await execa("bun", ["--version"], { stdio: "pipe" });
23
28
  }
24
- if (existsSync(devPath)) {
25
- return devPath;
29
+ catch {
30
+ consola.error("Bun is required to initialize workspace.");
31
+ consola.info("Install Bun: https://bun.sh/docs/installation");
32
+ process.exit(1);
26
33
  }
27
- // Fallback: try current working directory relative paths
28
- const cwdProdPath = join(process.cwd(), 'packages', 'cli', 'template');
29
- if (existsSync(cwdProdPath)) {
30
- return cwdProdPath;
34
+ if (!options.skipCloudflaredCheck) {
35
+ const cloudflaredInfo = await checkCloudflared();
36
+ if (!cloudflaredInfo.installed) {
37
+ printInstallInstructions(cloudflaredInfo);
38
+ const shouldContinue = await p.confirm({
39
+ message: "Continue with initialization? (You can install cloudflared later)",
40
+ initialValue: true,
41
+ });
42
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
43
+ consola.info("Cancelled");
44
+ process.exit(0);
45
+ }
46
+ }
47
+ else {
48
+ consola.success(`Cloudflare Tunnel available: ${c.dim(cloudflaredInfo.version)}`);
49
+ }
31
50
  }
32
- throw new Error('Template directory not found. Please ensure the CLI is properly installed.');
33
- }
34
- export const initCommand = new Command('init')
35
- .description('Initialize a new Agentstage project')
36
- .option('-y, --yes', 'Use default settings (non-interactive)', false)
37
- .action(async (options) => {
38
- const name = PROJECT_NAME;
39
- const useDefault = options.yes;
40
- // 1. 选择工作目录模式
41
51
  let locationMode;
42
52
  if (useDefault) {
43
- locationMode = 'default';
53
+ locationMode = "default";
44
54
  }
45
55
  else {
46
56
  const result = await p.select({
47
- message: 'Where to store the project?',
57
+ message: "Where to store the workspace?",
48
58
  options: [
49
59
  {
50
- value: 'default',
51
- label: `Default (~/.agentstage/${name})`,
52
- hint: 'Recommended'
60
+ value: "default",
61
+ label: `Default (~/.agentstage/${PROJECT_NAME})`,
62
+ hint: "Recommended",
53
63
  },
54
64
  {
55
- value: 'current',
56
- label: 'Current directory (./.agentstage)'
65
+ value: "current",
66
+ label: "Current directory (./.agentstage)",
57
67
  },
58
68
  {
59
- value: 'custom',
60
- label: 'Custom path'
69
+ value: "custom",
70
+ label: "Custom path",
61
71
  },
62
72
  ],
63
73
  });
64
74
  if (p.isCancel(result)) {
65
- consola.info('Cancelled');
75
+ consola.info("Cancelled");
66
76
  return;
67
77
  }
68
78
  locationMode = result;
69
79
  }
70
- // 2. 确定目标目录
71
80
  let targetDir;
72
81
  switch (locationMode) {
73
- case 'default':
74
- targetDir = join(homedir(), '.agentstage', name);
82
+ case "default":
83
+ targetDir = join(homedir(), ".agentstage", PROJECT_NAME);
75
84
  break;
76
- case 'current':
77
- targetDir = join(process.cwd(), '.agentstage');
85
+ case "current":
86
+ targetDir = join(process.cwd(), ".agentstage");
78
87
  break;
79
- case 'custom':
88
+ case "custom": {
80
89
  const customPath = await p.text({
81
- message: 'Enter custom path:',
82
- placeholder: '/path/to/project',
90
+ message: "Enter custom path:",
91
+ placeholder: "/path/to/workspace",
83
92
  validate: (value) => {
84
- if (!value || value.trim() === '') {
85
- return 'Path is required';
93
+ if (!value || value.trim() === "") {
94
+ return "Path is required";
86
95
  }
87
96
  },
88
97
  });
89
98
  if (p.isCancel(customPath)) {
90
- consola.info('Cancelled');
99
+ consola.info("Cancelled");
91
100
  return;
92
101
  }
93
102
  targetDir = resolve(customPath);
94
103
  break;
104
+ }
95
105
  default:
96
- targetDir = join(homedir(), '.agentstage', name);
106
+ targetDir = join(homedir(), ".agentstage", PROJECT_NAME);
97
107
  }
98
- // 3. 检查目录
99
108
  if (existsSync(targetDir)) {
100
109
  const files = await readdirSafe(targetDir);
101
110
  if (files.length > 0) {
102
- // 项目已存在,提示并退出
103
111
  console.log();
104
- consola.info('Project already initialized!');
112
+ consola.info("Workspace already initialized!");
105
113
  console.log(` Location: ${c.cyan(targetDir)}`);
106
114
  console.log();
107
115
  console.log(` cd ${c.cyan(targetDir)}`);
108
- console.log(` ${c.cyan('agentstage start')}`);
116
+ console.log(` ${c.cyan("agentstage serve <pageId>")}`);
109
117
  console.log();
118
+ await setWorkspaceDir(targetDir);
110
119
  return;
111
120
  }
112
121
  }
113
- // 4. 保存工作目录配置
114
122
  await setWorkspaceDir(targetDir);
115
123
  const s = p.spinner();
116
124
  try {
117
- // 5. 复制模板文件
118
- s.start('Creating project from template...');
119
- const templateDir = getTemplateDir();
120
- await mkdir(targetDir, { recursive: true });
121
- await copyTemplateFiles(templateDir, targetDir);
122
- s.stop('Project template copied');
123
- // 6. 更新 package.json 中的 workspace 依赖
124
- s.start('Configuring project...');
125
- await configurePackageJson(targetDir);
126
- s.stop('Project configured');
127
- // 7. 安装依赖
128
- s.start('Installing dependencies...');
129
- await installDependencies(targetDir);
130
- s.stop('Dependencies installed');
131
- // 完成
132
- console.log();
133
- consola.success('Project created successfully!');
125
+ s.start("Creating workspace structure...");
126
+ await mkdir(join(targetDir, "pages"), { recursive: true });
127
+ await mkdir(join(targetDir, ".agentstage"), { recursive: true });
128
+ const packageJson = {
129
+ name: "agentstage-workspace",
130
+ private: true,
131
+ version: "0.0.0",
132
+ type: "module",
133
+ dependencies: workspaceDependencies,
134
+ };
135
+ await writeFile(join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2));
136
+ s.stop("Workspace created");
137
+ s.start("Installing dependencies with Bun...");
138
+ await execa("bun", ["install"], { cwd: targetDir, stdio: "pipe" });
139
+ s.stop("Dependencies installed");
134
140
  console.log();
141
+ consola.success("Workspace initialized successfully");
135
142
  console.log(` Location: ${c.cyan(targetDir)}`);
143
+ console.log(` Pages: ${c.cyan("pages/<pageId>/ui.json")}`);
136
144
  console.log();
137
145
  console.log(` cd ${c.cyan(targetDir)}`);
138
- console.log(` ${c.cyan('agentstage start')}`);
146
+ console.log(` ${c.cyan("agentstage page add counter")}`);
147
+ console.log(` ${c.cyan("agentstage serve counter")}`);
139
148
  console.log();
140
149
  }
141
150
  catch (error) {
142
- s.stop('Failed to create project');
143
- consola.error(error.message);
151
+ s.stop("Failed to initialize workspace");
152
+ const message = error instanceof Error ? error.message : "Unknown initialization error";
153
+ consola.error(message);
144
154
  process.exit(1);
145
155
  }
146
156
  });
@@ -152,57 +162,3 @@ async function readdirSafe(dir) {
152
162
  return [];
153
163
  }
154
164
  }
155
- async function copyTemplateFiles(templateDir, targetDir) {
156
- const entries = await readdir(templateDir, { withFileTypes: true });
157
- for (const entry of entries) {
158
- const srcPath = join(templateDir, entry.name);
159
- const destPath = join(targetDir, entry.name);
160
- if (entry.isDirectory()) {
161
- await mkdir(destPath, { recursive: true });
162
- await copyTemplateFiles(srcPath, destPath);
163
- }
164
- else {
165
- await cp(srcPath, destPath);
166
- }
167
- }
168
- }
169
- async function configurePackageJson(targetDir) {
170
- const packageJsonPath = join(targetDir, 'package.json');
171
- const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
172
- // Check if we're in the monorepo by looking for packages from the CLI location
173
- const currentFilePath = fileURLToPath(import.meta.url);
174
- const localBridgePath = resolve(join(dirname(currentFilePath), '..', '..', '..', 'bridge'));
175
- const localRenderPath = resolve(join(dirname(currentFilePath), '..', '..', '..', 'render'));
176
- const isDev = existsSync(localBridgePath) && existsSync(localRenderPath);
177
- if (isDev) {
178
- // In dev mode, use file: protocol to reference the local packages
179
- packageJson.dependencies['@agentstage/bridge'] = `file:${localBridgePath}`;
180
- packageJson.dependencies['@agentstage/render'] = `file:${localRenderPath}`;
181
- }
182
- else {
183
- // Use npm version for production
184
- packageJson.dependencies['@agentstage/bridge'] = '^0.1.0';
185
- packageJson.dependencies['@agentstage/render'] = '^0.2.0';
186
- }
187
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
188
- }
189
- async function installDependencies(targetDir) {
190
- // Check if we're in the monorepo
191
- const currentFilePath = fileURLToPath(import.meta.url);
192
- const localBridgePath = resolve(join(dirname(currentFilePath), '..', '..', '..', 'bridge'));
193
- const isDev = existsSync(localBridgePath);
194
- if (isDev) {
195
- // In development mode (monorepo), use pnpm
196
- try {
197
- await execa('pnpm', ['install'], { cwd: targetDir, stdio: 'pipe' });
198
- }
199
- catch {
200
- // Fallback to npm if pnpm is not available
201
- await execa('npm', ['install'], { cwd: targetDir, stdio: 'pipe' });
202
- }
203
- }
204
- else {
205
- // In production, use npm
206
- await execa('npm', ['install'], { cwd: targetDir, stdio: 'pipe' });
207
- }
208
- }
@@ -2,7 +2,6 @@ import { Command } from 'commander';
2
2
  import consola from 'consola';
3
3
  import c from 'picocolors';
4
4
  import { writeFile, mkdir } from 'fs/promises';
5
- import { existsSync } from 'fs';
6
5
  import { join } from 'pathe';
7
6
  import { getWorkspaceDir, isInitialized, readRuntimeConfig, getPagesDir } from '../../utils/paths.js';
8
7
  import { FileStore } from '@agentstage/bridge';
@@ -35,19 +34,9 @@ export const pageAddCommand = new Command('add')
35
34
  try {
36
35
  const workspaceDir = await getWorkspaceDir();
37
36
  const config = await readRuntimeConfig();
38
- const routesDir = join(workspaceDir, 'src', 'routes');
39
- const pagesDir = join(workspaceDir, 'src', 'pages', name);
40
- const pageFile = join(routesDir, `${name}.tsx`);
37
+ const pagesDir = join(workspaceDir, 'pages', name);
41
38
  // 确保目录存在
42
- await mkdir(routesDir, { recursive: true });
43
39
  await mkdir(pagesDir, { recursive: true });
44
- if (existsSync(pageFile)) {
45
- printAgentErrorHelp(`Page "${name}" already exists`);
46
- process.exit(1);
47
- }
48
- // 生成 .tsx 路由文件
49
- const pageContent = generateTsxContent(name);
50
- await writeFile(pageFile, pageContent);
51
40
  // 处理 UI
52
41
  let uiContent;
53
42
  if (options.uiStdin) {
@@ -122,17 +111,16 @@ export const pageAddCommand = new Command('add')
122
111
  if (options.ui) {
123
112
  // 提供了完整 UI
124
113
  printAgentSuccess(`Page "${name}" created with custom UI and state`, [
125
- `Start dev server: agentstage dev start`,
126
- `Open http://localhost:${port}/${name} to see your page`,
114
+ `Start runtime: agentstage serve ${name}`,
115
+ `Open http://localhost:${port} to see your page`,
127
116
  `Update state: agentstage run set-state ${name} '{"key": "value"}'`
128
117
  ]);
129
118
  }
130
119
  else {
131
120
  // 默认 UI,输出 prompts
132
121
  consola.success(`Page "${name}" created`);
133
- console.log(` Route: ${c.cyan(`src/routes/${name}.tsx`)}`);
134
- console.log(` UI: ${c.cyan(`src/pages/${name}/ui.json`)}`);
135
- console.log(` Store: ${c.cyan(`src/pages/${name}/store.json`)}`);
122
+ console.log(` UI: ${c.cyan(`pages/${name}/ui.json`)}`);
123
+ console.log(` Store: ${c.cyan(`pages/${name}/store.json`)}`);
136
124
  console.log(` URL: ${c.cyan(`http://localhost:${port}/${name}`)}`);
137
125
  console.log();
138
126
  console.log(c.bold('─'.repeat(60)));
@@ -152,26 +140,6 @@ export const pageAddCommand = new Command('add')
152
140
  process.exit(1);
153
141
  }
154
142
  });
155
- function generateTsxContent(name) {
156
- const pascalName = toPascalCase(name);
157
- return `import { createFileRoute } from '@tanstack/react-router'
158
- import { useMemo } from 'react'
159
- import { PageRenderer } from '../components/PageRenderer'
160
- import { createPageBridge } from '../lib/bridge'
161
-
162
- export const Route = createFileRoute('/${name}')({
163
- component: ${pascalName}Page,
164
- })
165
-
166
- function ${pascalName}Page() {
167
- const bridge = useMemo(() => createPageBridge({
168
- pageId: '${name}',
169
- }), [])
170
-
171
- return <PageRenderer pageId="${name}" bridge={bridge} />
172
- }
173
- `;
174
- }
175
143
  function generateDefaultUi(name) {
176
144
  const titleName = toTitleCase(name);
177
145
  return {
@@ -211,9 +179,6 @@ function generateDefaultState(name) {
211
179
  pageId: name,
212
180
  };
213
181
  }
214
- function toPascalCase(str) {
215
- return str.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('');
216
- }
217
182
  function toTitleCase(str) {
218
183
  return str.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
219
184
  }
@@ -16,7 +16,7 @@ export const runExecCommand = new Command('exec')
16
16
  }
17
17
  const config = await readRuntimeConfig();
18
18
  if (!config) {
19
- consola.error('Runtime is not running. Start it first with `agentstage dev start`.');
19
+ consola.error('Runtime is not running. Start it first with `agentstage serve <pageId>`.');
20
20
  process.exit(1);
21
21
  }
22
22
  // Parse payload
@@ -13,7 +13,7 @@ export const runInspectCommand = new Command('inspect')
13
13
  }
14
14
  const config = await readRuntimeConfig();
15
15
  if (!config) {
16
- consola.error('Runtime is not running. Start it first with `agentstage dev start`.');
16
+ consola.error('Runtime is not running. Start it first with `agentstage serve <pageId>`.');
17
17
  process.exit(1);
18
18
  }
19
19
  const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
@@ -14,7 +14,7 @@ export const runWatchCommand = new Command('watch')
14
14
  }
15
15
  const config = await readRuntimeConfig();
16
16
  if (!config) {
17
- consola.error('Runtime is not running. Start it first with `agentstage dev start`.');
17
+ consola.error('Runtime is not running. Start it first with `agentstage serve <pageId>`.');
18
18
  process.exit(1);
19
19
  }
20
20
  const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const serveCommand: Command;