aidevops 3.21.12 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/aidevops.sh +3 -2
  4. package/bun.lock +573 -0
  5. package/package.json +12 -1
  6. package/packages/gui-api/package.json +9 -0
  7. package/packages/gui-api/src/app.ts +49 -0
  8. package/packages/gui-api/src/file-adapter.ts +207 -0
  9. package/packages/gui-api/src/server.ts +12 -0
  10. package/packages/gui-api/src/status-adapter-utils.ts +108 -0
  11. package/packages/gui-api/src/status-adapter.ts +484 -0
  12. package/packages/gui-api/src/status-local-repos.ts +239 -0
  13. package/packages/gui-desktop/package.json +6 -0
  14. package/packages/gui-desktop/scripts/install-macos-app.sh +1020 -0
  15. package/packages/gui-shared/package.json +9 -0
  16. package/packages/gui-shared/src/contracts.ts +341 -0
  17. package/packages/gui-shared/src/fixtures.ts +250 -0
  18. package/packages/gui-shared/src/index.ts +2 -0
  19. package/packages/gui-web/index.html +24 -0
  20. package/packages/gui-web/package.json +25 -0
  21. package/packages/gui-web/src/App.tsx +269 -0
  22. package/packages/gui-web/src/AppNavigation.tsx +379 -0
  23. package/packages/gui-web/src/AppWorkspace.tsx +114 -0
  24. package/packages/gui-web/src/FileExplorerSurface.tsx +153 -0
  25. package/packages/gui-web/src/InventorySurfaces.tsx +117 -0
  26. package/packages/gui-web/src/PathActions.tsx +17 -0
  27. package/packages/gui-web/src/StatusSurfaces.tsx +396 -0
  28. package/packages/gui-web/src/app-model.ts +546 -0
  29. package/packages/gui-web/src/dashboard.ts +79 -0
  30. package/packages/gui-web/src/main.tsx +24 -0
  31. package/packages/gui-web/src/status-client.ts +93 -0
  32. package/packages/gui-web/src/styles.css +1540 -0
  33. package/packages/gui-web/vite.config.ts +14 -0
  34. package/setup.sh +78 -3
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@aidevops/gui-shared",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ }
9
+ }
@@ -0,0 +1,341 @@
1
+ export type GuiRouteClassification = "read" | "write" | "destructive";
2
+
3
+ export type GuiOperationId = "setup.status.read" | "capabilities.read" | "filesystem.read";
4
+
5
+ export type GuiFileRootId = "agents" | "config" | "localSetup" | "git";
6
+
7
+ export interface GuiSourceRef {
8
+ surface: string;
9
+ authority: string;
10
+ path_refs: string[];
11
+ }
12
+
13
+ export interface GuiResponseEnvelope<TData> {
14
+ ok: boolean;
15
+ operation_id: GuiOperationId;
16
+ source: GuiSourceRef;
17
+ data: TData;
18
+ warnings: string[];
19
+ errors: string[];
20
+ redactions: string[];
21
+ observed_at: string;
22
+ }
23
+
24
+ export interface GuiRouteManifest {
25
+ route: string;
26
+ method: "GET";
27
+ operation_id: GuiOperationId;
28
+ classification: GuiRouteClassification;
29
+ source_surface: string;
30
+ adapter: string;
31
+ command_pattern: readonly string[];
32
+ redactions: readonly string[];
33
+ }
34
+
35
+ export interface GuiFileRootDefinition {
36
+ id: GuiFileRootId;
37
+ label: string;
38
+ path_ref: string;
39
+ description: string;
40
+ preview_policy: "agents_markdown_and_code" | "metadata_only";
41
+ }
42
+
43
+ export interface GuiFileEntry {
44
+ name: string;
45
+ kind: "directory" | "file";
46
+ path_ref: string;
47
+ relative_path: string;
48
+ extension: string;
49
+ preview_allowed: boolean;
50
+ }
51
+
52
+ export interface GuiFilePreview {
53
+ path_ref: string;
54
+ relative_path: string;
55
+ mode: "markdown" | "code" | "text" | "blocked";
56
+ language: string;
57
+ content: string;
58
+ truncated: boolean;
59
+ reason: string;
60
+ }
61
+
62
+ export interface GuiFileExplorerData {
63
+ root: GuiFileRootDefinition;
64
+ current_path_ref: string;
65
+ current_relative_path: string;
66
+ entries: GuiFileEntry[];
67
+ selected_preview: GuiFilePreview | null;
68
+ entry_limit: number;
69
+ }
70
+
71
+ export interface GuiSecretReference {
72
+ name: string;
73
+ status: "configured" | "missing" | "unchecked";
74
+ }
75
+
76
+ export interface GuiNavigationItem {
77
+ id: string;
78
+ label: string;
79
+ description: string;
80
+ }
81
+
82
+ export interface GuiSettingsSummary {
83
+ path_ref: string;
84
+ health: "present" | "missing" | "invalid" | "unchecked";
85
+ key_count: number;
86
+ keys: string[];
87
+ value_policy: "keys_only_no_values";
88
+ }
89
+
90
+ export interface GuiRepoSummary {
91
+ name: string;
92
+ platform: string;
93
+ slug: string;
94
+ local_path_status: "present" | "missing" | "not_provided" | "unchecked";
95
+ }
96
+
97
+ export interface GuiRepoRegistrySummary {
98
+ path_ref: string;
99
+ health: "present" | "missing" | "invalid" | "unchecked";
100
+ total: number;
101
+ repos: GuiRepoSummary[];
102
+ }
103
+
104
+ export interface GuiLocalRepoRemote {
105
+ name: string;
106
+ url_ref: string;
107
+ }
108
+
109
+ export interface GuiLocalRepoSetupSummary {
110
+ name: string;
111
+ path_ref: string;
112
+ aidevops_version: string;
113
+ default_branch: string;
114
+ remotes: GuiLocalRepoRemote[];
115
+ registered: boolean;
116
+ pulse: boolean | null;
117
+ local_only: boolean;
118
+ init_scope: string;
119
+ knowledge: string;
120
+ priority: string;
121
+ has_interface: boolean | null;
122
+ features: string[];
123
+ settings_policy: "read_only_no_writes";
124
+ }
125
+
126
+ export interface GuiLocalReposSetupSummary {
127
+ path_ref: string;
128
+ health: "present" | "missing" | "invalid" | "unchecked";
129
+ total: number;
130
+ excluded_worktrees: number;
131
+ repos: GuiLocalRepoSetupSummary[];
132
+ }
133
+
134
+ export type GuiAiProviderId = "anthropic" | "openai" | "cursor" | "google";
135
+
136
+ export interface GuiOAuthPoolAccountSummary {
137
+ email_ref: string;
138
+ status: string;
139
+ priority: number | null;
140
+ last_used: string;
141
+ expires_at: string;
142
+ cooldown_until: string | null;
143
+ }
144
+
145
+ export interface GuiOAuthProviderSummary {
146
+ provider: GuiAiProviderId;
147
+ configured: boolean;
148
+ total: number;
149
+ available: number;
150
+ active_or_idle: number;
151
+ rate_limited: number;
152
+ auth_errors: number;
153
+ pending_token: boolean;
154
+ accounts: GuiOAuthPoolAccountSummary[];
155
+ }
156
+
157
+ export interface GuiOAuthPoolSummary {
158
+ path_ref: string;
159
+ health: "present" | "missing" | "invalid" | "unchecked";
160
+ value_policy: "metadata_only_no_tokens";
161
+ providers: GuiOAuthProviderSummary[];
162
+ }
163
+
164
+ export interface GuiSetupTargetSummary {
165
+ label: string;
166
+ path_ref: string;
167
+ health: "present" | "missing" | "unchecked";
168
+ purpose: string;
169
+ installed_version: string;
170
+ latest_version: string;
171
+ needs_update: boolean;
172
+ }
173
+
174
+ export interface GuiAiAppSummary {
175
+ name: string;
176
+ status: "found" | "missing" | "unchecked";
177
+ app_path_ref: string;
178
+ binary_path_ref: string;
179
+ config_path_ref: string;
180
+ aidevops_target_path_ref: string;
181
+ app_version: string;
182
+ aidevops_version: string;
183
+ latest_version: string;
184
+ needs_update: boolean;
185
+ }
186
+
187
+ export interface GuiCapabilitySummary {
188
+ id: string;
189
+ label: string;
190
+ status: "available" | "planned" | "placeholder";
191
+ doc_ref: string;
192
+ }
193
+
194
+ export interface GuiMachineSummary {
195
+ id: string;
196
+ label: string;
197
+ initials: string;
198
+ username: string;
199
+ hostname: string;
200
+ local_ips: string[];
201
+ public_ip: string | null;
202
+ }
203
+
204
+ export interface GuiStatusData {
205
+ aidevops_version: string;
206
+ update: {
207
+ running_version: string;
208
+ installed_version: string;
209
+ restart_required: boolean;
210
+ message: string;
211
+ };
212
+ runtime: {
213
+ host: "local";
214
+ api: "hono";
215
+ read_only: true;
216
+ };
217
+ machine: GuiMachineSummary;
218
+ paths: Array<{
219
+ label: string;
220
+ path_ref: string;
221
+ health: "present" | "missing" | "unchecked";
222
+ }>;
223
+ helper_availability: Array<{
224
+ name: string;
225
+ status: "available" | "missing" | "unchecked";
226
+ }>;
227
+ navigation: GuiNavigationItem[];
228
+ settings: GuiSettingsSummary;
229
+ repos: GuiRepoRegistrySummary;
230
+ local_repos: GuiLocalReposSetupSummary;
231
+ oauth_pool: GuiOAuthPoolSummary;
232
+ setup_targets: GuiSetupTargetSummary[];
233
+ ai_apps: GuiAiAppSummary[];
234
+ capabilities: GuiCapabilitySummary[];
235
+ secrets: GuiSecretReference[];
236
+ placeholders: string[];
237
+ }
238
+
239
+ export const STATUS_ROUTE_MANIFEST: GuiRouteManifest = {
240
+ route: "/api/status",
241
+ method: "GET",
242
+ operation_id: "setup.status.read",
243
+ classification: "read",
244
+ source_surface: "setup",
245
+ adapter: "statusAdapter.readStatus",
246
+ command_pattern: ["aidevops", "status"],
247
+ redactions: ["secret_values", "credential_paths", "private_key_material"],
248
+ };
249
+
250
+ export const FILE_EXPLORER_ROUTE_MANIFEST: GuiRouteManifest = {
251
+ route: "/api/files/:root",
252
+ method: "GET",
253
+ operation_id: "filesystem.read",
254
+ classification: "read",
255
+ source_surface: "filesystem",
256
+ adapter: "fileAdapter.readFileExplorer",
257
+ command_pattern: ["node:fs", "read-only", "root-allowlist"],
258
+ redactions: ["secret_values", "credential_paths", "private_key_material"],
259
+ };
260
+
261
+ export const GUI_FILE_ROOTS: readonly GuiFileRootDefinition[] = [
262
+ {
263
+ id: "agents",
264
+ label: "Agents",
265
+ path_ref: "~/.aidevops/agents",
266
+ description: "Agent, workflow, tool, service, and reference files deployed by aidevops.",
267
+ preview_policy: "agents_markdown_and_code",
268
+ },
269
+ {
270
+ id: "config",
271
+ label: "Config",
272
+ path_ref: "~/.config/aidevops",
273
+ description: "Local aidevops configuration files. Contents stay hidden until a redaction policy lands.",
274
+ preview_policy: "metadata_only",
275
+ },
276
+ {
277
+ id: "localSetup",
278
+ label: "Local Setup",
279
+ path_ref: "~/.aidevops",
280
+ description: "Local aidevops runtime folders, cache, memory, logs, and deployed assets.",
281
+ preview_policy: "metadata_only",
282
+ },
283
+ {
284
+ id: "git",
285
+ label: "Git",
286
+ path_ref: "~/Git",
287
+ description: "Local git workspace roots and worktrees.",
288
+ preview_policy: "metadata_only",
289
+ },
290
+ ] as const;
291
+
292
+ export const BANNED_ROUTE_PATTERNS = [
293
+ "/shell",
294
+ "/exec",
295
+ "/terminal",
296
+ "/run",
297
+ "/run-command",
298
+ ] as const;
299
+
300
+ export const SECRET_SENTINELS = [
301
+ "SECRET_SENTINEL_DO_NOT_RENDER",
302
+ "-----BEGIN PRIVATE KEY-----",
303
+ "Bearer fake-token-value",
304
+ "sessionid=fake-cookie-value",
305
+ "/tmp/fake/credentials.json",
306
+ ] as const;
307
+
308
+ export function createEnvelope<TData>(input: {
309
+ operation_id: GuiOperationId;
310
+ source: GuiSourceRef;
311
+ data: TData;
312
+ warnings?: string[];
313
+ errors?: string[];
314
+ observed_at?: string;
315
+ }): GuiResponseEnvelope<TData> {
316
+ return {
317
+ ok: input.errors === undefined || input.errors.length === 0,
318
+ operation_id: input.operation_id,
319
+ source: input.source,
320
+ data: input.data,
321
+ warnings: input.warnings ?? [],
322
+ errors: input.errors ?? [],
323
+ redactions: ["secret_values", "credential_paths", "private_key_material"],
324
+ observed_at: input.observed_at ?? new Date().toISOString(),
325
+ };
326
+ }
327
+
328
+ export function containsSecretSentinel(value: unknown): boolean {
329
+ const serialized = JSON.stringify(value);
330
+ return SECRET_SENTINELS.some((sentinel) => serialized.includes(sentinel));
331
+ }
332
+
333
+ export function assertNoSecretSentinels(value: unknown): void {
334
+ if (containsSecretSentinel(value)) {
335
+ throw new Error("Secret sentinel leaked into GUI payload");
336
+ }
337
+ }
338
+
339
+ export function isReadOnlyManifest(manifest: GuiRouteManifest): boolean {
340
+ return manifest.method === "GET" && manifest.classification === "read";
341
+ }
@@ -0,0 +1,250 @@
1
+ import { GUI_FILE_ROOTS, type GuiFileExplorerData, type GuiStatusData } from "./contracts";
2
+
3
+ export const statusFixture: GuiStatusData = {
4
+ aidevops_version: "unknown",
5
+ update: {
6
+ running_version: "unknown",
7
+ installed_version: "unknown",
8
+ restart_required: false,
9
+ message: "The GUI app is using the latest installed aidevops version.",
10
+ },
11
+ runtime: {
12
+ host: "local",
13
+ api: "hono",
14
+ read_only: true,
15
+ },
16
+ machine: {
17
+ id: "local",
18
+ label: "Local machine",
19
+ initials: "LM",
20
+ username: "local",
21
+ hostname: "localhost",
22
+ local_ips: ["127.0.0.1"],
23
+ public_ip: null,
24
+ },
25
+ paths: [
26
+ {
27
+ label: "deployed agents",
28
+ path_ref: "~/.aidevops/agents",
29
+ health: "unchecked",
30
+ },
31
+ {
32
+ label: "settings",
33
+ path_ref: "~/.config/aidevops/settings.json",
34
+ health: "unchecked",
35
+ },
36
+ ],
37
+ helper_availability: [
38
+ {
39
+ name: "aidevops status",
40
+ status: "unchecked",
41
+ },
42
+ ],
43
+ navigation: [
44
+ {
45
+ id: "overview",
46
+ label: "Overview",
47
+ description: "Local setup, update, and API health.",
48
+ },
49
+ {
50
+ id: "agents",
51
+ label: "Agents",
52
+ description: "Read-only file explorer for deployed agent files.",
53
+ },
54
+ {
55
+ id: "config",
56
+ label: "Config",
57
+ description: "Read-only file explorer for aidevops config.",
58
+ },
59
+ {
60
+ id: "git",
61
+ label: "Local Repos",
62
+ description: "Read-only local git workspace browser.",
63
+ },
64
+ {
65
+ id: "security",
66
+ label: "Secrets",
67
+ description: "Secret-reference-only trust boundary.",
68
+ },
69
+ ],
70
+ settings: {
71
+ path_ref: "~/.config/aidevops/settings.json",
72
+ health: "unchecked",
73
+ key_count: 0,
74
+ keys: [],
75
+ value_policy: "keys_only_no_values",
76
+ },
77
+ repos: {
78
+ path_ref: "~/.config/aidevops/repos.json",
79
+ health: "unchecked",
80
+ total: 0,
81
+ repos: [],
82
+ },
83
+ local_repos: {
84
+ path_ref: "~/Git",
85
+ health: "unchecked",
86
+ total: 0,
87
+ excluded_worktrees: 0,
88
+ repos: [],
89
+ },
90
+ oauth_pool: {
91
+ path_ref: "~/.aidevops/oauth-pool.json",
92
+ health: "unchecked",
93
+ value_policy: "metadata_only_no_tokens",
94
+ providers: [
95
+ {
96
+ provider: "anthropic",
97
+ configured: false,
98
+ total: 0,
99
+ available: 0,
100
+ active_or_idle: 0,
101
+ rate_limited: 0,
102
+ auth_errors: 0,
103
+ pending_token: false,
104
+ accounts: [],
105
+ },
106
+ {
107
+ provider: "openai",
108
+ configured: false,
109
+ total: 0,
110
+ available: 0,
111
+ active_or_idle: 0,
112
+ rate_limited: 0,
113
+ auth_errors: 0,
114
+ pending_token: false,
115
+ accounts: [],
116
+ },
117
+ {
118
+ provider: "cursor",
119
+ configured: false,
120
+ total: 0,
121
+ available: 0,
122
+ active_or_idle: 0,
123
+ rate_limited: 0,
124
+ auth_errors: 0,
125
+ pending_token: false,
126
+ accounts: [],
127
+ },
128
+ {
129
+ provider: "google",
130
+ configured: false,
131
+ total: 0,
132
+ available: 0,
133
+ active_or_idle: 0,
134
+ rate_limited: 0,
135
+ auth_errors: 0,
136
+ pending_token: false,
137
+ accounts: [],
138
+ },
139
+ ],
140
+ },
141
+ setup_targets: [
142
+ {
143
+ label: "Deployed agents",
144
+ path_ref: "~/.aidevops/agents/VERSION",
145
+ health: "unchecked",
146
+ purpose: "Canonical aidevops agent, workflow, script, and reference bundle.",
147
+ installed_version: "unknown",
148
+ latest_version: "unknown",
149
+ needs_update: false,
150
+ },
151
+ ],
152
+ ai_apps: [
153
+ {
154
+ name: "OpenCode",
155
+ status: "unchecked",
156
+ app_path_ref: "~/Applications/OpenCode AIDevOps.app",
157
+ binary_path_ref: "not found",
158
+ config_path_ref: "~/.config/opencode/opencode.json",
159
+ aidevops_target_path_ref: "~/.config/opencode/AGENTS.md",
160
+ app_version: "unknown",
161
+ aidevops_version: "unknown",
162
+ latest_version: "unknown",
163
+ needs_update: false,
164
+ },
165
+ {
166
+ name: "Claude Code",
167
+ status: "unchecked",
168
+ app_path_ref: "/Applications/Claude.app",
169
+ binary_path_ref: "not found",
170
+ config_path_ref: "~/.config/Claude/claude_desktop_config.json",
171
+ aidevops_target_path_ref: "~/.config/Claude/AGENTS.md",
172
+ app_version: "unknown",
173
+ aidevops_version: "unknown",
174
+ latest_version: "unknown",
175
+ needs_update: false,
176
+ },
177
+ ],
178
+ capabilities: [
179
+ {
180
+ id: "setup-status",
181
+ label: "Setup/status",
182
+ status: "available",
183
+ doc_ref: "docs/gui/helper-api-contract.md:98",
184
+ },
185
+ {
186
+ id: "repos",
187
+ label: "Repository registry",
188
+ status: "placeholder",
189
+ doc_ref: "docs/gui/helper-api-contract.md:103",
190
+ },
191
+ {
192
+ id: "settings",
193
+ label: "Effective settings",
194
+ status: "placeholder",
195
+ doc_ref: "docs/gui/helper-api-contract.md:101",
196
+ },
197
+ ],
198
+ secrets: [
199
+ {
200
+ name: "GITHUB_TOKEN",
201
+ status: "unchecked",
202
+ },
203
+ ],
204
+ placeholders: [
205
+ "Settings, repos, routines, OpenCode sessions, and capabilities will be added as read-only adapters.",
206
+ ],
207
+ };
208
+
209
+ export const fileExplorerFixture: GuiFileExplorerData = {
210
+ root: GUI_FILE_ROOTS[0],
211
+ current_path_ref: "~/.aidevops/agents",
212
+ current_relative_path: "",
213
+ entry_limit: 80,
214
+ entries: [
215
+ {
216
+ name: "workflows",
217
+ kind: "directory",
218
+ path_ref: "~/.aidevops/agents/workflows",
219
+ relative_path: "workflows",
220
+ extension: "",
221
+ preview_allowed: false,
222
+ },
223
+ {
224
+ name: "AGENTS.md",
225
+ kind: "file",
226
+ path_ref: "~/.aidevops/agents/AGENTS.md",
227
+ relative_path: "AGENTS.md",
228
+ extension: ".md",
229
+ preview_allowed: true,
230
+ },
231
+ ],
232
+ selected_preview: {
233
+ path_ref: "~/.aidevops/agents/AGENTS.md",
234
+ relative_path: "AGENTS.md",
235
+ mode: "markdown",
236
+ language: "md",
237
+ content: "# AI DevOps Framework\n\nRead-only local preview.",
238
+ truncated: false,
239
+ reason: "",
240
+ },
241
+ };
242
+
243
+ export const unsafeSecretFixture = {
244
+ secret_name: "GUI_TEST_SECRET",
245
+ secret_value: "SECRET_SENTINEL_DO_NOT_RENDER",
246
+ private_key: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
247
+ bearer: "Bearer fake-token-value",
248
+ cookie: "sessionid=fake-cookie-value",
249
+ credential_path: "/tmp/fake/credentials.json",
250
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./contracts";
2
+ export * from "./fixtures";
@@ -0,0 +1,24 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>aidevops GUI</title>
7
+ </head>
8
+ <body>
9
+ <div id="root">
10
+ <main class="app-shell">
11
+ <aside class="sidebar" aria-label="Dashboard navigation">
12
+ <div class="brand-mark">aidevops</div>
13
+ <button class="nav-item active" type="button"><span>Overview</span><small>Loading local dashboard.</small></button>
14
+ </aside>
15
+ <section class="content" aria-label="aidevops status">
16
+ <p class="eyebrow">Read-only local dashboard scaffold</p>
17
+ <h1>aidevops control plane</h1>
18
+ <p class="lede">Loading the local GUI. If this message stays visible, refresh the page or check ~/Library/Logs/aidevops-gui/web.log.</p>
19
+ </section>
20
+ </main>
21
+ </div>
22
+ <script type="module" src="/src/main.tsx"></script>
23
+ </body>
24
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@aidevops/gui-web",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@aidevops/gui-shared": "workspace:*",
8
+ "@fontsource/ibm-plex-mono": "^5.2.7",
9
+ "@fontsource/ibm-plex-sans": "^5.2.8",
10
+ "@fontsource/ibm-plex-serif": "^5.2.7",
11
+ "@fontsource/inter": "^5.2.8",
12
+ "@fontsource/playpen-sans": "^5.2.8",
13
+ "@fontsource/poppins": "^5.2.7",
14
+ "@fontsource/source-sans-3": "^5.2.9",
15
+ "@fontsource/source-serif-4": "^5.2.9",
16
+ "@fontsource/tilt-neon": "^5.2.10",
17
+ "@fontsource/ubuntu-mono": "^5.2.8",
18
+ "react": "^19.0.0",
19
+ "react-dom": "^19.0.0",
20
+ "react-icons": "^5.5.0"
21
+ },
22
+ "scripts": {
23
+ "dev": "vite --host 127.0.0.1"
24
+ }
25
+ }