@frontman-ai/vite 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,235 @@
1
+ # @frontman-ai/vite
2
+
3
+ Vite integration for Frontman - provides AI-powered development tools for any Vite-based application (React, Vue, Svelte, SolidJS, vanilla, etc.).
4
+
5
+ ## Installation
6
+
7
+ ### Quick Install (Recommended)
8
+
9
+ The fastest way to install Frontman is using our CLI installer:
10
+
11
+ ```bash
12
+ npx @frontman-ai/vite install
13
+
14
+ # Or with a custom server host
15
+ npx @frontman-ai/vite install --server frontman.company.com
16
+ ```
17
+
18
+ The installer will:
19
+ - Detect your Vite project and package manager
20
+ - Install `@frontman-ai/vite` as a dev dependency
21
+ - Add `frontmanPlugin()` to your `vite.config.ts` plugins array (or create one)
22
+
23
+ ### CLI Options
24
+
25
+ ```bash
26
+ npx @frontman-ai/vite install [options]
27
+
28
+ Options:
29
+ --server <host> Frontman server host (default: api.frontman.sh)
30
+ --prefix <path> Target directory (default: current directory)
31
+ --dry-run Preview changes without writing files
32
+ --skip-deps Skip dependency installation
33
+ --help Show help message
34
+ ```
35
+
36
+ ### Manual Installation
37
+
38
+ If you prefer to set things up manually or need to integrate with an existing configuration:
39
+
40
+ ```bash
41
+ npm install -D @frontman-ai/vite
42
+ ```
43
+
44
+ Then follow the [Manual Setup](#manual-setup) instructions below.
45
+
46
+ ## Quick Start
47
+
48
+ After running the installer, you're ready to go! Start your Vite dev server:
49
+
50
+ ```bash
51
+ npm run dev
52
+ ```
53
+
54
+ Then open your browser to `http://localhost:5173/frontman` to access the Frontman UI.
55
+
56
+ ## Manual Setup
57
+
58
+ Add the plugin to your `vite.config.ts`:
59
+
60
+ ```typescript
61
+ import { defineConfig } from 'vite';
62
+ import { frontmanPlugin } from '@frontman-ai/vite';
63
+
64
+ export default defineConfig({
65
+ plugins: [
66
+ frontmanPlugin(),
67
+ ],
68
+ });
69
+ ```
70
+
71
+ That's it! The plugin hooks into Vite's dev server and serves the Frontman UI at `/frontman`.
72
+
73
+ ## Adding to Existing Config
74
+
75
+ If you already have a `vite.config.ts` with plugins, add `frontmanPlugin()` to your existing plugins array:
76
+
77
+ ```typescript
78
+ import { defineConfig } from 'vite';
79
+ import react from '@vitejs/plugin-react';
80
+ import { frontmanPlugin } from '@frontman-ai/vite';
81
+
82
+ export default defineConfig({
83
+ plugins: [
84
+ frontmanPlugin(),
85
+ react(),
86
+ // ...your other plugins
87
+ ],
88
+ });
89
+ ```
90
+
91
+ ### Installer shows "manual modification required"
92
+
93
+ This happens when the installer can't find a `plugins: [` array in your Vite config. Manually add the import and plugin as shown above.
94
+
95
+ ## Configuration Options
96
+
97
+ ```typescript
98
+ import { frontmanPlugin } from '@frontman-ai/vite';
99
+
100
+ frontmanPlugin({
101
+ // All options are optional
102
+ isDev: true, // Development mode (default: NODE_ENV !== "production")
103
+ basePath: 'frontman', // Base path for Frontman routes (default: "frontman")
104
+ host: 'frontman.local:4000', // Frontman server host (default: env FRONTMAN_HOST or "frontman.local:4000")
105
+ clientUrl: 'https://...', // Custom client bundle URL
106
+ clientCssUrl: 'https://...', // Custom client CSS URL
107
+ entrypointUrl: 'http://...', // Custom entrypoint URL for the API
108
+ isLightTheme: false, // Use light theme (default: false / dark)
109
+ projectRoot: '.', // Project root directory (default: env PROJECT_ROOT or cwd)
110
+ sourceRoot: '.', // Source root for resolving file paths (default: projectRoot)
111
+ });
112
+ ```
113
+
114
+ ### Understanding the `host` Option
115
+
116
+ The `host` option specifies the Frontman server that the client UI will connect to for AI capabilities. When you visit `/frontman` in your Vite app:
117
+
118
+ 1. The plugin serves the Frontman UI HTML
119
+ 2. The UI loads the client JavaScript with `?host=<your-host>`
120
+ 3. The client establishes a WebSocket connection to `wss://<host>/socket`
121
+ 4. AI interactions and tool calls flow through this connection
122
+
123
+ The plugin itself doesn't connect to the Frontman server - it only passes the host to the client.
124
+
125
+ **Examples:**
126
+ - Production: `host: 'api.frontman.sh'` → client connects to `wss://api.frontman.sh/socket`
127
+ - Local dev: `host: 'frontman.local:4000'` → client connects to `wss://frontman.local:4000/socket`
128
+
129
+ ## Supported Frameworks
130
+
131
+ This plugin works with any Vite-based project:
132
+
133
+ - React (via `@vitejs/plugin-react`)
134
+ - Vue (via `@vitejs/plugin-vue`)
135
+ - Svelte (via `@sveltejs/vite-plugin-svelte`)
136
+ - SolidJS (via `vite-plugin-solid`)
137
+ - Vanilla JS/TS
138
+ - Any other Vite-compatible framework
139
+
140
+ | Version | Status |
141
+ |---------|--------|
142
+ | Vite 5.x | Fully supported |
143
+ | Vite 6.x | Fully supported |
144
+
145
+ ## Architecture
146
+
147
+ ```
148
+ Vite Dev Server
149
+
150
+ ├─> configureServer hook
151
+ │ └─> frontmanPlugin registers Connect middleware
152
+ │ └─> Adapts Node.js req/res ↔ Web API Request/Response
153
+
154
+ ├─> GET /frontman
155
+ │ └─> Serves Frontman UI (HTML + client bundle)
156
+ │ └─> Client connects to Frontman server via WebSocket
157
+
158
+ ├─> GET /frontman/tools
159
+ │ └─> Returns tool definitions from ToolRegistry
160
+ │ └─> Core tools (file read, write, search, etc.)
161
+
162
+ ├─> POST /frontman/tools/call
163
+ │ └─> Executes tool → returns SSE stream with results
164
+
165
+ └─> POST /frontman/resolve-source-location
166
+ └─> Resolves source maps to original component locations
167
+ ```
168
+
169
+ ### Key Technical Details
170
+
171
+ **Node.js ↔ Web API Adapter**
172
+ - Vite's dev server uses Node.js `IncomingMessage`/`ServerResponse`
173
+ - Frontman middleware uses Web API `Request`/`Response`
174
+ - The plugin adapts between the two, including SSE stream piping
175
+
176
+ **Available Endpoints**
177
+
178
+ | Route | Method | Description |
179
+ |-------|--------|-------------|
180
+ | `GET /frontman` | GET | Serves the Frontman UI |
181
+ | `GET /frontman/tools` | GET | Returns available tool definitions |
182
+ | `POST /frontman/tools/call` | POST | Executes a tool call (SSE response) |
183
+ | `POST /frontman/resolve-source-location` | POST | Resolves source maps to original locations |
184
+ | `OPTIONS /frontman/*` | OPTIONS | CORS preflight handling |
185
+
186
+ Non-frontman routes pass through to Vite's normal dev server handling.
187
+
188
+ ## Troubleshooting
189
+
190
+ ### Frontman UI not loading
191
+
192
+ **Check 1: Verify the plugin is registered**
193
+ Make sure `frontmanPlugin()` is in your `vite.config.ts` plugins array and your dev server is running.
194
+
195
+ **Check 2: Check the URL**
196
+ The default path is `http://localhost:5173/frontman`. If you changed the `basePath` option, use that path instead.
197
+
198
+ **Check 3: Check for port conflicts**
199
+ If Vite is running on a different port, use that port in the URL (e.g., `http://localhost:3000/frontman`).
200
+
201
+ ### Installer shows "manual modification required"
202
+
203
+ This happens when the installer can't find a `plugins: [` array in your Vite config to inject into. Manually add the import and plugin call as shown in [Manual Setup](#manual-setup).
204
+
205
+ ### CORS errors in browser console
206
+
207
+ The plugin includes CORS headers for all `/frontman/*` routes. If you're seeing CORS errors, verify the request is going to the correct Vite dev server URL.
208
+
209
+ ## API
210
+
211
+ ### `frontmanPlugin(options?)`
212
+
213
+ Creates a Vite plugin that serves the Frontman UI and tool endpoints on the dev server.
214
+
215
+ ```typescript
216
+ import { frontmanPlugin } from '@frontman-ai/vite';
217
+
218
+ const plugin = frontmanPlugin({
219
+ host: string, // Frontman server host (default: env FRONTMAN_HOST or "frontman.local:4000")
220
+ basePath: string, // Base path (default: "frontman")
221
+ isDev: boolean, // Dev mode (default: NODE_ENV !== "production")
222
+ projectRoot: string, // Project root (default: env PROJECT_ROOT or cwd)
223
+ sourceRoot: string, // Source root (default: projectRoot)
224
+ clientUrl: string, // Custom client bundle URL
225
+ clientCssUrl: string, // Custom client CSS URL
226
+ entrypointUrl: string, // Custom entrypoint URL
227
+ isLightTheme: boolean, // Light theme (default: false)
228
+ });
229
+ ```
230
+
231
+ **Returns:** A Vite plugin object with `name: "frontman"` and a `configureServer` hook.
232
+
233
+ ## License
234
+
235
+ Apache-2.0
package/dist/cli.js ADDED
@@ -0,0 +1,836 @@
1
+ #!/usr/bin/env node
2
+ import * as Process from 'process';
3
+ import * as Fs from 'fs';
4
+ import * as Nodepath from 'path';
5
+
6
+ // ../../node_modules/@rescript/runtime/lib/es6/Primitive_option.js
7
+ function some(x) {
8
+ if (x === void 0) {
9
+ return {
10
+ BS_PRIVATE_NESTED_SOME_NONE: 0
11
+ };
12
+ } else if (x !== null && x.BS_PRIVATE_NESTED_SOME_NONE !== void 0) {
13
+ return {
14
+ BS_PRIVATE_NESTED_SOME_NONE: x.BS_PRIVATE_NESTED_SOME_NONE + 1 | 0
15
+ };
16
+ } else {
17
+ return x;
18
+ }
19
+ }
20
+ function fromNullable(x) {
21
+ if (x == null) {
22
+ return;
23
+ } else {
24
+ return some(x);
25
+ }
26
+ }
27
+ function valFromOption(x) {
28
+ if (x === null || x.BS_PRIVATE_NESTED_SOME_NONE === void 0) {
29
+ return x;
30
+ }
31
+ let depth = x.BS_PRIVATE_NESTED_SOME_NONE;
32
+ if (depth === 0) {
33
+ return;
34
+ } else {
35
+ return {
36
+ BS_PRIVATE_NESTED_SOME_NONE: depth - 1 | 0
37
+ };
38
+ }
39
+ }
40
+
41
+ // ../../node_modules/@rescript/runtime/lib/es6/Stdlib_Option.js
42
+ function mapOr(opt, $$default, f) {
43
+ if (opt !== void 0) {
44
+ return f(valFromOption(opt));
45
+ } else {
46
+ return $$default;
47
+ }
48
+ }
49
+ function map(opt, f) {
50
+ if (opt !== void 0) {
51
+ return some(f(valFromOption(opt)));
52
+ }
53
+ }
54
+ function flatMap(opt, f) {
55
+ if (opt !== void 0) {
56
+ return f(valFromOption(opt));
57
+ }
58
+ }
59
+ function getOr(opt, $$default) {
60
+ if (opt !== void 0) {
61
+ return valFromOption(opt);
62
+ } else {
63
+ return $$default;
64
+ }
65
+ }
66
+ function isSome(x) {
67
+ return x !== void 0;
68
+ }
69
+
70
+ // ../../node_modules/@rescript/runtime/lib/es6/Primitive_exceptions.js
71
+ function isExtension(e) {
72
+ if (e == null) {
73
+ return false;
74
+ } else {
75
+ return typeof e.RE_EXN_ID === "string";
76
+ }
77
+ }
78
+ function internalToException(e) {
79
+ if (isExtension(e)) {
80
+ return e;
81
+ } else {
82
+ return {
83
+ RE_EXN_ID: "JsExn",
84
+ _1: e
85
+ };
86
+ }
87
+ }
88
+
89
+ // ../bindings/src/ChildProcess.res.mjs
90
+ var execPromise = (async function(command, options) {
91
+ const { exec } = await import('child_process');
92
+ const { promisify } = await import('util');
93
+ const execP = promisify(exec);
94
+ return await execP(command, options);
95
+ });
96
+ var bufferToString = (function(value) {
97
+ if (value == null) return "";
98
+ if (typeof value === "string") return value;
99
+ if (Buffer.isBuffer(value)) return value.toString("utf8");
100
+ return String(value);
101
+ });
102
+ async function execWithOptions(command, options) {
103
+ try {
104
+ let newrecord = { ...options };
105
+ newrecord.maxBuffer = getOr(options.maxBuffer, 52428800);
106
+ let result = await execPromise(command, newrecord);
107
+ return {
108
+ TAG: "Ok",
109
+ _0: {
110
+ stdout: bufferToString(result.stdout),
111
+ stderr: bufferToString(result.stderr)
112
+ }
113
+ };
114
+ } catch (raw_exn) {
115
+ let exn = internalToException(raw_exn);
116
+ let actualError = getOr(fromNullable(exn._1), exn);
117
+ return {
118
+ TAG: "Error",
119
+ _0: {
120
+ code: fromNullable(actualError.code),
121
+ stdout: getOr(map(fromNullable(actualError.stdout), bufferToString), ""),
122
+ stderr: getOr(map(fromNullable(actualError.stderr), bufferToString), "")
123
+ }
124
+ };
125
+ }
126
+ }
127
+
128
+ // src/cli/FrontmanVite__Cli__Style.res.mjs
129
+ var esc = "\x1B";
130
+ function purple(text) {
131
+ return esc + `[38;2;152;93;247m` + text + esc + `[0m`;
132
+ }
133
+ function purpleBold(text) {
134
+ return esc + `[1;38;2;152;93;247m` + text + esc + `[0m`;
135
+ }
136
+ function purpleDim(text) {
137
+ return esc + `[38;2;128;81;205m` + text + esc + `[0m`;
138
+ }
139
+ function green(text) {
140
+ return esc + `[32m` + text + esc + `[0m`;
141
+ }
142
+ function yellow(text) {
143
+ return esc + `[33m` + text + esc + `[0m`;
144
+ }
145
+ function yellowBold(text) {
146
+ return esc + `[1;33m` + text + esc + `[0m`;
147
+ }
148
+ function bold(text) {
149
+ return esc + `[1m` + text + esc + `[0m`;
150
+ }
151
+ function dim(text) {
152
+ return esc + `[2m` + text + esc + `[0m`;
153
+ }
154
+ var check = green("\u2714");
155
+ var warn = yellow("\u26A0");
156
+ var bullet = purple("\u25B8");
157
+ var divider = purple("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
158
+
159
+ // ../../node_modules/@rescript/runtime/lib/es6/Stdlib_JSON.js
160
+ function bool(json) {
161
+ if (typeof json === "boolean") {
162
+ return json;
163
+ }
164
+ }
165
+ function $$null(json) {
166
+ if (json === null) {
167
+ return null;
168
+ }
169
+ }
170
+ function string(json) {
171
+ if (typeof json === "string") {
172
+ return json;
173
+ }
174
+ }
175
+ function float(json) {
176
+ if (typeof json === "number") {
177
+ return json;
178
+ }
179
+ }
180
+ function object(json) {
181
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
182
+ return json;
183
+ }
184
+ }
185
+ function array(json) {
186
+ if (Array.isArray(json)) {
187
+ return json;
188
+ }
189
+ }
190
+ var Decode = {
191
+ bool,
192
+ $$null,
193
+ string,
194
+ float,
195
+ object,
196
+ array
197
+ };
198
+
199
+ // src/cli/FrontmanVite__Cli__Detect.res.mjs
200
+ async function fileExists(path) {
201
+ try {
202
+ await Fs.promises.access(path);
203
+ return true;
204
+ } catch (exn) {
205
+ return false;
206
+ }
207
+ }
208
+ async function readFile(path) {
209
+ try {
210
+ return await Fs.promises.readFile(path, "utf8");
211
+ } catch (exn) {
212
+ return;
213
+ }
214
+ }
215
+ async function detectPackageManager(projectDir) {
216
+ let checkDir = async (dir) => {
217
+ let bunLockb = Nodepath.join(dir, "bun.lockb");
218
+ let bunLock = Nodepath.join(dir, "bun.lock");
219
+ let denoLock = Nodepath.join(dir, "deno.lock");
220
+ let pnpmLock = Nodepath.join(dir, "pnpm-lock.yaml");
221
+ let yarnLock = Nodepath.join(dir, "yarn.lock");
222
+ let npmLock = Nodepath.join(dir, "package-lock.json");
223
+ if (await fileExists(bunLockb) || await fileExists(bunLock)) {
224
+ return "Bun";
225
+ } else if (await fileExists(denoLock)) {
226
+ return "Deno";
227
+ } else if (await fileExists(pnpmLock)) {
228
+ return "Pnpm";
229
+ } else if (await fileExists(yarnLock)) {
230
+ return "Yarn";
231
+ } else if (await fileExists(npmLock)) {
232
+ return "Npm";
233
+ } else {
234
+ return;
235
+ }
236
+ };
237
+ let pm = await checkDir(projectDir);
238
+ if (pm !== void 0) {
239
+ return pm;
240
+ }
241
+ let parentDir = Nodepath.dirname(projectDir);
242
+ if (parentDir === projectDir) {
243
+ return "Npm";
244
+ }
245
+ let pm$1 = await checkDir(parentDir);
246
+ if (pm$1 !== void 0) {
247
+ return pm$1;
248
+ }
249
+ let grandparentDir = Nodepath.dirname(parentDir);
250
+ if (grandparentDir === parentDir) {
251
+ return "Npm";
252
+ }
253
+ let pm$2 = await checkDir(grandparentDir);
254
+ if (pm$2 !== void 0) {
255
+ return pm$2;
256
+ } else {
257
+ return "Npm";
258
+ }
259
+ }
260
+ var frontmanImportPattern = /@frontman-ai\/vite|frontman-vite|frontmanPlugin/;
261
+ async function findViteConfig(projectDir) {
262
+ let candidates = [
263
+ "vite.config.ts",
264
+ "vite.config.js",
265
+ "vite.config.mts",
266
+ "vite.config.mjs"
267
+ ];
268
+ let check2 = async (remaining) => {
269
+ let fileName = remaining[0];
270
+ if (fileName === void 0) {
271
+ return;
272
+ }
273
+ let filePath = Nodepath.join(projectDir, fileName);
274
+ let content = await readFile(filePath);
275
+ if (content !== void 0) {
276
+ return [
277
+ fileName,
278
+ content
279
+ ];
280
+ }
281
+ let rest = remaining.slice(1, remaining.length);
282
+ return await check2(rest);
283
+ };
284
+ return await check2(candidates);
285
+ }
286
+ async function analyzeViteConfig(projectDir) {
287
+ let match = await findViteConfig(projectDir);
288
+ if (match === void 0) {
289
+ return [
290
+ "NotFound",
291
+ "vite.config.ts"
292
+ ];
293
+ }
294
+ let content = match[1];
295
+ let fileName = match[0];
296
+ if (frontmanImportPattern.test(content)) {
297
+ return [
298
+ "HasFrontman",
299
+ fileName
300
+ ];
301
+ }
302
+ let filePath = Nodepath.join(projectDir, fileName);
303
+ return [
304
+ {
305
+ TAG: "NeedsFrontman",
306
+ filePath,
307
+ content
308
+ },
309
+ fileName
310
+ ];
311
+ }
312
+ async function hasPackageJson(projectDir) {
313
+ return await fileExists(Nodepath.join(projectDir, "package.json"));
314
+ }
315
+ async function hasViteDependency(projectDir) {
316
+ let pkgPath = Nodepath.join(projectDir, "package.json");
317
+ let content = await readFile(pkgPath);
318
+ if (content === void 0) {
319
+ return false;
320
+ }
321
+ try {
322
+ let json = JSON.parse(content);
323
+ let obj = Decode.object(json);
324
+ if (obj === void 0) {
325
+ return false;
326
+ }
327
+ let checkDeps = (key) => mapOr(flatMap(obj[key], Decode.object), false, (deps) => isSome(deps["vite"]));
328
+ if (checkDeps("dependencies")) {
329
+ return true;
330
+ } else {
331
+ return checkDeps("devDependencies");
332
+ }
333
+ } catch (exn) {
334
+ return false;
335
+ }
336
+ }
337
+ async function detect(projectDir) {
338
+ let hasPackage = await hasPackageJson(projectDir);
339
+ if (!hasPackage) {
340
+ return {
341
+ TAG: "Error",
342
+ _0: "No package.json found. Please run from your Vite project root."
343
+ };
344
+ }
345
+ let hasVite = await hasViteDependency(projectDir);
346
+ if (!hasVite) {
347
+ return {
348
+ TAG: "Error",
349
+ _0: "Could not find vite in package.json. Please verify this is a Vite project."
350
+ };
351
+ }
352
+ let match = await analyzeViteConfig(projectDir);
353
+ let packageManager = await detectPackageManager(projectDir);
354
+ return {
355
+ TAG: "Ok",
356
+ _0: {
357
+ viteConfig: match[0],
358
+ packageManager,
359
+ viteConfigFileName: match[1]
360
+ }
361
+ };
362
+ }
363
+ function getPackageManagerCommand(pm) {
364
+ switch (pm) {
365
+ case "Npm":
366
+ return "npm";
367
+ case "Yarn":
368
+ return "npx yarn";
369
+ case "Pnpm":
370
+ return "npx pnpm";
371
+ case "Bun":
372
+ return "bun";
373
+ case "Deno":
374
+ return "deno";
375
+ }
376
+ }
377
+ function getDevCommand(pm) {
378
+ switch (pm) {
379
+ case "Npm":
380
+ return "npm run dev";
381
+ case "Yarn":
382
+ return "yarn dev";
383
+ case "Pnpm":
384
+ return "pnpm dev";
385
+ case "Bun":
386
+ return "bun dev";
387
+ case "Deno":
388
+ return "deno task dev";
389
+ }
390
+ }
391
+ function getInstallArgs(pm) {
392
+ switch (pm) {
393
+ case "Npm":
394
+ return [
395
+ "install",
396
+ "-D"
397
+ ];
398
+ case "Yarn":
399
+ return [
400
+ "add",
401
+ "-D"
402
+ ];
403
+ case "Pnpm":
404
+ return [
405
+ "add",
406
+ "--save-dev"
407
+ ];
408
+ case "Bun":
409
+ case "Deno":
410
+ return [
411
+ "add",
412
+ "--dev"
413
+ ];
414
+ }
415
+ }
416
+
417
+ // src/cli/FrontmanVite__Cli__Templates.res.mjs
418
+ function banner() {
419
+ let l1 = purpleBold(" ___ _ ");
420
+ let l2 = purpleBold(" | __| _ ___ _ _ | |_ _ __ __ _ _ _ ");
421
+ let l3 = purpleBold(" | _| '_/ _ \\ ' \\| _| ' \\/ _` | ' \\ ");
422
+ let l4 = purpleBold(" |_||_| \\___/_||_|\\__|_|_|_\\__,_|_||_|");
423
+ let tagline = purpleDim(" AI that sees your DOM and edits your frontend");
424
+ return `
425
+ ` + l1 + `
426
+ ` + l2 + `
427
+ ` + l3 + `
428
+ ` + l4 + `
429
+
430
+ ` + tagline + `
431
+ `;
432
+ }
433
+ function viteConfig(fileName) {
434
+ let bar = yellow("|");
435
+ return ` ` + bar + `
436
+ ` + bar + ` ` + yellowBold(fileName) + ` needs manual modification.
437
+ ` + bar + `
438
+ ` + bar + ` ` + purple("1.") + ` Add import at the top of the file:
439
+ ` + bar + `
440
+ ` + bar + ` ` + dim("import { frontmanPlugin } from '@frontman-ai/vite';") + `
441
+ ` + bar + `
442
+ ` + bar + ` ` + purple("2.") + ` Add ` + bold("frontmanPlugin()") + ` to your plugins array:
443
+ ` + bar + `
444
+ ` + bar + ` ` + dim("plugins: [") + `
445
+ ` + bar + ` ` + dim(" frontmanPlugin(),") + `
446
+ ` + bar + ` ` + dim(" // ...your other plugins") + `
447
+ ` + bar + ` ` + dim("],") + `
448
+ ` + bar + `
449
+ ` + bar + ` ` + bold("Docs:") + ` ` + dim("https://frontman.sh/docs/vite") + `
450
+ ` + bar;
451
+ }
452
+ var ManualInstructions = {
453
+ viteConfig
454
+ };
455
+ function fileCreated(fileName) {
456
+ return ` ` + check + ` Created ` + bold(fileName);
457
+ }
458
+ function fileUpdated(fileName) {
459
+ return ` ` + check + ` Updated ` + bold(fileName) + ` ` + dim("(added frontmanPlugin)");
460
+ }
461
+ function fileSkipped(fileName) {
462
+ return ` ` + purple("\u2013") + ` Skipped ` + bold(fileName) + ` ` + dim("(already configured)");
463
+ }
464
+ function manualEditRequired(fileName) {
465
+ return ` ` + warn + ` ` + bold(fileName) + ` requires manual setup ` + dim("(see details below)");
466
+ }
467
+ function installComplete(devCommand, port) {
468
+ return `
469
+ ` + purpleBold("Frontman setup complete!") + `
470
+
471
+ ` + purpleBold("Next steps:") + `
472
+ ` + purple("1.") + ` Start your dev server ` + dim(devCommand) + `
473
+ ` + purple("2.") + ` Open your browser to ` + dim(`http://localhost:` + port + `/frontman`) + `
474
+
475
+ ` + purple("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510") + `
476
+ ` + purple("\u2502") + ` ` + purple("\u2502") + `
477
+ ` + purple("\u2502") + ` Questions? Comments? Need support? ` + purple("\u2502") + `
478
+ ` + purple("\u2502") + ` ` + purple("\u2502") + `
479
+ ` + purple("\u2502") + ` Join us on Discord: ` + purple("\u2502") + `
480
+ ` + purple("\u2502") + ` ` + purpleDim("https://discord.gg/J77jBzMM") + ` ` + purple("\u2502") + `
481
+ ` + purple("\u2502") + ` ` + purple("\u2502") + `
482
+ ` + purple("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518") + `
483
+ `;
484
+ }
485
+ var dryRunHeader = ` ` + warn + ` ` + yellowBold("DRY RUN MODE") + ` ` + dim("\u2014 No files will be created") + `
486
+ `;
487
+ var SuccessMessages = {
488
+ fileCreated,
489
+ fileUpdated,
490
+ fileSkipped,
491
+ manualEditRequired,
492
+ installComplete,
493
+ dryRunHeader
494
+ };
495
+ var importLine = `import { frontmanPlugin } from '@frontman-ai/vite';`;
496
+
497
+ // src/cli/FrontmanVite__Cli__Install.res.mjs
498
+ async function installDependencies(projectDir, packageManager, dryRun) {
499
+ let pm = getPackageManagerCommand(packageManager);
500
+ let args = getInstallArgs(packageManager);
501
+ let packages = ["@frontman-ai/vite"];
502
+ let packages$1;
503
+ packages$1 = packageManager === "Deno" ? packages.map((p) => "npm:" + p) : packages;
504
+ let cmd = pm + ` ` + args.join(" ") + ` ` + packages$1.join(" ");
505
+ if (dryRun) {
506
+ console.log(` ` + dim(`Would run: ` + cmd));
507
+ return {
508
+ TAG: "Ok",
509
+ _0: void 0
510
+ };
511
+ }
512
+ console.log(` ` + purple("Installing dependencies with " + pm + "..."));
513
+ let err = await execWithOptions(cmd, {
514
+ cwd: projectDir
515
+ });
516
+ if (err.TAG === "Ok") {
517
+ console.log(` ` + check + ` Dependencies installed`);
518
+ return {
519
+ TAG: "Ok",
520
+ _0: void 0
521
+ };
522
+ }
523
+ let err$1 = err._0;
524
+ let stderr = err$1.stderr === "" ? "Unknown error" : err$1.stderr;
525
+ return {
526
+ TAG: "Error",
527
+ _0: `Failed to install dependencies: ` + stderr
528
+ };
529
+ }
530
+ function injectFrontmanPlugin(content) {
531
+ let pluginsPattern = /plugins\s*:\s*\[/;
532
+ if (!pluginsPattern.test(content)) {
533
+ return {
534
+ TAG: "Error",
535
+ _0: "Could not find a `plugins: [` array in your Vite config"
536
+ };
537
+ }
538
+ let importStatement = importLine + "\n";
539
+ let lines = content.split("\n");
540
+ let lastImportIdx = {
541
+ contents: -1
542
+ };
543
+ lines.forEach((line, idx) => {
544
+ let trimmed = line.trim();
545
+ if (trimmed.startsWith("import ") || trimmed.startsWith("import{")) {
546
+ lastImportIdx.contents = idx;
547
+ return;
548
+ }
549
+ });
550
+ let contentWithImport;
551
+ if (lastImportIdx.contents >= 0) {
552
+ let before = lines.slice(0, lastImportIdx.contents + 1 | 0).join("\n");
553
+ let after = lines.slice(lastImportIdx.contents + 1 | 0, lines.length).join("\n");
554
+ contentWithImport = before + "\n" + importStatement + after;
555
+ } else {
556
+ contentWithImport = importStatement + "\n" + content;
557
+ }
558
+ let result = contentWithImport.replace(/plugins\s*:\s*\[/, "plugins: [\n frontmanPlugin(),");
559
+ return {
560
+ TAG: "Ok",
561
+ _0: result
562
+ };
563
+ }
564
+ async function handleViteConfig(projectDir, info, dryRun) {
565
+ let match = info.viteConfig;
566
+ if (typeof match !== "object") {
567
+ if (match === "NotFound") {
568
+ let fileName = "vite.config.ts";
569
+ let filePath = Nodepath.join(projectDir, fileName);
570
+ if (dryRun) {
571
+ console.log(` ` + dim(`Would create: ` + fileName));
572
+ return {
573
+ TAG: "Ok",
574
+ _0: void 0
575
+ };
576
+ } else {
577
+ await Fs.promises.writeFile(filePath, `import { defineConfig } from 'vite';
578
+ import { frontmanPlugin } from '@frontman-ai/vite';
579
+
580
+ export default defineConfig({
581
+ plugins: [
582
+ frontmanPlugin(),
583
+ ],
584
+ });
585
+ `, "utf8");
586
+ console.log(SuccessMessages.fileCreated(fileName));
587
+ return {
588
+ TAG: "Ok",
589
+ _0: void 0
590
+ };
591
+ }
592
+ }
593
+ console.log(SuccessMessages.fileSkipped(info.viteConfigFileName));
594
+ return {
595
+ TAG: "Ok",
596
+ _0: void 0
597
+ };
598
+ } else {
599
+ if (dryRun) {
600
+ console.log(` ` + dim(`Would modify: ` + info.viteConfigFileName));
601
+ return {
602
+ TAG: "Ok",
603
+ _0: void 0
604
+ };
605
+ }
606
+ let newContent = injectFrontmanPlugin(match.content);
607
+ if (newContent.TAG === "Ok") {
608
+ await Fs.promises.writeFile(match.filePath, newContent._0, "utf8");
609
+ console.log(SuccessMessages.fileUpdated(info.viteConfigFileName));
610
+ return {
611
+ TAG: "Ok",
612
+ _0: void 0
613
+ };
614
+ }
615
+ console.log(SuccessMessages.manualEditRequired(info.viteConfigFileName));
616
+ return {
617
+ TAG: "Error",
618
+ _0: ManualInstructions.viteConfig(info.viteConfigFileName)
619
+ };
620
+ }
621
+ }
622
+ async function run(options) {
623
+ let projectDir = getOr(options.prefix, process.cwd());
624
+ console.log(banner());
625
+ console.log(` ` + bullet + ` ` + bold("Server:") + ` ` + options.server);
626
+ if (options.dryRun) {
627
+ console.log("");
628
+ console.log(SuccessMessages.dryRunHeader);
629
+ }
630
+ let msg = await detect(projectDir);
631
+ if (msg.TAG === "Ok") {
632
+ let info = msg._0;
633
+ console.log(` ` + bullet + ` ` + bold("Detected:") + ` Vite project`);
634
+ console.log("");
635
+ if (!options.skipDeps) {
636
+ let msg$1 = await installDependencies(projectDir, info.packageManager, options.dryRun);
637
+ if (msg$1.TAG !== "Ok") {
638
+ console.error(` ` + warn + ` ` + msg$1._0);
639
+ }
640
+ console.log("");
641
+ }
642
+ let manualSteps = [];
643
+ let details = await handleViteConfig(projectDir, info, options.dryRun);
644
+ if (details.TAG !== "Ok") {
645
+ manualSteps.push(details._0);
646
+ }
647
+ if (manualSteps.length !== 0) {
648
+ console.log("");
649
+ console.log(` ` + divider);
650
+ console.log("");
651
+ console.log(` ` + yellowBold("Manual steps required:"));
652
+ console.log("");
653
+ manualSteps.forEach((step) => {
654
+ console.log(step);
655
+ });
656
+ console.log("");
657
+ return {
658
+ TAG: "PartialSuccess",
659
+ manualStepsRequired: manualSteps
660
+ };
661
+ }
662
+ if (!options.dryRun) {
663
+ let devCommand = getDevCommand(info.packageManager);
664
+ console.log("");
665
+ console.log(` ` + divider);
666
+ console.log(SuccessMessages.installComplete(devCommand, "5173"));
667
+ }
668
+ return "Success";
669
+ }
670
+ let msg$2 = msg._0;
671
+ console.error(` ` + warn + ` ` + bold("Error:") + ` ` + msg$2);
672
+ return {
673
+ TAG: "Failure",
674
+ _0: msg$2
675
+ };
676
+ }
677
+
678
+ // src/cli/FrontmanVite__Cli.res.mjs
679
+ var helpText = `
680
+ Frontman Vite CLI
681
+
682
+ Usage:
683
+ @frontman-ai/vite <command> [options]
684
+
685
+ Commands:
686
+ install Install Frontman in a Vite project
687
+
688
+ Options:
689
+ --server <host> Frontman server host (default: api.frontman.sh)
690
+ --prefix <path> Target directory (default: current directory)
691
+ --dry-run Preview changes without writing files
692
+ --skip-deps Skip dependency installation
693
+ --help Show this help message
694
+
695
+ Examples:
696
+ npx @frontman-ai/vite install
697
+ npx @frontman-ai/vite install --server frontman.company.com
698
+ npx @frontman-ai/vite install --dry-run
699
+ `;
700
+ function parseArgs(argv) {
701
+ let args = argv.slice(2, argv.length);
702
+ let _remaining = args;
703
+ let _result = {
704
+ command: void 0,
705
+ server: void 0,
706
+ prefix: void 0,
707
+ dryRun: false,
708
+ skipDeps: false,
709
+ help: false
710
+ };
711
+ while (true) {
712
+ let result = _result;
713
+ let remaining = _remaining;
714
+ let arg = remaining[0];
715
+ if (arg === void 0) {
716
+ return result;
717
+ }
718
+ let rest = remaining.slice(1, remaining.length);
719
+ switch (arg) {
720
+ case "--dry-run":
721
+ _result = {
722
+ command: result.command,
723
+ server: result.server,
724
+ prefix: result.prefix,
725
+ dryRun: true,
726
+ skipDeps: result.skipDeps,
727
+ help: result.help
728
+ };
729
+ _remaining = rest;
730
+ continue;
731
+ case "--prefix":
732
+ let value = rest[0];
733
+ let nextRest = rest.slice(1, rest.length);
734
+ _result = {
735
+ command: result.command,
736
+ server: result.server,
737
+ prefix: value,
738
+ dryRun: result.dryRun,
739
+ skipDeps: result.skipDeps,
740
+ help: result.help
741
+ };
742
+ _remaining = nextRest;
743
+ continue;
744
+ case "--server":
745
+ let value$1 = rest[0];
746
+ let nextRest$1 = rest.slice(1, rest.length);
747
+ _result = {
748
+ command: result.command,
749
+ server: value$1,
750
+ prefix: result.prefix,
751
+ dryRun: result.dryRun,
752
+ skipDeps: result.skipDeps,
753
+ help: result.help
754
+ };
755
+ _remaining = nextRest$1;
756
+ continue;
757
+ case "--skip-deps":
758
+ _result = {
759
+ command: result.command,
760
+ server: result.server,
761
+ prefix: result.prefix,
762
+ dryRun: result.dryRun,
763
+ skipDeps: true,
764
+ help: result.help
765
+ };
766
+ _remaining = rest;
767
+ continue;
768
+ case "--help":
769
+ case "-h":
770
+ break;
771
+ case "install":
772
+ _result = {
773
+ command: "install",
774
+ server: result.server,
775
+ prefix: result.prefix,
776
+ dryRun: result.dryRun,
777
+ skipDeps: result.skipDeps,
778
+ help: result.help
779
+ };
780
+ _remaining = rest;
781
+ continue;
782
+ default:
783
+ _remaining = rest;
784
+ continue;
785
+ }
786
+ _result = {
787
+ command: result.command,
788
+ server: result.server,
789
+ prefix: result.prefix,
790
+ dryRun: result.dryRun,
791
+ skipDeps: result.skipDeps,
792
+ help: true
793
+ };
794
+ _remaining = rest;
795
+ continue;
796
+ }
797
+ }
798
+ async function main() {
799
+ let args = parseArgs(process.argv);
800
+ if (args.help) {
801
+ console.log(helpText);
802
+ Process.exit(0);
803
+ }
804
+ let cmd = args.command;
805
+ if (cmd !== void 0) {
806
+ if (cmd === "install") {
807
+ let server = getOr(args.server, "api.frontman.sh");
808
+ let result = await run({
809
+ server,
810
+ prefix: args.prefix,
811
+ dryRun: args.dryRun,
812
+ skipDeps: args.skipDeps
813
+ });
814
+ if (typeof result !== "object") {
815
+ Process.exit(0);
816
+ return;
817
+ }
818
+ if (result.TAG === "PartialSuccess") {
819
+ Process.exit(0);
820
+ return;
821
+ }
822
+ Process.exit(1);
823
+ return;
824
+ } else {
825
+ console.error(`Unknown command: ` + cmd);
826
+ console.log(helpText);
827
+ Process.exit(1);
828
+ return;
829
+ }
830
+ } else {
831
+ console.log(helpText);
832
+ Process.exit(0);
833
+ return;
834
+ }
835
+ }
836
+ main();
package/dist/index.d.ts CHANGED
@@ -20,20 +20,22 @@ import * as FrontmanCore__ToolRegistry$FrontmanFrontmanCore from '@frontman/fron
20
20
  // Generated by ReScript, PLEASE EDIT WITH CARE
21
21
 
22
22
 
23
+ let productionHost = "api.frontman.sh";
24
+
23
25
  let host = process.env["FRONTMAN_HOST"];
24
26
 
25
27
  let defaultHost = host !== undefined ? host : "frontman.local:4000";
26
28
 
27
29
  function makeFromObject(config) {
28
- let isDev = Stdlib_Option.getOr(config.isDev, Stdlib_Option.mapOr(process.env["NODE_ENV"], true, env => env === "development"));
30
+ let host = Stdlib_Option.getOr(config.host, defaultHost);
31
+ let isDev = Stdlib_Option.getOr(config.isDev, host !== productionHost);
29
32
  let projectRoot = Stdlib_Option.getOr(Stdlib_Option.orElse(config.projectRoot, Stdlib_Option.orElse(process.env["PROJECT_ROOT"], process.env["PWD"])), ".");
30
33
  let sourceRoot = Stdlib_Option.getOr(config.sourceRoot, projectRoot);
31
34
  let basePath = Stdlib_Option.getOr(config.basePath, "frontman");
32
35
  let serverName = Stdlib_Option.getOr(config.serverName, "frontman-vite");
33
36
  let serverVersion = Stdlib_Option.getOr(config.serverVersion, "1.0.0");
34
- let host = Stdlib_Option.getOr(config.host, defaultHost);
35
37
  let isLightTheme = Stdlib_Option.getOr(config.isLightTheme, false);
36
- let baseUrl = Stdlib_Option.getOr(process.env["FRONTMAN_CLIENT_URL"], isDev ? "http://localhost:5173/src/Main.res.mjs" : "https://frontman.dev/frontman.es.js");
38
+ let baseUrl = Stdlib_Option.getOr(process.env["FRONTMAN_CLIENT_URL"], isDev ? "http://localhost:5173/src/Main.res.mjs" : "https://app.frontman.sh/frontman.es.js");
37
39
  let url = new URL(baseUrl);
38
40
  let clientUrl = Stdlib_Option.getOr(config.clientUrl, (url.searchParams.set("clientName", "vite"), url.searchParams.set("host", host), url.href));
39
41
  let parsedUrl = new URL(clientUrl);
package/dist/index.js CHANGED
@@ -110,18 +110,19 @@ function orElse(opt, other) {
110
110
  }
111
111
 
112
112
  // src/FrontmanVite__Config.res.mjs
113
+ var productionHost = "api.frontman.sh";
113
114
  var host = process.env["FRONTMAN_HOST"];
114
115
  var defaultHost = host !== void 0 ? host : "frontman.local:4000";
115
116
  function makeFromObject(config) {
116
- let isDev = getOr(config.isDev, mapOr(process.env["NODE_ENV"], true, (env) => env === "development"));
117
+ let host2 = getOr(config.host, defaultHost);
118
+ let isDev = getOr(config.isDev, host2 !== productionHost);
117
119
  let projectRoot = getOr(orElse(config.projectRoot, orElse(process.env["PROJECT_ROOT"], process.env["PWD"])), ".");
118
120
  let sourceRoot = getOr(config.sourceRoot, projectRoot);
119
121
  let basePath = getOr(config.basePath, "frontman");
120
122
  let serverName = getOr(config.serverName, "frontman-vite");
121
123
  let serverVersion = getOr(config.serverVersion, "1.0.0");
122
- let host2 = getOr(config.host, defaultHost);
123
124
  let isLightTheme = getOr(config.isLightTheme, false);
124
- let baseUrl = getOr(process.env["FRONTMAN_CLIENT_URL"], isDev ? "http://localhost:5173/src/Main.res.mjs" : "https://frontman.dev/frontman.es.js");
125
+ let baseUrl = getOr(process.env["FRONTMAN_CLIENT_URL"], isDev ? "http://localhost:5173/src/Main.res.mjs" : "https://app.frontman.sh/frontman.es.js");
125
126
  let url2 = new URL(baseUrl);
126
127
  let clientUrl = getOr(config.clientUrl, (url2.searchParams.set("clientName", "vite"), url2.searchParams.set("host", host2), url2.href));
127
128
  let parsedUrl = new URL(clientUrl);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.0",
6
+ "version": "0.1.2",
7
7
  "description": "Vite integration for Frontman - AI-powered development tools",
8
8
  "license": "Apache-2.0",
9
9
  "author": "Frontman AI",
@@ -34,6 +34,10 @@
34
34
  "default": "./dist/index.js"
35
35
  }
36
36
  },
37
+ "bin": {
38
+ "frontman-vite": "./dist/cli.js",
39
+ "@frontman-ai/vite": "./dist/cli.js"
40
+ },
37
41
  "files": [
38
42
  "dist",
39
43
  "README.md"