agent-stage 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cleanup.d.ts +2 -0
- package/dist/commands/{reset.js → cleanup.js} +3 -2
- package/dist/commands/dev/index.d.ts +2 -0
- package/dist/commands/dev/index.js +11 -0
- package/dist/commands/dev/init.d.ts +2 -0
- package/dist/commands/dev/init.js +231 -0
- package/dist/commands/dev/start.d.ts +2 -0
- package/dist/commands/dev/start.js +145 -0
- package/dist/commands/dev/status.d.ts +2 -0
- package/dist/commands/dev/status.js +55 -0
- package/dist/commands/dev/stop.d.ts +2 -0
- package/dist/commands/dev/stop.js +45 -0
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/exec.js +46 -30
- package/dist/commands/guide.d.ts +2 -0
- package/dist/commands/guide.js +268 -0
- package/dist/commands/init.js +9 -9
- package/dist/commands/inspect.js +1 -1
- package/dist/commands/ls.js +65 -17
- package/dist/commands/page/add.d.ts +2 -0
- package/dist/commands/page/add.js +262 -0
- package/dist/commands/page/index.d.ts +2 -0
- package/dist/commands/page/index.js +11 -0
- package/dist/commands/page/ls.d.ts +2 -0
- package/dist/commands/page/ls.js +45 -0
- package/dist/commands/page/manifest.d.ts +2 -0
- package/dist/commands/page/manifest.js +82 -0
- package/dist/commands/page/rm.d.ts +2 -0
- package/dist/commands/page/rm.js +89 -0
- package/dist/commands/run/exec.d.ts +2 -0
- package/dist/commands/run/exec.js +89 -0
- package/dist/commands/run/get-state.d.ts +2 -0
- package/dist/commands/run/get-state.js +71 -0
- package/dist/commands/run/index.d.ts +2 -0
- package/dist/commands/run/index.js +13 -0
- package/dist/commands/run/inspect.d.ts +2 -0
- package/dist/commands/run/inspect.js +80 -0
- package/dist/commands/run/set-state.d.ts +2 -0
- package/dist/commands/run/set-state.js +93 -0
- package/dist/commands/run/watch.d.ts +2 -0
- package/dist/commands/run/watch.js +72 -0
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.js +230 -0
- package/dist/commands/watch.js +1 -1
- package/dist/index.js +11 -30
- package/dist/utils/agent-helper.d.ts +30 -0
- package/dist/utils/agent-helper.js +181 -0
- package/dist/utils/cloudflared.d.ts +21 -0
- package/dist/utils/cloudflared.js +90 -0
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.js +7 -10
- package/dist/utils/tunnel.d.ts +21 -0
- package/dist/utils/tunnel.js +116 -0
- package/package.json +3 -2
- package/template/package.json +7 -6
- package/template/src/components/PageRenderer.tsx +108 -0
- package/template/src/components/bridge-state-provider.tsx +87 -0
- package/template/src/lib/bridge.ts +53 -0
- package/template/src/pages/counter/store.json +8 -0
- package/template/src/pages/counter/ui.json +108 -0
- package/template/src/pages/test-page/store.json +8 -0
- package/template/src/routeTree.gen.ts +77 -0
- package/template/src/routes/__root.tsx +1 -1
- package/template/src/routes/counter.tsx +19 -0
- package/dist/commands/add-page.d.ts +0 -2
- package/dist/commands/add-page.js +0 -132
- package/dist/commands/reset.d.ts +0 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { runExecCommand } from './exec.js';
|
|
3
|
+
import { runSetStateCommand } from './set-state.js';
|
|
4
|
+
import { runGetStateCommand } from './get-state.js';
|
|
5
|
+
import { runWatchCommand } from './watch.js';
|
|
6
|
+
import { runInspectCommand } from './inspect.js';
|
|
7
|
+
export const runCommand = new Command('run')
|
|
8
|
+
.description('Runtime commands for controlling pages (Agent operations)')
|
|
9
|
+
.addCommand(runExecCommand)
|
|
10
|
+
.addCommand(runSetStateCommand)
|
|
11
|
+
.addCommand(runGetStateCommand)
|
|
12
|
+
.addCommand(runWatchCommand)
|
|
13
|
+
.addCommand(runInspectCommand);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import consola from 'consola';
|
|
3
|
+
import c from 'picocolors';
|
|
4
|
+
import { isInitialized, readRuntimeConfig } from '../../utils/paths.js';
|
|
5
|
+
import { BridgeClient } from '@agentstage/bridge/sdk';
|
|
6
|
+
export const runInspectCommand = new Command('inspect')
|
|
7
|
+
.description('Inspect a page\'s state and schema')
|
|
8
|
+
.argument('<page>', 'Page ID')
|
|
9
|
+
.action(async (pageId) => {
|
|
10
|
+
if (!isInitialized()) {
|
|
11
|
+
consola.error('Project not initialized. Please run `agentstage init` first.');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const config = await readRuntimeConfig();
|
|
15
|
+
if (!config) {
|
|
16
|
+
consola.error('Runtime is not running. Start it first with `agentstage dev start`.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
|
|
20
|
+
try {
|
|
21
|
+
await client.connect();
|
|
22
|
+
const store = await client.findStoreByKey(pageId, 'main');
|
|
23
|
+
if (!store) {
|
|
24
|
+
consola.error(`Page "${pageId}" is not connected. Make sure the page is open in browser.`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
// Get description
|
|
28
|
+
const description = await client.describe(store.id);
|
|
29
|
+
// Get current state
|
|
30
|
+
const state = await client.getState(store.id);
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(c.bold('Page:'), c.cyan(pageId));
|
|
33
|
+
console.log(c.bold('Store ID:'), c.gray(store.id));
|
|
34
|
+
console.log(c.bold('Version:'), c.gray(store.version.toString()));
|
|
35
|
+
console.log(c.bold('Connected:'), c.gray(store.connectedAt.toISOString()));
|
|
36
|
+
console.log();
|
|
37
|
+
if (description) {
|
|
38
|
+
console.log(c.bold('Schema:'));
|
|
39
|
+
console.log(c.gray(JSON.stringify(description.schema, null, 2)));
|
|
40
|
+
console.log();
|
|
41
|
+
console.log(c.bold('Actions:'));
|
|
42
|
+
if (description.actions && Object.keys(description.actions).length > 0) {
|
|
43
|
+
for (const [name, def] of Object.entries(description.actions)) {
|
|
44
|
+
console.log(` ${c.cyan(name)}`);
|
|
45
|
+
console.log(` ${c.gray(def.description || 'No description')}`);
|
|
46
|
+
if (def.payload) {
|
|
47
|
+
console.log(` payload: ${c.gray(JSON.stringify(def.payload))}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(c.gray(' No actions defined'));
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
if (description.events && Object.keys(description.events).length > 0) {
|
|
56
|
+
console.log(c.bold('Events:'));
|
|
57
|
+
for (const [name, def] of Object.entries(description.events)) {
|
|
58
|
+
console.log(` ${c.cyan(name)}`);
|
|
59
|
+
console.log(` ${c.gray(def.description || 'No description')}`);
|
|
60
|
+
}
|
|
61
|
+
console.log();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log(c.bold('Current State:'));
|
|
65
|
+
if (state) {
|
|
66
|
+
console.log(c.gray(JSON.stringify(state.state, null, 2)));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(c.gray(' No state available'));
|
|
70
|
+
}
|
|
71
|
+
console.log();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
consola.error('Failed to inspect:', error.message);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
client.disconnect();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import consola from 'consola';
|
|
3
|
+
import c from 'picocolors';
|
|
4
|
+
import { isInitialized, readRuntimeConfig, getPagesDir } from '../../utils/paths.js';
|
|
5
|
+
import { BridgeClient } from '@agentstage/bridge/sdk';
|
|
6
|
+
import { FileStore } from '@agentstage/bridge';
|
|
7
|
+
export const runSetStateCommand = new Command('set-state')
|
|
8
|
+
.description('Set state on a page')
|
|
9
|
+
.argument('<page>', 'Page ID')
|
|
10
|
+
.argument('<json>', 'State as JSON string')
|
|
11
|
+
.option('--live', 'Also sync to connected browser via WebSocket', false)
|
|
12
|
+
.option('--wait [timeoutMs]', 'Wait for browser ACK when using --live')
|
|
13
|
+
.action(async (pageId, jsonStr, options) => {
|
|
14
|
+
if (!isInitialized()) {
|
|
15
|
+
consola.error('Project not initialized. Please run `agentstage init` first.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
// Parse state
|
|
19
|
+
let state;
|
|
20
|
+
try {
|
|
21
|
+
state = JSON.parse(jsonStr);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
consola.error('Invalid JSON state');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const config = await readRuntimeConfig();
|
|
28
|
+
// If --live is specified, require running runtime
|
|
29
|
+
if (options.live) {
|
|
30
|
+
if (!config) {
|
|
31
|
+
consola.error('Runtime is not running. Start it first or remove --live flag.');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
|
|
35
|
+
try {
|
|
36
|
+
await client.connect();
|
|
37
|
+
const store = await client.findStoreByKey(pageId, 'main');
|
|
38
|
+
if (!store) {
|
|
39
|
+
consola.error(`Page "${pageId}" is not connected. Make sure the page is open in browser.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const waitForAck = options.wait !== undefined && options.wait !== false;
|
|
43
|
+
const timeoutMs = typeof options.wait === 'string' && Number.isFinite(Number(options.wait))
|
|
44
|
+
? Number(options.wait)
|
|
45
|
+
: 5000;
|
|
46
|
+
if (waitForAck) {
|
|
47
|
+
consola.info(`Setting state on "${pageId}" (waiting ${timeoutMs}ms)...`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
consola.info(`Setting state on "${pageId}"...`);
|
|
51
|
+
}
|
|
52
|
+
await client.setStateByKey(pageId, 'main', state, waitForAck ? { waitForAck: true, timeoutMs } : {});
|
|
53
|
+
consola.success('State updated');
|
|
54
|
+
console.log(` Page: ${c.cyan(pageId)}`);
|
|
55
|
+
console.log(` State:`);
|
|
56
|
+
console.log(c.gray(JSON.stringify(state, null, 2)));
|
|
57
|
+
if (waitForAck) {
|
|
58
|
+
console.log(` ACK: ${c.green('received')}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
consola.error('Failed to set state:', error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
client.disconnect();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// File-only mode (works without running server)
|
|
71
|
+
const pagesDir = await getPagesDir();
|
|
72
|
+
const fileStore = new FileStore({ pagesDir });
|
|
73
|
+
try {
|
|
74
|
+
const saved = await fileStore.save(pageId, {
|
|
75
|
+
state,
|
|
76
|
+
version: 0,
|
|
77
|
+
updatedAt: new Date().toISOString(),
|
|
78
|
+
pageId,
|
|
79
|
+
});
|
|
80
|
+
consola.success('State saved to file');
|
|
81
|
+
console.log(` Page: ${c.cyan(pageId)}`);
|
|
82
|
+
console.log(` File: ${c.gray(`${pagesDir}/${pageId}/store.json`)}`);
|
|
83
|
+
console.log(` Version: ${c.gray(saved.version)}`);
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(c.dim('Note: State will apply when the page is next opened.'));
|
|
86
|
+
console.log(c.dim(' Use --live to apply to a running browser immediately.'));
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
consola.error('Failed to save state:', error.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import consola from 'consola';
|
|
3
|
+
import c from 'picocolors';
|
|
4
|
+
import { isInitialized, readRuntimeConfig } from '../../utils/paths.js';
|
|
5
|
+
import { BridgeClient } from '@agentstage/bridge/sdk';
|
|
6
|
+
export const runWatchCommand = new Command('watch')
|
|
7
|
+
.description('Watch state changes on a page')
|
|
8
|
+
.argument('<page>', 'Page ID')
|
|
9
|
+
.option('-f, --format <type>', 'Output format (pretty|json)', 'pretty')
|
|
10
|
+
.action(async (pageId, options) => {
|
|
11
|
+
if (!isInitialized()) {
|
|
12
|
+
consola.error('Project not initialized. Please run `agentstage init` first.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const config = await readRuntimeConfig();
|
|
16
|
+
if (!config) {
|
|
17
|
+
consola.error('Runtime is not running. Start it first with `agentstage dev start`.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
|
|
21
|
+
try {
|
|
22
|
+
await client.connect();
|
|
23
|
+
const store = await client.findStoreByKey(pageId, 'main');
|
|
24
|
+
if (!store) {
|
|
25
|
+
consola.error(`Page "${pageId}" is not connected. Make sure the page is open in browser.`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
consola.info(`Watching "${pageId}" for state changes...`);
|
|
29
|
+
console.log(c.dim('Press Ctrl+C to stop'));
|
|
30
|
+
console.log();
|
|
31
|
+
// Print initial state
|
|
32
|
+
const initialState = await client.getStateByKey(pageId, 'main');
|
|
33
|
+
if (initialState) {
|
|
34
|
+
printState('initial', initialState.state, initialState.version, options.format);
|
|
35
|
+
}
|
|
36
|
+
// Subscribe to changes
|
|
37
|
+
client.subscribe(store.id);
|
|
38
|
+
client.onEvent((event) => {
|
|
39
|
+
if (event.storeId !== store.id)
|
|
40
|
+
return;
|
|
41
|
+
if (event.type === 'stateChanged') {
|
|
42
|
+
printState('changed', event.state, event.version, options.format);
|
|
43
|
+
}
|
|
44
|
+
else if (event.type === 'disconnected') {
|
|
45
|
+
console.log(c.yellow(`[${new Date().toISOString()}] Page disconnected`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// Keep running until interrupted
|
|
49
|
+
await new Promise(() => { });
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
consola.error('Failed to watch:', error.message);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
function printState(type, state, version, format) {
|
|
57
|
+
const timestamp = new Date().toISOString();
|
|
58
|
+
if (format === 'json') {
|
|
59
|
+
console.log(JSON.stringify({
|
|
60
|
+
timestamp,
|
|
61
|
+
type,
|
|
62
|
+
version,
|
|
63
|
+
state,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const label = type === 'initial' ? c.blue('[initial]') : c.green('[changed]');
|
|
68
|
+
console.log(`${c.gray(timestamp)} ${label} (v${version}):`);
|
|
69
|
+
console.log(c.gray(JSON.stringify(state, null, 2)));
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { execa } from 'execa';
|
|
3
|
+
import consola from 'consola';
|
|
4
|
+
import { existsSync, readFileSync } from 'fs';
|
|
5
|
+
import { resolve } from 'pathe';
|
|
6
|
+
import picocolors from 'picocolors';
|
|
7
|
+
import { getWorkspaceDir, readRuntimeConfig, isInitialized } from '../utils/paths.js';
|
|
8
|
+
export const verifyCommand = new Command('verify')
|
|
9
|
+
.description('Verify page(s) before delivery - 交付前验收检查')
|
|
10
|
+
.argument('[page]', 'Page to verify (default: all pages)')
|
|
11
|
+
.option('--ci', 'CI mode (non-interactive, exit with error code)')
|
|
12
|
+
.action(async (page, options) => {
|
|
13
|
+
if (!isInitialized()) {
|
|
14
|
+
consola.error('No workspace found. Run `agentstage init` first.');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
let workspace;
|
|
18
|
+
try {
|
|
19
|
+
workspace = await getWorkspaceDir();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
consola.error(error.message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const checks = [];
|
|
26
|
+
// 1. TypeScript 编译检查
|
|
27
|
+
checks.push(await checkTypeScript(workspace));
|
|
28
|
+
// 2. 路由检查
|
|
29
|
+
if (page) {
|
|
30
|
+
checks.push(await checkRoute(workspace, page));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
checks.push(await checkAllRoutes(workspace));
|
|
34
|
+
}
|
|
35
|
+
// 3. 运行时检查
|
|
36
|
+
checks.push(await checkRuntime(workspace, page));
|
|
37
|
+
// 4. 输出报告
|
|
38
|
+
printReport(checks, options.ci);
|
|
39
|
+
// CI 模式下有问题就退出
|
|
40
|
+
if (options.ci && checks.some(c => !c.passed)) {
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
async function checkTypeScript(workspace) {
|
|
45
|
+
consola.info('Checking TypeScript compilation...');
|
|
46
|
+
try {
|
|
47
|
+
// 运行类型检查(忽略 node_modules 错误)
|
|
48
|
+
const { stderr } = await execa('npx', ['tsc', '--noEmit', '--skipLibCheck'], {
|
|
49
|
+
cwd: workspace,
|
|
50
|
+
reject: false,
|
|
51
|
+
timeout: 60000
|
|
52
|
+
});
|
|
53
|
+
if (stderr && stderr.includes('error')) {
|
|
54
|
+
const errors = parseTSErrors(stderr);
|
|
55
|
+
return {
|
|
56
|
+
name: 'TypeScript 编译',
|
|
57
|
+
passed: false,
|
|
58
|
+
errors: errors.slice(0, 5) // 只显示前5个
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { name: 'TypeScript 编译', passed: true };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
name: 'TypeScript 编译',
|
|
66
|
+
passed: false,
|
|
67
|
+
errors: [`无法运行 TypeScript 检查: ${error.message}`]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function parseTSErrors(output) {
|
|
72
|
+
const errors = [];
|
|
73
|
+
const lines = output.split('\n');
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
// 匹配 TS 错误格式: file(line,col): error TSxxxx: message
|
|
76
|
+
const match = line.match(/(.+?)\((\d+),(\d+)\):\s+error\s+TS\d+:\s+(.+)/);
|
|
77
|
+
if (match) {
|
|
78
|
+
const [, file, lineNum, col, message] = match;
|
|
79
|
+
const shortFile = file.replace(process.cwd(), '');
|
|
80
|
+
errors.push(`${shortFile}:${lineNum}:${col} - ${message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return errors.length > 0 ? errors : [output.slice(0, 200)];
|
|
84
|
+
}
|
|
85
|
+
async function checkRoute(workspace, pageId) {
|
|
86
|
+
const routeFile = resolve(workspace, 'src/routes', `${pageId}.tsx`);
|
|
87
|
+
if (!existsSync(routeFile)) {
|
|
88
|
+
return {
|
|
89
|
+
name: `路由检查 (${pageId})`,
|
|
90
|
+
passed: false,
|
|
91
|
+
errors: [`页面文件不存在: src/routes/${pageId}.tsx`]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// 检查文件内容
|
|
95
|
+
const content = readFileSync(routeFile, 'utf8');
|
|
96
|
+
const issues = [];
|
|
97
|
+
if (!content.includes('export const Route')) {
|
|
98
|
+
issues.push('缺少 Route 导出');
|
|
99
|
+
}
|
|
100
|
+
// 检查 HTML 实体(常见问题)
|
|
101
|
+
if (content.includes('"') || content.includes(''')) {
|
|
102
|
+
issues.push('发现 HTML 实体 ("/'),可能导致编译错误');
|
|
103
|
+
}
|
|
104
|
+
// 检查是否有未闭合的括号
|
|
105
|
+
const openBraces = (content.match(/{/g) || []).length;
|
|
106
|
+
const closeBraces = (content.match(/}/g) || []).length;
|
|
107
|
+
if (openBraces !== closeBraces) {
|
|
108
|
+
issues.push(`括号不匹配: {${openBraces} 个, }${closeBraces} 个`);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
name: `路由检查 (${pageId})`,
|
|
112
|
+
passed: issues.length === 0,
|
|
113
|
+
errors: issues,
|
|
114
|
+
warnings: issues.length > 0 ? ['这些问题可能导致页面无法正常工作'] : undefined
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async function checkAllRoutes(workspace) {
|
|
118
|
+
const routesDir = resolve(workspace, 'src/routes');
|
|
119
|
+
if (!existsSync(routesDir)) {
|
|
120
|
+
return {
|
|
121
|
+
name: '路由检查 (all)',
|
|
122
|
+
passed: false,
|
|
123
|
+
errors: ['路由目录不存在: src/routes']
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
name: '路由检查 (all)',
|
|
128
|
+
passed: true,
|
|
129
|
+
warnings: ['建议逐个页面检查: agentstage verify <page>']
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function checkRuntime(workspace, pageId) {
|
|
133
|
+
const config = await readRuntimeConfig();
|
|
134
|
+
if (!config) {
|
|
135
|
+
return {
|
|
136
|
+
name: '运行时检查',
|
|
137
|
+
passed: false,
|
|
138
|
+
errors: ['Server 未运行'],
|
|
139
|
+
warnings: ['请先执行: agentstage start']
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// 检查进程是否真的存在
|
|
143
|
+
try {
|
|
144
|
+
process.kill(config.pid, 0);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return {
|
|
148
|
+
name: '运行时检查',
|
|
149
|
+
passed: false,
|
|
150
|
+
errors: [`Server 进程 (${config.pid}) 不存在,可能已崩溃`],
|
|
151
|
+
warnings: ['请重启: agentstage restart']
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// HTTP 检查
|
|
155
|
+
const url = pageId
|
|
156
|
+
? `http://localhost:${config.port || 3000}/${pageId}`
|
|
157
|
+
: `http://localhost:${config.port || 3000}/`;
|
|
158
|
+
try {
|
|
159
|
+
const response = await fetch(url, {
|
|
160
|
+
signal: AbortSignal.timeout(5000)
|
|
161
|
+
});
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
return {
|
|
164
|
+
name: '运行时检查',
|
|
165
|
+
passed: false,
|
|
166
|
+
errors: [`页面返回 ${response.status} ${response.statusText}`]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// 检查页面内容是否包含 Vite 错误
|
|
170
|
+
const html = await response.text();
|
|
171
|
+
if (html.includes('[plugin:') && html.includes('error')) {
|
|
172
|
+
return {
|
|
173
|
+
name: '运行时检查',
|
|
174
|
+
passed: false,
|
|
175
|
+
errors: ['页面包含 Vite 错误信息,请检查浏览器控制台']
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
name: '运行时检查',
|
|
180
|
+
passed: true,
|
|
181
|
+
warnings: pageId ? undefined : ['建议检查具体页面: agentstage verify <page>']
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return {
|
|
186
|
+
name: '运行时检查',
|
|
187
|
+
passed: false,
|
|
188
|
+
errors: [`无法访问页面: ${error.message}`]
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function printReport(checks, ci) {
|
|
193
|
+
const { green, red, yellow, bold } = picocolors;
|
|
194
|
+
console.log('\n' + bold('📋 交付验收检查报告'));
|
|
195
|
+
console.log('─'.repeat(60));
|
|
196
|
+
let errorCount = 0;
|
|
197
|
+
let warningCount = 0;
|
|
198
|
+
for (const check of checks) {
|
|
199
|
+
const icon = check.passed ? green('✅') : red('❌');
|
|
200
|
+
console.log(`\n${icon} ${bold(check.name)}`);
|
|
201
|
+
if (check.errors && check.errors.length > 0) {
|
|
202
|
+
errorCount += check.errors.length;
|
|
203
|
+
for (const error of check.errors) {
|
|
204
|
+
console.log(` ${red('❌')} ${error}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (check.warnings && check.warnings.length > 0) {
|
|
208
|
+
warningCount += check.warnings.length;
|
|
209
|
+
for (const warning of check.warnings) {
|
|
210
|
+
console.log(` ${yellow('⚠️')} ${warning}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
console.log('─'.repeat(60));
|
|
215
|
+
if (errorCount > 0) {
|
|
216
|
+
console.log('\n' + red(bold(`❌ 检查未通过 (${errorCount} 个错误)`)));
|
|
217
|
+
if (!ci) {
|
|
218
|
+
console.log('\n' + yellow('💡 建议修复步骤:'));
|
|
219
|
+
console.log(' 1. 根据错误信息修改代码');
|
|
220
|
+
console.log(' 2. 重新运行 agentstage verify');
|
|
221
|
+
console.log(' 3. 确认所有检查通过后再交付');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (warningCount > 0) {
|
|
225
|
+
console.log('\n' + yellow(bold(`⚠️ 检查通过但有警告 (${warningCount} 个)`)));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
console.log('\n' + green(bold('✅ 所有检查通过,可以交付!')));
|
|
229
|
+
}
|
|
230
|
+
}
|
package/dist/commands/watch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import consola from 'consola';
|
|
3
3
|
import c from 'picocolors';
|
|
4
|
-
import { BridgeClient } from '
|
|
4
|
+
import { BridgeClient } from '@agentstage/bridge/sdk';
|
|
5
5
|
import { readRuntimeConfig, isInitialized } from '../utils/paths.js';
|
|
6
6
|
export const watchCommand = new Command('watch')
|
|
7
7
|
.description('Watch a page for real-time changes')
|
package/dist/index.js
CHANGED
|
@@ -1,41 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import consola from 'consola';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { lsCommand } from './commands/ls.js';
|
|
10
|
-
import { inspectCommand } from './commands/inspect.js';
|
|
11
|
-
import { watchCommand } from './commands/watch.js';
|
|
12
|
-
import { execCommand } from './commands/exec.js';
|
|
13
|
-
import { addPageCommand } from './commands/add-page.js';
|
|
14
|
-
import { rmPageCommand } from './commands/rm-page.js';
|
|
15
|
-
import { componentsCommand } from './commands/components.js';
|
|
16
|
-
import { doctorCommand } from './commands/doctor.js';
|
|
17
|
-
import { resetCommand } from './commands/reset.js';
|
|
4
|
+
// New command structure
|
|
5
|
+
import { devCommand } from './commands/dev/index.js';
|
|
6
|
+
import { pageCommand } from './commands/page/index.js';
|
|
7
|
+
import { runCommand } from './commands/run/index.js';
|
|
8
|
+
import { guideCommand } from './commands/guide.js';
|
|
18
9
|
const program = new Command();
|
|
19
10
|
program
|
|
20
11
|
.name('agentstage')
|
|
21
12
|
.description('Agent UI Stage CLI - Create interactive UI for AI agents')
|
|
22
13
|
.version('0.2.0');
|
|
23
|
-
//
|
|
24
|
-
program.addCommand(
|
|
25
|
-
program.addCommand(
|
|
26
|
-
program.addCommand(
|
|
27
|
-
program.addCommand(
|
|
28
|
-
|
|
29
|
-
program.addCommand(lsCommand);
|
|
30
|
-
program.addCommand(inspectCommand);
|
|
31
|
-
program.addCommand(watchCommand);
|
|
32
|
-
program.addCommand(execCommand);
|
|
33
|
-
program.addCommand(addPageCommand);
|
|
34
|
-
program.addCommand(rmPageCommand);
|
|
35
|
-
program.addCommand(componentsCommand);
|
|
36
|
-
program.addCommand(doctorCommand);
|
|
37
|
-
program.addCommand(resetCommand);
|
|
38
|
-
// 错误处理
|
|
14
|
+
// New command structure
|
|
15
|
+
program.addCommand(devCommand);
|
|
16
|
+
program.addCommand(pageCommand);
|
|
17
|
+
program.addCommand(runCommand);
|
|
18
|
+
program.addCommand(guideCommand);
|
|
19
|
+
// Error handling
|
|
39
20
|
program.exitOverride();
|
|
40
21
|
try {
|
|
41
22
|
await program.parseAsync(process.argv);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Error Helper - 为 Agent 提供自我修复指引
|
|
3
|
+
*/
|
|
4
|
+
export interface ErrorGuide {
|
|
5
|
+
message: string;
|
|
6
|
+
cause: string;
|
|
7
|
+
fix: string;
|
|
8
|
+
example: string;
|
|
9
|
+
relatedCommands: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 根据错误消息匹配对应的指引
|
|
13
|
+
*/
|
|
14
|
+
export declare function findErrorGuide(errorMessage: string): ErrorGuide | null;
|
|
15
|
+
/**
|
|
16
|
+
* 输出 Agent 友好的错误指引
|
|
17
|
+
*/
|
|
18
|
+
export declare function printAgentErrorHelp(error: Error | string, context?: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* 输出成功案例指引
|
|
21
|
+
*/
|
|
22
|
+
export declare function printAgentSuccess(message: string, nextSteps?: string[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* 输出 Agent 可用的提示
|
|
25
|
+
*/
|
|
26
|
+
export declare function printAgentHint(message: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* 输出 Agent 警告
|
|
29
|
+
*/
|
|
30
|
+
export declare function printAgentWarning(message: string): void;
|