@expressots/studio 4.0.0-preview.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/README.md +122 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +132 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/studio.d.ts +54 -0
- package/dist/studio.d.ts.map +1 -0
- package/dist/studio.js +308 -0
- package/dist/studio.js.map +1 -0
- package/dist/ui/assets/index-CwOg4Wcc.js +522 -0
- package/dist/ui/assets/index-CwOg4Wcc.js.map +1 -0
- package/dist/ui/assets/index-D2kzut78.css +1 -0
- package/dist/ui/expressots-icon.svg +11 -0
- package/dist/ui/index.html +18 -0
- package/package.json +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @expressots/studio
|
|
2
|
+
|
|
3
|
+
ExpressoTS Studio - Developer Experience Platform. The main package that orchestrates the Studio Agent and Web UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D @expressots/studio
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### CLI Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Start Studio
|
|
17
|
+
npx expressots-studio start
|
|
18
|
+
|
|
19
|
+
# With options
|
|
20
|
+
npx expressots-studio start --port 3333 --agent-port 3334
|
|
21
|
+
|
|
22
|
+
# Show project info
|
|
23
|
+
npx expressots-studio info
|
|
24
|
+
|
|
25
|
+
# Clean Studio data
|
|
26
|
+
npx expressots-studio clean
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Programmatic Usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Studio } from '@expressots/studio';
|
|
33
|
+
|
|
34
|
+
const studio = new Studio({
|
|
35
|
+
uiPort: 3333,
|
|
36
|
+
agentPort: 3334,
|
|
37
|
+
dbPath: '.studio/studio.db',
|
|
38
|
+
srcPath: './src',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await studio.start();
|
|
42
|
+
|
|
43
|
+
// Studio is now running
|
|
44
|
+
// UI: http://localhost:3333
|
|
45
|
+
// Agent: ws://localhost:3334
|
|
46
|
+
|
|
47
|
+
// To stop
|
|
48
|
+
await studio.stop();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Integration with ExpressoTS CLI
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Via ExpressoTS CLI (when integrated)
|
|
55
|
+
expressots studio
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CLI Commands
|
|
59
|
+
|
|
60
|
+
### `start` (default)
|
|
61
|
+
|
|
62
|
+
Start ExpressoTS Studio.
|
|
63
|
+
|
|
64
|
+
Options:
|
|
65
|
+
- `-p, --port <port>` - UI port (default: 3333)
|
|
66
|
+
- `-a, --agent-port <port>` - Agent WebSocket port (default: 3334)
|
|
67
|
+
- `-d, --db-path <path>` - Database path (default: .studio/studio.db)
|
|
68
|
+
- `--src <path>` - Source directory to scan (default: ./src)
|
|
69
|
+
- `--no-browser` - Don't open browser automatically
|
|
70
|
+
|
|
71
|
+
### `info`
|
|
72
|
+
|
|
73
|
+
Show information about the current project including discovered routes.
|
|
74
|
+
|
|
75
|
+
Options:
|
|
76
|
+
- `--src <path>` - Source directory to scan (default: ./src)
|
|
77
|
+
|
|
78
|
+
### `clean`
|
|
79
|
+
|
|
80
|
+
Remove all Studio data (database, cache).
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
- `-d, --db-path <path>` - Database path (default: .studio/studio.db)
|
|
84
|
+
|
|
85
|
+
## Configuration
|
|
86
|
+
|
|
87
|
+
You can also configure Studio via `expressots.config.ts`:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// expressots.config.ts
|
|
91
|
+
export default {
|
|
92
|
+
studio: {
|
|
93
|
+
uiPort: 3333,
|
|
94
|
+
agentPort: 3334,
|
|
95
|
+
dbPath: '.studio/studio.db',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Architecture
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
┌─────────────────────────────────────────────────────────┐
|
|
104
|
+
│ ExpressoTS Studio │
|
|
105
|
+
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │
|
|
106
|
+
│ │ Web UI │ │ Studio Agent │ │ MCP Server │ │
|
|
107
|
+
│ │ :3333 │←→│ :3334 │←→│ (AI tools) │ │
|
|
108
|
+
│ └─────────────┘ └──────────────┘ └────────────┘ │
|
|
109
|
+
│ ↓ ↓ │
|
|
110
|
+
│ Your ExpressoTS App (instrumented) │
|
|
111
|
+
└─────────────────────────────────────────────────────────┘
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## What's Included
|
|
115
|
+
|
|
116
|
+
- **Studio Agent**: Instrumentation, route discovery, request recording
|
|
117
|
+
- **Web UI**: Dashboard with trace visualization, architecture map, metrics
|
|
118
|
+
- **MCP Server**: AI-powered code generation tools (optional)
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT © ExpressoTS
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ExpressoTS Studio CLI
|
|
4
|
+
* Main entry point for launching the Studio
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import open from 'open';
|
|
10
|
+
import { Studio } from './studio.js';
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name('expressots-studio')
|
|
14
|
+
.description('ExpressoTS Studio - Developer Experience Platform')
|
|
15
|
+
.version('0.1.0');
|
|
16
|
+
program
|
|
17
|
+
.command('start')
|
|
18
|
+
.alias('s')
|
|
19
|
+
.description('Start ExpressoTS Studio')
|
|
20
|
+
.option('-p, --port <port>', 'UI port', '3333')
|
|
21
|
+
.option('-a, --agent-port <port>', 'Agent WebSocket port', '3334')
|
|
22
|
+
.option('-d, --db-path <path>', 'Database path', '.studio/studio.db')
|
|
23
|
+
.option('--no-browser', 'Do not open browser automatically')
|
|
24
|
+
.option('--src <path>', 'Source directory to scan', './src')
|
|
25
|
+
.option('--standalone', 'Run in standalone mode (starts own agent)', false)
|
|
26
|
+
.action(async (options) => {
|
|
27
|
+
try {
|
|
28
|
+
const studio = new Studio({
|
|
29
|
+
uiPort: parseInt(options.port, 10),
|
|
30
|
+
agentPort: parseInt(options.agentPort, 10),
|
|
31
|
+
dbPath: options.dbPath,
|
|
32
|
+
srcPath: options.src,
|
|
33
|
+
standalone: options.standalone,
|
|
34
|
+
});
|
|
35
|
+
await studio.start();
|
|
36
|
+
const agentStatus = options.standalone
|
|
37
|
+
? chalk.yellow('Standalone')
|
|
38
|
+
: studio.isAgentConnected()
|
|
39
|
+
? chalk.green('Connected')
|
|
40
|
+
: chalk.gray('Waiting for app...');
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(chalk.green(' Studio is running'));
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log(` UI ${chalk.cyan(`http://localhost:${options.port}`)}`);
|
|
45
|
+
console.log(` Agent ${agentStatus} ${chalk.gray(`ws://localhost:${options.agentPort}`)}`);
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(chalk.gray(' Press Ctrl+C to stop'));
|
|
48
|
+
console.log('');
|
|
49
|
+
if (options.browser !== false) {
|
|
50
|
+
await open(`http://localhost:${options.port}`);
|
|
51
|
+
}
|
|
52
|
+
process.on('SIGINT', async () => {
|
|
53
|
+
console.log('');
|
|
54
|
+
await studio.stop();
|
|
55
|
+
console.log(chalk.gray(' Studio stopped'));
|
|
56
|
+
process.exit(0);
|
|
57
|
+
});
|
|
58
|
+
process.on('SIGTERM', async () => {
|
|
59
|
+
await studio.stop();
|
|
60
|
+
process.exit(0);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(chalk.red('\n Failed to start Studio:'), error instanceof Error ? error.message : error);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
program
|
|
69
|
+
.command('info')
|
|
70
|
+
.description('Show information about the current project')
|
|
71
|
+
.option('--src <path>', 'Source directory to scan', './src')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const { RouteScanner } = await import('@expressots/studio-agent');
|
|
74
|
+
const spinner = ora('Scanning project...').start();
|
|
75
|
+
try {
|
|
76
|
+
const scanner = new RouteScanner(options.src);
|
|
77
|
+
const structure = await scanner.scan();
|
|
78
|
+
spinner.succeed(chalk.green('Project scanned successfully'));
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log(chalk.cyan('📁 Project Structure:'));
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(chalk.white(` Controllers: ${structure.controllers.length}`));
|
|
83
|
+
console.log(chalk.white(` Services: ${structure.services.length}`));
|
|
84
|
+
console.log(chalk.white(` Providers: ${structure.providers.length}`));
|
|
85
|
+
console.log(chalk.white(` Middleware: ${structure.middleware.length}`));
|
|
86
|
+
console.log('');
|
|
87
|
+
if (structure.controllers.length > 0) {
|
|
88
|
+
console.log(chalk.cyan('🛣️ Routes:'));
|
|
89
|
+
console.log('');
|
|
90
|
+
const routes = scanner.getRoutes();
|
|
91
|
+
for (const route of routes) {
|
|
92
|
+
const methodColor = route.method === 'GET' ? chalk.green :
|
|
93
|
+
route.method === 'POST' ? chalk.blue :
|
|
94
|
+
route.method === 'PUT' ? chalk.yellow :
|
|
95
|
+
route.method === 'DELETE' ? chalk.red :
|
|
96
|
+
chalk.white;
|
|
97
|
+
console.log(` ${methodColor(route.method.padEnd(7))} ${route.path}`);
|
|
98
|
+
}
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
spinner.fail(chalk.red('Failed to scan project'));
|
|
104
|
+
console.error(error instanceof Error ? error.message : error);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
program
|
|
109
|
+
.command('clean')
|
|
110
|
+
.description('Clean Studio data')
|
|
111
|
+
.option('-d, --db-path <path>', 'Database path', '.studio/studio.db')
|
|
112
|
+
.action(async (options) => {
|
|
113
|
+
const fs = await import('fs');
|
|
114
|
+
const path = await import('path');
|
|
115
|
+
const dbPath = path.resolve(process.cwd(), options.dbPath);
|
|
116
|
+
const studioDir = path.dirname(dbPath);
|
|
117
|
+
if (fs.existsSync(studioDir)) {
|
|
118
|
+
fs.rmSync(studioDir, { recursive: true });
|
|
119
|
+
console.log(chalk.green('✓'), 'Studio data cleaned');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(chalk.yellow('No Studio data found'));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
// Default to start if no command is provided
|
|
126
|
+
program
|
|
127
|
+
.action(() => {
|
|
128
|
+
const startCmd = program.commands.find((cmd) => cmd.name() === 'start');
|
|
129
|
+
startCmd?.parse();
|
|
130
|
+
});
|
|
131
|
+
program.parse();
|
|
132
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAmBrC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,mBAAmB,CAAC;KACzB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,KAAK,CAAC,GAAG,CAAC;KACV,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,mBAAmB,EAAE,SAAS,EAAE,MAAM,CAAC;KAC9C,MAAM,CAAC,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,CAAC;KACjE,MAAM,CAAC,sBAAsB,EAAE,eAAe,EAAE,mBAAmB,CAAC;KACpE,MAAM,CAAC,cAAc,EAAE,mCAAmC,CAAC;KAC3D,MAAM,CAAC,cAAc,EAAE,0BAA0B,EAAE,OAAO,CAAC;KAC3D,MAAM,CAAC,cAAc,EAAE,2CAA2C,EAAE,KAAK,CAAC;KAC1E,MAAM,CAAC,KAAK,EAAE,OAAqB,EAAE,EAAE;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACxB,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YAC1C,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,GAAG;YACpB,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QAErB,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU;YACpC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC;YAC5B,CAAC,CAAC,MAAM,CAAC,gBAAgB,EAAE;gBACzB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;gBAC1B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAEvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,IAAI,CAAC,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,aAAa,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IAEL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,6BAA6B,CAAC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CAAC,cAAc,EAAE,0BAA0B,EAAE,OAAO,CAAC;KAC3D,MAAM,CAAC,KAAK,EAAE,OAAoB,EAAE,EAAE;IACrC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,GAAG,CAAC,qBAAqB,CAAC,CAAC,KAAK,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QAEvC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACzE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,IAAI,SAAS,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAEhB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,WAAW,GACf,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBACtC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;wBACtC,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;4BACvC,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gCACvC,KAAK,CAAC,KAAK,CAAC;gBAEd,OAAO,CAAC,GAAG,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mBAAmB,CAAC;KAChC,MAAM,CAAC,sBAAsB,EAAE,eAAe,EAAE,mBAAmB,CAAC;KACpE,MAAM,CAAC,KAAK,EAAE,OAAqB,EAAE,EAAE;IACtC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,6CAA6C;AAC7C,OAAO;KACJ,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,OAAO,CAAC,CAAC;IACjF,QAAQ,EAAE,KAAK,EAAE,CAAC;AACpB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @expressots/studio
|
|
3
|
+
*
|
|
4
|
+
* ExpressoTS Studio - Developer Experience Platform
|
|
5
|
+
* Main orchestrator package that launches the agent and UI
|
|
6
|
+
*/
|
|
7
|
+
export { Studio } from './studio.js';
|
|
8
|
+
export type { StudioConfig } from './studio.js';
|
|
9
|
+
export { StudioAgent, RouteScanner, RequestRecorder } from '@expressots/studio-agent';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @expressots/studio
|
|
3
|
+
*
|
|
4
|
+
* ExpressoTS Studio - Developer Experience Platform
|
|
5
|
+
* Main orchestrator package that launches the agent and UI
|
|
6
|
+
*/
|
|
7
|
+
export { Studio } from './studio.js';
|
|
8
|
+
// Re-export from agent for convenience
|
|
9
|
+
export { StudioAgent, RouteScanner, RequestRecorder } from '@expressots/studio-agent';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,uCAAuC;AACvC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/studio.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* 1. UI-only mode (default): Just serves the UI, connects to agent in user's app
|
|
6
|
+
* 2. Standalone mode: Starts its own agent (for demos or when not using core integration)
|
|
7
|
+
*/
|
|
8
|
+
import { StudioAgent } from '@expressots/studio-agent';
|
|
9
|
+
export interface StudioConfig {
|
|
10
|
+
uiPort: number;
|
|
11
|
+
agentPort: number;
|
|
12
|
+
dbPath: string;
|
|
13
|
+
srcPath: string;
|
|
14
|
+
serviceName?: string;
|
|
15
|
+
/** If true, starts a standalone agent (default: false, just serves UI) */
|
|
16
|
+
standalone?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class Studio {
|
|
19
|
+
private config;
|
|
20
|
+
private agent;
|
|
21
|
+
private uiApp;
|
|
22
|
+
private uiServer;
|
|
23
|
+
private agentClient;
|
|
24
|
+
private agentConnected;
|
|
25
|
+
constructor(config?: Partial<StudioConfig>);
|
|
26
|
+
/** Start the Studio */
|
|
27
|
+
start(): Promise<void>;
|
|
28
|
+
/** Stop the Studio */
|
|
29
|
+
stop(): Promise<void>;
|
|
30
|
+
/** Connect to an existing agent running in the user's app */
|
|
31
|
+
private connectToAgent;
|
|
32
|
+
/** Start the Studio Agent (standalone mode only) */
|
|
33
|
+
private startAgent;
|
|
34
|
+
/** Start the UI server */
|
|
35
|
+
private startUIServer;
|
|
36
|
+
/** Find the bundled UI dist path */
|
|
37
|
+
private findUIDistPath;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the brand icon SVG as a base64 data URI. Used by the dev-mode
|
|
40
|
+
* fallback HTML, which is served as a catch-all when the UI bundle is
|
|
41
|
+
* missing — so we can't rely on `/expressots-icon.svg` being reachable.
|
|
42
|
+
* Returns a tiny inline SVG placeholder if the asset is missing.
|
|
43
|
+
*/
|
|
44
|
+
private getIconDataUri;
|
|
45
|
+
/** Get development mode HTML */
|
|
46
|
+
private getDevModeHTML;
|
|
47
|
+
/** Check if agent is connected */
|
|
48
|
+
isAgentConnected(): boolean;
|
|
49
|
+
/** Get the Studio Agent instance (standalone mode only) */
|
|
50
|
+
getAgent(): StudioAgent | null;
|
|
51
|
+
/** Get configuration */
|
|
52
|
+
getConfig(): StudioConfig;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=studio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAkB;gBAE5B,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM;IAW9C,uBAAuB;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,sBAAsB;IAChB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,6DAA6D;YAC/C,cAAc;IAsC5B,oDAAoD;YACtC,UAAU;IAYxB,0BAA0B;YACZ,aAAa;IAkC3B,oCAAoC;IACpC,OAAO,CAAC,cAAc;IAQtB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA0BtB,gCAAgC;IAChC,OAAO,CAAC,cAAc;IA0HtB,kCAAkC;IAClC,gBAAgB,IAAI,OAAO;IAI3B,2DAA2D;IAC3D,QAAQ,IAAI,WAAW,GAAG,IAAI;IAI9B,wBAAwB;IACxB,SAAS,IAAI,YAAY;CAG1B"}
|
package/dist/studio.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* 1. UI-only mode (default): Just serves the UI, connects to agent in user's app
|
|
6
|
+
* 2. Standalone mode: Starts its own agent (for demos or when not using core integration)
|
|
7
|
+
*/
|
|
8
|
+
import express from 'express';
|
|
9
|
+
import { createServer } from 'http';
|
|
10
|
+
import { io as SocketIOClient } from 'socket.io-client';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { StudioAgent } from '@expressots/studio-agent';
|
|
15
|
+
export class Studio {
|
|
16
|
+
config;
|
|
17
|
+
agent = null;
|
|
18
|
+
uiApp = null;
|
|
19
|
+
uiServer = null;
|
|
20
|
+
agentClient = null;
|
|
21
|
+
agentConnected = false;
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
this.config = {
|
|
24
|
+
uiPort: config.uiPort ?? 3333,
|
|
25
|
+
agentPort: config.agentPort ?? 3334,
|
|
26
|
+
dbPath: config.dbPath ?? '.studio/studio.db',
|
|
27
|
+
srcPath: config.srcPath ?? './src',
|
|
28
|
+
serviceName: config.serviceName ?? 'expressots-app',
|
|
29
|
+
standalone: config.standalone ?? false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Start the Studio */
|
|
33
|
+
async start() {
|
|
34
|
+
if (this.config.standalone) {
|
|
35
|
+
await this.startAgent();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// UI-only mode: Try to connect to existing agent
|
|
39
|
+
await this.connectToAgent();
|
|
40
|
+
}
|
|
41
|
+
// Start the UI server
|
|
42
|
+
await this.startUIServer();
|
|
43
|
+
}
|
|
44
|
+
/** Stop the Studio */
|
|
45
|
+
async stop() {
|
|
46
|
+
// Disconnect from agent
|
|
47
|
+
if (this.agentClient) {
|
|
48
|
+
this.agentClient.disconnect();
|
|
49
|
+
this.agentClient = null;
|
|
50
|
+
}
|
|
51
|
+
// Stop UI server
|
|
52
|
+
if (this.uiServer) {
|
|
53
|
+
await new Promise((resolve) => {
|
|
54
|
+
this.uiServer.close(() => resolve());
|
|
55
|
+
});
|
|
56
|
+
this.uiServer = null;
|
|
57
|
+
}
|
|
58
|
+
// Stop agent (only if we started it)
|
|
59
|
+
if (this.agent) {
|
|
60
|
+
await this.agent.stop();
|
|
61
|
+
this.agent = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Connect to an existing agent running in the user's app */
|
|
65
|
+
async connectToAgent() {
|
|
66
|
+
const agentUrl = `http://localhost:${this.config.agentPort}`;
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const probe = SocketIOClient(agentUrl, {
|
|
69
|
+
transports: ['websocket'],
|
|
70
|
+
reconnection: false,
|
|
71
|
+
timeout: 3000,
|
|
72
|
+
});
|
|
73
|
+
this.agentClient = probe;
|
|
74
|
+
const cleanup = (connected) => {
|
|
75
|
+
this.agentConnected = connected;
|
|
76
|
+
probe.removeAllListeners();
|
|
77
|
+
probe.disconnect();
|
|
78
|
+
if (this.agentClient === probe) {
|
|
79
|
+
this.agentClient = null;
|
|
80
|
+
}
|
|
81
|
+
resolve();
|
|
82
|
+
};
|
|
83
|
+
const timeout = setTimeout(() => {
|
|
84
|
+
if (!this.agentConnected) {
|
|
85
|
+
cleanup(false);
|
|
86
|
+
}
|
|
87
|
+
}, 3000);
|
|
88
|
+
probe.on('connect', () => {
|
|
89
|
+
clearTimeout(timeout);
|
|
90
|
+
cleanup(true);
|
|
91
|
+
});
|
|
92
|
+
probe.on('connect_error', () => {
|
|
93
|
+
// Wait for timeout fallback; reconnection is disabled.
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** Start the Studio Agent (standalone mode only) */
|
|
98
|
+
async startAgent() {
|
|
99
|
+
this.agent = new StudioAgent({
|
|
100
|
+
port: this.config.agentPort,
|
|
101
|
+
dbPath: this.config.dbPath,
|
|
102
|
+
serviceName: this.config.serviceName,
|
|
103
|
+
enableRecording: true,
|
|
104
|
+
enableProfiling: true,
|
|
105
|
+
});
|
|
106
|
+
await this.agent.start();
|
|
107
|
+
}
|
|
108
|
+
/** Start the UI server */
|
|
109
|
+
async startUIServer() {
|
|
110
|
+
this.uiApp = express();
|
|
111
|
+
// Try to find the built UI files
|
|
112
|
+
const uiDistPath = this.findUIDistPath();
|
|
113
|
+
if (uiDistPath && fs.existsSync(uiDistPath)) {
|
|
114
|
+
// Serve static files from the UI build
|
|
115
|
+
this.uiApp.use(express.static(uiDistPath));
|
|
116
|
+
// SPA fallback
|
|
117
|
+
this.uiApp.get('*', (_req, res) => {
|
|
118
|
+
res.sendFile(path.join(uiDistPath, 'index.html'));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Development mode - show placeholder
|
|
123
|
+
this.uiApp.get('*', (_req, res) => {
|
|
124
|
+
res.send(this.getDevModeHTML());
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
this.uiServer = createServer(this.uiApp);
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
this.uiServer.listen(this.config.uiPort, () => {
|
|
130
|
+
resolve();
|
|
131
|
+
});
|
|
132
|
+
this.uiServer.on('error', (err) => {
|
|
133
|
+
reject(err);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** Find the bundled UI dist path */
|
|
138
|
+
findUIDistPath() {
|
|
139
|
+
// The UI is bundled into this package at build time and lives at
|
|
140
|
+
// `<package>/dist/ui/` next to the orchestrator's compiled JS.
|
|
141
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
142
|
+
const candidate = path.resolve(here, 'ui');
|
|
143
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Resolve the brand icon SVG as a base64 data URI. Used by the dev-mode
|
|
147
|
+
* fallback HTML, which is served as a catch-all when the UI bundle is
|
|
148
|
+
* missing — so we can't rely on `/expressots-icon.svg` being reachable.
|
|
149
|
+
* Returns a tiny inline SVG placeholder if the asset is missing.
|
|
150
|
+
*/
|
|
151
|
+
getIconDataUri() {
|
|
152
|
+
try {
|
|
153
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
154
|
+
const candidates = [
|
|
155
|
+
path.resolve(here, 'ui', 'expressots-icon.svg'),
|
|
156
|
+
path.resolve(here, '..', 'public', 'expressots-icon.svg'),
|
|
157
|
+
path.resolve(here, '..', '..', 'public', 'expressots-icon.svg'),
|
|
158
|
+
];
|
|
159
|
+
for (const c of candidates) {
|
|
160
|
+
if (fs.existsSync(c)) {
|
|
161
|
+
const svg = fs.readFileSync(c, 'utf8');
|
|
162
|
+
return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// fall through
|
|
168
|
+
}
|
|
169
|
+
// Minimal inline fallback (green circle) — matches the brand tone but
|
|
170
|
+
// ships zero external dependencies.
|
|
171
|
+
const fallback = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">' +
|
|
172
|
+
'<circle cx="32" cy="32" r="30" fill="#171717"/>' +
|
|
173
|
+
'<circle cx="32" cy="32" r="22" fill="#3de678"/></svg>';
|
|
174
|
+
return `data:image/svg+xml;base64,${Buffer.from(fallback).toString('base64')}`;
|
|
175
|
+
}
|
|
176
|
+
/** Get development mode HTML */
|
|
177
|
+
getDevModeHTML() {
|
|
178
|
+
const modeText = this.config.standalone
|
|
179
|
+
? '🔧 Standalone Mode - Agent running here'
|
|
180
|
+
: this.agentConnected
|
|
181
|
+
? '✅ Connected to your app\'s agent'
|
|
182
|
+
: '⏳ Waiting for agent connection...';
|
|
183
|
+
const modeColor = this.config.standalone
|
|
184
|
+
? '#f59e0b'
|
|
185
|
+
: this.agentConnected
|
|
186
|
+
? '#22c55e'
|
|
187
|
+
: '#6b7280';
|
|
188
|
+
const iconDataUri = this.getIconDataUri();
|
|
189
|
+
return `
|
|
190
|
+
<!DOCTYPE html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="UTF-8">
|
|
194
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
195
|
+
<link rel="icon" type="image/svg+xml" href="${iconDataUri}">
|
|
196
|
+
<title>ExpressoTS Studio</title>
|
|
197
|
+
<style>
|
|
198
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
199
|
+
body {
|
|
200
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
201
|
+
background: #171717;
|
|
202
|
+
color: #fff;
|
|
203
|
+
min-height: 100vh;
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
justify-content: center;
|
|
207
|
+
}
|
|
208
|
+
.container { text-align: center; padding: 40px; max-width: 600px; }
|
|
209
|
+
.logo {
|
|
210
|
+
width: 96px;
|
|
211
|
+
height: 96px;
|
|
212
|
+
margin: 0 auto 20px;
|
|
213
|
+
display: block;
|
|
214
|
+
border-radius: 50%;
|
|
215
|
+
box-shadow: 0 8px 32px rgba(61, 230, 120, 0.2);
|
|
216
|
+
}
|
|
217
|
+
h1 {
|
|
218
|
+
font-size: 2.5rem;
|
|
219
|
+
margin-bottom: 10px;
|
|
220
|
+
background: linear-gradient(90deg, #3de678, #19ce59);
|
|
221
|
+
-webkit-background-clip: text;
|
|
222
|
+
-webkit-text-fill-color: transparent;
|
|
223
|
+
background-clip: text;
|
|
224
|
+
}
|
|
225
|
+
.subtitle { color: #9ca3af; font-size: 1.1rem; margin-bottom: 30px; }
|
|
226
|
+
.status {
|
|
227
|
+
background: rgba(61, 230, 120, 0.08);
|
|
228
|
+
border: 1px solid ${modeColor}40;
|
|
229
|
+
border-radius: 12px;
|
|
230
|
+
padding: 20px 30px;
|
|
231
|
+
margin-bottom: 30px;
|
|
232
|
+
}
|
|
233
|
+
.status h2 { color: ${modeColor}; font-size: 1.2rem; margin-bottom: 10px; }
|
|
234
|
+
.status p { color: #9ca3af; }
|
|
235
|
+
.agent-info {
|
|
236
|
+
background: rgba(34, 197, 94, 0.1);
|
|
237
|
+
border: 1px solid rgba(34, 197, 94, 0.3);
|
|
238
|
+
border-radius: 8px;
|
|
239
|
+
padding: 15px;
|
|
240
|
+
margin-top: 20px;
|
|
241
|
+
}
|
|
242
|
+
.agent-info code { color: #22c55e; font-family: 'JetBrains Mono', monospace; }
|
|
243
|
+
.instructions { color: #6b7280; font-size: 0.9rem; margin-top: 30px; line-height: 1.8; }
|
|
244
|
+
.instructions code {
|
|
245
|
+
background: rgba(255,255,255,0.1);
|
|
246
|
+
padding: 2px 8px;
|
|
247
|
+
border-radius: 4px;
|
|
248
|
+
font-family: 'JetBrains Mono', monospace;
|
|
249
|
+
}
|
|
250
|
+
.help-box {
|
|
251
|
+
background: rgba(99, 102, 241, 0.1);
|
|
252
|
+
border: 1px solid rgba(99, 102, 241, 0.3);
|
|
253
|
+
border-radius: 8px;
|
|
254
|
+
padding: 20px;
|
|
255
|
+
margin-top: 30px;
|
|
256
|
+
text-align: left;
|
|
257
|
+
}
|
|
258
|
+
.help-box h3 { color: #818cf8; margin-bottom: 10px; }
|
|
259
|
+
.help-box ol { padding-left: 20px; color: #9ca3af; }
|
|
260
|
+
.help-box li { margin-bottom: 8px; }
|
|
261
|
+
</style>
|
|
262
|
+
</head>
|
|
263
|
+
<body>
|
|
264
|
+
<div class="container">
|
|
265
|
+
<img class="logo" src="${iconDataUri}" alt="ExpressoTS" />
|
|
266
|
+
<h1>ExpressoTS Studio</h1>
|
|
267
|
+
<p class="subtitle">Developer Experience Platform</p>
|
|
268
|
+
|
|
269
|
+
<div class="status">
|
|
270
|
+
<h2>${modeText}</h2>
|
|
271
|
+
<div class="agent-info">
|
|
272
|
+
<code>ws://localhost:${this.config.agentPort}</code>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
${!this.agentConnected && !this.config.standalone ? `
|
|
277
|
+
<div class="help-box">
|
|
278
|
+
<h3>📋 How to capture requests:</h3>
|
|
279
|
+
<ol>
|
|
280
|
+
<li>Install the agent in your app: <code>npm install @expressots/studio-agent</code></li>
|
|
281
|
+
<li>Restart your ExpressoTS app</li>
|
|
282
|
+
<li>Refresh this page</li>
|
|
283
|
+
</ol>
|
|
284
|
+
</div>
|
|
285
|
+
` : ''}
|
|
286
|
+
|
|
287
|
+
<p class="instructions">
|
|
288
|
+
The Studio UI failed to load. Try reinstalling @expressots/studio.
|
|
289
|
+
</p>
|
|
290
|
+
</div>
|
|
291
|
+
</body>
|
|
292
|
+
</html>
|
|
293
|
+
`;
|
|
294
|
+
}
|
|
295
|
+
/** Check if agent is connected */
|
|
296
|
+
isAgentConnected() {
|
|
297
|
+
return this.agentConnected || this.agent !== null;
|
|
298
|
+
}
|
|
299
|
+
/** Get the Studio Agent instance (standalone mode only) */
|
|
300
|
+
getAgent() {
|
|
301
|
+
return this.agent;
|
|
302
|
+
}
|
|
303
|
+
/** Get configuration */
|
|
304
|
+
getConfig() {
|
|
305
|
+
return { ...this.config };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=studio.js.map
|