codepiper 0.1.4 → 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/.env.example CHANGED
@@ -7,8 +7,11 @@
7
7
  # CODEPIPER_WS_PORT=9999 # WebSocket port (standalone)
8
8
 
9
9
  # ── Remote Access ───────────────────────────────────────────────────
10
- # Comma-separated hostnames or origins allowed for WebSocket connections.
11
- # Required when accessing from a non-localhost domain (reverse proxy, tunnel, etc.).
10
+ # Comma-separated list of allowed origin hostnames for WebSocket and CSRF checks.
11
+ # Required when accessing the web dashboard from a non-localhost domain
12
+ # (e.g., via reverse proxy, Cloudflare Tunnel, etc.).
13
+ # Accepts bare hostnames or full origins (scheme is stripped).
14
+ # Localhost (127.0.0.1, ::1, localhost) is always allowed implicitly.
12
15
  # CODEPIPER_ALLOWED_ORIGINS=myapp.example.com,other.dev
13
16
 
14
17
  # ── Database ────────────────────────────────────────────────────────
@@ -16,6 +19,9 @@
16
19
 
17
20
  # ── Security ───────────────────────────────────────────────────────
18
21
  # CODEPIPER_SECRET= # Hook authentication secret (auto-generated if unset)
22
+ # CODEPIPER_FORCE_SECURE_COOKIES=0 # Set to 1 to force Secure auth cookies behind TLS-terminating proxies
23
+ # CODEPIPER_TRUST_PROXY_HEADERS=0 # Set to 1 to trust X-Forwarded-For/X-Real-IP/X-Forwarded-Proto headers
24
+ # CODEPIPER_MFA_QR_TIMEOUT_MS=8000 # Optional QR generation timeout before fallback to manual MFA key
19
25
 
20
26
  # ── Push Delivery (Optional) ───────────────────────────────────────
21
27
  # CODEPIPER_PUSH_ENABLED=0 # Set to 1 to enable daemon web-push delivery
package/CHANGELOG.md CHANGED
@@ -6,6 +6,32 @@ This project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.1] - 2026-02-21
10
+
11
+ ### Added
12
+ - Added `test:onboarding-smoke` scenario test to validate first-run auth onboarding end-to-end (bootstrap password, MFA setup, and final authenticated state).
13
+ - Added onboarding smoke coverage to release smoke gate to catch onboarding regressions before publishing.
14
+
15
+ ### Fixed
16
+ - Fixed MFA setup screen retry/race behavior that could leave onboarding stuck on “Generating authenticator QR code...”.
17
+ - Improved setup page MFA retry behavior so failed attempts can be retried cleanly without refresh.
18
+
19
+ ## [0.2.0] - 2026-02-21
20
+
21
+ ### Added
22
+ - First-run bootstrap password flow for web onboarding when no password exists.
23
+ - CLI password rotation generator (`codepiper auth reset-password --generate`).
24
+ - Onboarding-aware auth status signals (`onboardingPending`) across daemon and web auth state.
25
+
26
+ ### Changed
27
+ - Enforced MFA-first onboarding flow for initial web sign-in.
28
+ - Improved onboarding and production docs for bootstrap password, remote origin allowlisting, and CSRF/WS origin controls.
29
+ - Updated setup/login UI copy and recovery flow for clearer first-run guidance.
30
+
31
+ ### Fixed
32
+ - Added QR generation timeout with manual-key fallback to prevent MFA setup hangs on constrained VPS environments.
33
+ - Hardened MFA setup handling in API/UI paths with explicit failure and retry behavior.
34
+
9
35
  ## [0.1.4] - 2026-02-20
10
36
 
11
37
  ### Fixed
package/README.md CHANGED
@@ -224,7 +224,7 @@ codepiper daemon --web --port 3456
224
224
  Then:
225
225
  1. Open `http://127.0.0.1:3000` (or your custom port).
226
226
  2. Complete onboarding:
227
- 1. Set admin password.
227
+ 1. Sign in with the bootstrap password printed by the daemon on first web start.
228
228
  2. Set up and verify MFA (required before first sign-in).
229
229
  3. Create your first session in **Sessions**.
230
230
  4. Send a prompt from terminal/conversation view.
@@ -232,8 +232,10 @@ Then:
232
232
  ### First-Run Onboarding (Mandatory MFA)
233
233
 
234
234
  Web onboarding is a strict two-step flow:
235
- 1. Set your admin password.
236
- 2. Complete TOTP MFA setup and verification.
235
+ 1. Daemon generates a secure bootstrap password once (printed on first `codepiper daemon --web` run).
236
+ - Password is printed only on TTY startup (not in non-interactive service logs).
237
+ - For headless/service deployments, rotate/generate from host CLI: `codepiper auth reset-password --generate`
238
+ 2. After signing in with that password, complete TOTP MFA setup and verification.
237
239
 
238
240
  Until MFA verification succeeds, the dashboard remains in onboarding mode and does not issue a normal authenticated session.
239
241
 
@@ -242,6 +244,12 @@ MFA disable/reset is CLI-only:
242
244
  codepiper auth reset-mfa
243
245
  ```
244
246
 
247
+ Rotate password with a generated secure value:
248
+ ```bash
249
+ codepiper auth reset-password --generate
250
+ ```
251
+ If MFA is not enabled, this command forces MFA onboarding on the next sign-in.
252
+
245
253
  ### Direct Tmux Attach
246
254
 
247
255
  Use this when you want to attach from your local terminal without going through CLI streaming:
@@ -375,8 +383,13 @@ Environment Variables:
375
383
  CODEPIPER_DB_PATH SQLite database path (default: ~/.codepiper/codepiper.db)
376
384
  CODEPIPER_WS_PORT WebSocket port (default: 9999)
377
385
  CODEPIPER_HTTP_PORT HTTP port (default: 3000, overridden by --port)
386
+ CODEPIPER_ALLOWED_ORIGINS Comma-separated allowed origin hostnames for WebSocket and CSRF checks.
387
+ Required when accessing the dashboard from a non-localhost domain
388
+ (e.g., via reverse proxy, Cloudflare Tunnel, etc.).
389
+ Localhost is always allowed implicitly
378
390
  CODEPIPER_FORCE_SECURE_COOKIES Optional (`1`) to force `Secure` auth cookies behind TLS-terminating proxies
379
391
  CODEPIPER_TRUST_PROXY_HEADERS Optional (`1`) to trust proxy headers (`X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto`) for client IP and secure-cookie inference
392
+ CODEPIPER_MFA_QR_TIMEOUT_MS Optional QR generation timeout in ms before manual-key fallback (default: 8000)
380
393
  CODEPIPER_PUSH_ENABLED Optional daemon push delivery toggle (`1` to enable)
381
394
  CODEPIPER_PUSH_PUBLIC_KEY Optional Base64URL VAPID public key for daemon push delivery
382
395
  CODEPIPER_PUSH_PRIVATE_KEY Optional Base64URL VAPID private key for daemon push delivery
@@ -476,7 +489,8 @@ Sessions support two billing modes via `--billing` flag or `billingMode` API par
476
489
  - Unix socket API is trusted local access (no session token required)
477
490
  - HTTP/WebSocket dashboard routes enforce session auth when auth is configured
478
491
  - MFA disable/reset is CLI-only (`codepiper auth reset-mfa`) to reduce browser downgrade risk
479
- - Browser-originated mutating HTTP API requests require same-origin (CSRF mitigation)
492
+ - Browser-originated mutating HTTP API requests require same-origin or `CODEPIPER_ALLOWED_ORIGINS` match (CSRF mitigation)
493
+ - WebSocket upgrades gated by `Origin` header (CSWSH prevention) — only localhost and `CODEPIPER_ALLOWED_ORIGINS` accepted
480
494
  - Input validation prevents injection attacks
481
495
  - Audit logs for all permission decisions
482
496
  - Remote access recommended via SSH port forwarding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codepiper",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Orchestrate Claude Code and Codex sessions from one control plane: policy-guarded, real-time, and recoverable across devices.",
5
5
  "license": "MIT",
6
6
  "author": "g3ortega",
@@ -74,6 +74,7 @@
74
74
  "dev:web": "bun run --cwd packages/web dev",
75
75
  "cli": "bun run packages/cli/src/main.ts",
76
76
  "test": "bun test",
77
+ "test:onboarding-smoke": "bun run scripts/tests/onboarding-smoke.ts",
77
78
  "test:integration": "bun test --integration",
78
79
  "typecheck": "bunx tsc -b tsconfig.json",
79
80
  "typecheck:strict:daemon": "bunx tsc -p packages/daemon/tsconfig.strict.json --pretty false",
@@ -7,7 +7,7 @@ interface AuthOptions {
7
7
  args: string[];
8
8
  }
9
9
 
10
- function parseAuthOptions(args: string[]): AuthOptions {
10
+ export function parseAuthOptions(args: string[]): AuthOptions {
11
11
  let socket = "/tmp/codepiper.sock";
12
12
  const positional: string[] = [];
13
13
 
@@ -69,7 +69,41 @@ async function handleStatus(socket: string): Promise<void> {
69
69
  }
70
70
  }
71
71
 
72
- async function handleResetPassword(socket: string): Promise<void> {
72
+ interface ResetPasswordFlags {
73
+ generate: boolean;
74
+ }
75
+
76
+ export function parseResetPasswordFlags(args: string[]): ResetPasswordFlags {
77
+ const flags: ResetPasswordFlags = { generate: false };
78
+ for (const arg of args) {
79
+ if (arg === "--generate" || arg === "-g") {
80
+ flags.generate = true;
81
+ continue;
82
+ }
83
+ throw new Error(`Unknown reset-password option: ${arg}`);
84
+ }
85
+ return flags;
86
+ }
87
+
88
+ async function handleResetPassword(socket: string, args: string[]): Promise<void> {
89
+ const flags = parseResetPasswordFlags(args);
90
+ if (flags.generate) {
91
+ const result = await daemonRequest<{ ok: boolean; generated: boolean; password: string }>(
92
+ socket,
93
+ "/auth/cli/reset-password",
94
+ {
95
+ method: "POST",
96
+ body: JSON.stringify({ generate: true }),
97
+ }
98
+ );
99
+
100
+ console.log("\x1b[32m✓\x1b[0m Password rotated with a generated secure value");
101
+ console.log(` New password: ${result.password}`);
102
+ console.log(" All existing sessions have been invalidated.");
103
+ console.log(" Keep this password secure and complete MFA onboarding if prompted.");
104
+ return;
105
+ }
106
+
73
107
  process.stdout.write("New password: ");
74
108
  const password = await readPasswordFromStdin();
75
109
 
@@ -240,7 +274,7 @@ export async function runAuthCommand(args: string[]): Promise<void> {
240
274
  await handleStatus(options.socket);
241
275
  break;
242
276
  case "reset-password":
243
- await handleResetPassword(options.socket);
277
+ await handleResetPassword(options.socket, options.args);
244
278
  break;
245
279
  case "reset-mfa":
246
280
  await handleResetMfa(options.socket);
@@ -254,7 +288,7 @@ export async function runAuthCommand(args: string[]): Promise<void> {
254
288
  default:
255
289
  console.error(`Unknown auth subcommand: ${options.subcommand}`);
256
290
  console.error(
257
- "Available subcommands: status, reset-password, reset-mfa, sessions, revoke-all"
291
+ "Available subcommands: status, reset-password [--generate], reset-mfa, sessions, revoke-all"
258
292
  );
259
293
  process.exit(1);
260
294
  }
@@ -114,7 +114,7 @@ Manage authentication and security settings.
114
114
 
115
115
  Subcommands:
116
116
  status Show auth configuration status
117
- reset-password Reset the dashboard password
117
+ reset-password Reset the dashboard password (or generate one)
118
118
  reset-mfa Disable two-factor authentication
119
119
  sessions List active login sessions
120
120
  revoke-all Revoke all active sessions
@@ -125,6 +125,7 @@ Options:
125
125
  Examples:
126
126
  codepiper auth status
127
127
  codepiper auth reset-password
128
+ codepiper auth reset-password --generate
128
129
  codepiper auth reset-mfa
129
130
  codepiper auth sessions
130
131
  codepiper auth revoke-all`,
@@ -46,13 +46,20 @@ export async function handleAuthStatus(req: Request, ctx: RouteContext): Promise
46
46
  return Response.json({
47
47
  setupRequired: false,
48
48
  mfaEnabled: false,
49
+ onboardingPending: false,
49
50
  authenticated: false,
50
51
  authEnabled: false,
51
52
  });
52
53
  }
53
54
 
54
55
  const setupRequired = authService.isSetupRequired();
55
- const mfaSetupRequired = !setupRequired && authService.isMfaSetupPending();
56
+ const onboardingPending = !setupRequired && authService.isMfaSetupPending();
57
+ let mfaSetupRequired = false;
58
+ if (onboardingPending) {
59
+ const onboardingTokenHash = extractAndHashOnboardingToken(req);
60
+ mfaSetupRequired =
61
+ onboardingTokenHash !== null && authService.validateOnboardingToken(onboardingTokenHash);
62
+ }
56
63
  const tokenHash = extractAndHashToken(req);
57
64
  const authenticated = tokenHash ? authService.validateSession(tokenHash) : false;
58
65
 
@@ -62,7 +69,13 @@ export async function handleAuthStatus(req: Request, ctx: RouteContext): Promise
62
69
  mfaEnabled = config?.totpEnabled ?? false;
63
70
  }
64
71
 
65
- return Response.json({ setupRequired, mfaEnabled, mfaSetupRequired, authenticated });
72
+ return Response.json({
73
+ setupRequired,
74
+ mfaEnabled,
75
+ mfaSetupRequired,
76
+ onboardingPending,
77
+ authenticated,
78
+ });
66
79
  }
67
80
 
68
81
  export async function handleAuthSetup(req: Request, ctx: RouteContext): Promise<Response> {
@@ -233,8 +246,15 @@ export async function handleMfaSetup(_req: Request, ctx: RouteContext): Promise<
233
246
  return Response.json({ error: "Auth not configured" }, { status: 500 });
234
247
  }
235
248
 
236
- const result = await authService.generateTotpSetup();
237
- return Response.json(result);
249
+ try {
250
+ const result = await authService.generateTotpSetup();
251
+ return Response.json(result);
252
+ } catch (error) {
253
+ if (error instanceof AuthError) {
254
+ return Response.json({ error: error.message, code: error.code }, { status: 400 });
255
+ }
256
+ return Response.json({ error: "Failed to generate MFA setup data" }, { status: 500 });
257
+ }
238
258
  }
239
259
 
240
260
  export async function handleMfaVerify(req: Request, ctx: RouteContext): Promise<Response> {
@@ -318,12 +338,30 @@ export async function handleCliResetPassword(req: Request, ctx: RouteContext): P
318
338
 
319
339
  try {
320
340
  const body = (await req.json()) as Record<string, unknown>;
321
- const password = getStringField(body, "password");
322
- if (!password) {
323
- return Response.json({ error: "Password is required" }, { status: 400 });
341
+ const shouldGenerate = body.generate === true;
342
+
343
+ let password: string;
344
+ if (shouldGenerate) {
345
+ password = authService.generateSecurePassword();
346
+ } else {
347
+ const provided = getStringField(body, "password");
348
+ if (!provided) {
349
+ return Response.json({ error: "Password is required" }, { status: 400 });
350
+ }
351
+ password = provided;
324
352
  }
325
353
 
326
354
  await authService.resetPassword(password);
355
+
356
+ // Re-enter MFA onboarding when MFA is not currently enabled
357
+ const config = ctx.db.getAuthConfig();
358
+ if (config && !config.totpEnabled) {
359
+ ctx.db.updateAuthOnboardingState(true, null, null);
360
+ }
361
+
362
+ if (shouldGenerate) {
363
+ return Response.json({ ok: true, generated: true, password });
364
+ }
327
365
  return Response.json({ ok: true });
328
366
  } catch (error) {
329
367
  if (error instanceof AuthError) {
@@ -18,6 +18,8 @@ const ONBOARDING_TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
18
18
  const TOTP_WINDOW = 1; // ±1 step (90 seconds total)
19
19
  const RECOVERY_CODE_COUNT = 8;
20
20
  const MIN_PASSWORD_LENGTH = 8;
21
+ const GENERATED_PASSWORD_LENGTH = 24;
22
+ const DEFAULT_MFA_QR_TIMEOUT_MS = 8000;
21
23
 
22
24
  export interface LoginResult {
23
25
  token: string;
@@ -35,7 +37,7 @@ export interface MfaSetupRequiredResult {
35
37
  export interface TotpSetupResult {
36
38
  secret: string;
37
39
  otpauthUri: string;
38
- qrDataUrl: string;
40
+ qrDataUrl: string | null;
39
41
  }
40
42
 
41
43
  export interface TotpVerifyResult {
@@ -46,6 +48,10 @@ export interface OnboardingTotpVerifyResult extends TotpVerifyResult {
46
48
  token: string;
47
49
  }
48
50
 
51
+ export interface BootstrapPasswordResult {
52
+ password: string;
53
+ }
54
+
49
55
  export class AuthService {
50
56
  private pendingTotpSecret: string | null = null;
51
57
 
@@ -97,6 +103,27 @@ export class AuthService {
97
103
  };
98
104
  }
99
105
 
106
+ async initializeBootstrapPassword(): Promise<BootstrapPasswordResult> {
107
+ if (!this.isSetupRequired()) {
108
+ throw new AuthError("Password is already configured", "PASSWORD_ALREADY_CONFIGURED");
109
+ }
110
+
111
+ const password = this.generateSecurePassword();
112
+ const hash = await Bun.password.hash(password, {
113
+ algorithm: "argon2id",
114
+ memoryCost: 65536,
115
+ timeCost: 3,
116
+ });
117
+
118
+ this.db.createAuthConfig(hash, {
119
+ mfaSetupPending: true,
120
+ onboardingTokenHash: null,
121
+ onboardingTokenExpiresAt: null,
122
+ });
123
+
124
+ return { password };
125
+ }
126
+
100
127
  // ─── Login ────────────────────────────────────────────────────────
101
128
 
102
129
  async login(
@@ -269,7 +296,15 @@ export class AuthService {
269
296
  });
270
297
 
271
298
  const otpauthUri = totp.toString();
272
- const qrDataUrl = await QRCode.toDataURL(otpauthUri);
299
+ let qrDataUrl: string | null = null;
300
+ try {
301
+ qrDataUrl = await this.withTimeout(QRCode.toDataURL(otpauthUri), this.getQrTimeoutMs());
302
+ } catch (error) {
303
+ console.warn(
304
+ "[auth] QR generation failed or timed out; falling back to manual setup key only:",
305
+ error
306
+ );
307
+ }
273
308
 
274
309
  // Store pending secret (not persisted until verified)
275
310
  this.pendingTotpSecret = secret.base32;
@@ -457,6 +492,70 @@ export class AuthService {
457
492
  return codes;
458
493
  }
459
494
 
495
+ generateSecurePassword(length = GENERATED_PASSWORD_LENGTH): string {
496
+ const safeLength = Math.max(length, MIN_PASSWORD_LENGTH);
497
+ const charSets = {
498
+ upper: "ABCDEFGHJKLMNPQRSTUVWXYZ",
499
+ lower: "abcdefghijkmnopqrstuvwxyz",
500
+ digits: "23456789",
501
+ symbols: "!@#$%^&*_-+=",
502
+ };
503
+ const allChars = Object.values(charSets).join("");
504
+
505
+ const pickFrom = (set: string): string => set.charAt(crypto.randomInt(0, set.length));
506
+
507
+ // Guarantee at least one character from each class
508
+ const chars: string[] = [
509
+ pickFrom(charSets.upper),
510
+ pickFrom(charSets.lower),
511
+ pickFrom(charSets.digits),
512
+ pickFrom(charSets.symbols),
513
+ ];
514
+
515
+ for (let i = chars.length; i < safeLength; i++) {
516
+ chars.push(pickFrom(allChars));
517
+ }
518
+
519
+ // Fisher-Yates shuffle
520
+ for (let i = chars.length - 1; i > 0; i--) {
521
+ const j = crypto.randomInt(0, i + 1);
522
+ const tmp = chars[i] ?? "";
523
+ chars[i] = chars[j] ?? "";
524
+ chars[j] = tmp;
525
+ }
526
+
527
+ return chars.join("");
528
+ }
529
+
530
+ private getQrTimeoutMs(): number {
531
+ const raw = process.env.CODEPIPER_MFA_QR_TIMEOUT_MS;
532
+ if (!raw) {
533
+ return DEFAULT_MFA_QR_TIMEOUT_MS;
534
+ }
535
+ const parsed = Number.parseInt(raw, 10);
536
+ if (!Number.isFinite(parsed) || parsed <= 0) {
537
+ return DEFAULT_MFA_QR_TIMEOUT_MS;
538
+ }
539
+ return parsed;
540
+ }
541
+
542
+ private async withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
543
+ let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
544
+ try {
545
+ return await Promise.race([
546
+ promise,
547
+ new Promise<never>((_resolve, reject) => {
548
+ timeoutHandle = setTimeout(
549
+ () => reject(new Error("QR code generation timed out")),
550
+ timeoutMs
551
+ );
552
+ }),
553
+ ]);
554
+ } finally {
555
+ clearTimeout(timeoutHandle);
556
+ }
557
+ }
558
+
460
559
  private hashRecoveryCode(code: string): string {
461
560
  return crypto.createHash("sha256").update(code).digest("hex");
462
561
  }
@@ -372,7 +372,7 @@ async function main() {
372
372
  }
373
373
 
374
374
  // Initialize authentication
375
- const { AuthService } = await import("./auth/authService");
375
+ const { AuthError, AuthService } = await import("./auth/authService");
376
376
  const { RateLimiter } = await import("./auth/rateLimiter");
377
377
  const { getOrCreateEncryptionKey, getOrCreateHookSecret } = await import("./crypto/encryption");
378
378
 
@@ -388,8 +388,30 @@ async function main() {
388
388
  console.log(`Cleaned up ${expiredSessions} expired auth session(s)`);
389
389
  }
390
390
 
391
- if (authService.isSetupRequired()) {
392
- console.log("Auth: No password configured. Web dashboard will show setup page.");
391
+ if (authService.isSetupRequired() && cliArgs.web) {
392
+ try {
393
+ const bootstrap = await authService.initializeBootstrapPassword();
394
+ console.log("Auth: Generated first-run bootstrap password for web onboarding.");
395
+ if (process.stdout.isTTY) {
396
+ console.log(`Auth: Bootstrap password: ${bootstrap.password}`);
397
+ } else {
398
+ console.log(
399
+ "Auth: Bootstrap password was generated but is not printed because stdout is not a TTY."
400
+ );
401
+ }
402
+ console.log("Auth: Sign in once with this password, then complete mandatory MFA setup.");
403
+ console.log("Auth: Rotate anytime with `codepiper auth reset-password --generate`.");
404
+ } catch (error) {
405
+ // Race guard: another process may have configured the password between the check and now
406
+ if (!(error instanceof AuthError && error.code === "PASSWORD_ALREADY_CONFIGURED")) {
407
+ throw error;
408
+ }
409
+ console.log("Auth: Password configured. Web dashboard requires login.");
410
+ }
411
+ } else if (authService.isSetupRequired()) {
412
+ console.log(
413
+ "Auth: No password configured yet. Start with `codepiper daemon --web` to auto-generate a bootstrap password."
414
+ );
393
415
  } else {
394
416
  console.log("Auth: Password configured. Web dashboard requires login.");
395
417
  }
@@ -1,4 +1,4 @@
1
- import{r as i,j as e}from"./react-vendor-B5MgMUHH.js";import{c as A,a as f,f as h,u as U,S as ne,b as le,d as ce,e as de,g as N,B as xe,M as me,A as he,Z as pe}from"./index-DcbqZfqt.js";import{R as g,A as B,C as E,X as w,Y as S,T as v,a as u,B as K,b as F,P as ue,d as fe,e as je}from"./chart-vendor-DlOHLaCG.js";import"./monaco-core-DQ5Mk8AK.js";/**
1
+ import{r as i,j as e}from"./react-vendor-B5MgMUHH.js";import{c as A,a as f,f as h,u as U,S as ne,b as le,d as ce,e as de,g as N,B as xe,M as me,A as he,Z as pe}from"./index-DBlyZnLx.js";import{R as g,A as B,C as E,X as w,Y as S,T as v,a as u,B as K,b as F,P as ue,d as fe,e as je}from"./chart-vendor-DlOHLaCG.js";import"./monaco-core-DQ5Mk8AK.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{r as c,j as e}from"./react-vendor-B5MgMUHH.js";import{c as z,S as V,b as O,d as U,e as q,g as y,h as v,i as B,j as f,u,B as p,P as D,I as j,D as S,k as $,l as F,m as I,n as E,o as R,p as T,C as X,q as J,X as K,r as L}from"./index-DcbqZfqt.js";import{T as _}from"./trash-2-DCNTb20S.js";import{P as Q,L as k}from"./pencil-CSMVe0ds.js";import{T as Y,a as Z,b as C,c as P}from"./tabs-Ckbv8J0d.js";import"./monaco-core-DQ5Mk8AK.js";import"./chart-vendor-DlOHLaCG.js";/**
1
+ import{r as c,j as e}from"./react-vendor-B5MgMUHH.js";import{c as z,S as V,b as O,d as U,e as q,g as y,h as v,i as B,j as f,u,B as p,P as D,I as j,D as S,k as $,l as F,m as I,n as E,o as R,p as T,C as X,q as J,X as K,r as L}from"./index-DBlyZnLx.js";import{T as _}from"./trash-2-CP0E4E_1.js";import{P as Q,L as k}from"./pencil-Da7xVL3F.js";import{T as Y,a as Z,b as C,c as P}from"./tabs-B571nOS8.js";import"./monaco-core-DQ5Mk8AK.js";import"./chart-vendor-DlOHLaCG.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,5 +1,5 @@
1
1
  const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/monacoSetup-DvBj52bT.js","assets/monaco-react-DfZNWvtW.js","assets/react-vendor-B5MgMUHH.js","assets/monaco-core-DQ5Mk8AK.js","assets/monaco-core-B_19GPAS.css"])))=>i.map(i=>d[i]);
2
- import{r as u,j as t,k as ur,l as hs,o as gt,m as pt,n as wn,p as At,q as Vt,t as ps,w as dr,E as xs,x as kn,y as Rt,z as ke,A as gs,C as bs,D as We,F as Zt,G as ys,H as St,I as ft,J as vs,M as ws,K as ks,u as js}from"./react-vendor-B5MgMUHH.js";import{c as K,j as A,q as xt,i as Gt,S as mr,b as fr,G as jn,d as hr,e as pr,g as pn,s as Ns,a as Qe,t as xr,C as gr,u as q,T as Cs,v as Ss,B as Ke,P as Es,X as et,r as Rs,h as Et,L as br,w as Ae,x as yr,M as vr,y as en,z as wr,E as Ts,F as As,H as Ms,J as Ds,K as Ps,N as Ls,O as _s,Q as Fs,R as zs,U as $s,I as Is,V as Os,W as Bs,Y as Hs,_ as Us,$ as qs}from"./index-DcbqZfqt.js";import{_ as In}from"./monaco-core-DQ5Mk8AK.js";import{R as Vs}from"./refresh-cw-CdfhRXCd.js";import{L as On,P as Ks}from"./pencil-CSMVe0ds.js";import{x as Gs,a as Ws}from"./terminal-vendor-Cs8KPbV3.js";import{T as Qs,a as Ys,b as Xs,c as kt}from"./tabs-Ckbv8J0d.js";import"./chart-vendor-DlOHLaCG.js";/**
2
+ import{r as u,j as t,k as ur,l as hs,o as gt,m as pt,n as wn,p as At,q as Vt,t as ps,w as dr,E as xs,x as kn,y as Rt,z as ke,A as gs,C as bs,D as We,F as Zt,G as ys,H as St,I as ft,J as vs,M as ws,K as ks,u as js}from"./react-vendor-B5MgMUHH.js";import{c as K,j as A,q as xt,i as Gt,S as mr,b as fr,G as jn,d as hr,e as pr,g as pn,s as Ns,a as Qe,t as xr,C as gr,u as q,T as Cs,v as Ss,B as Ke,P as Es,X as et,r as Rs,h as Et,L as br,w as Ae,x as yr,M as vr,y as en,z as wr,E as Ts,F as As,H as Ms,J as Ds,K as Ps,N as Ls,O as _s,Q as Fs,R as zs,U as $s,I as Is,V as Os,W as Bs,Y as Hs,_ as Us,$ as qs}from"./index-DBlyZnLx.js";import{_ as In}from"./monaco-core-DQ5Mk8AK.js";import{R as Vs}from"./refresh-cw-CPonRjbV.js";import{L as On,P as Ks}from"./pencil-Da7xVL3F.js";import{x as Gs,a as Ws}from"./terminal-vendor-Cs8KPbV3.js";import{T as Qs,a as Ys,b as Xs,c as kt}from"./tabs-B571nOS8.js";import"./chart-vendor-DlOHLaCG.js";/**
3
3
  * @license lucide-react v0.564.0 - ISC
4
4
  *
5
5
  * This source code is licensed under the ISC license.