@helloleo/plugins 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12977,7 +12977,7 @@ export async function signOutNetSuite(): Promise<void> {
12977
12977
  `;
12978
12978
  }
12979
12979
  function signInButtonTsx() {
12980
- return `import { signInWithNetSuite } from '../lib/netsuite';
12980
+ return `import { signInWithNetSuite } from '../../lib/netsuite';
12981
12981
 
12982
12982
  export function SignInWithNetSuite() {
12983
12983
  return (
@@ -12991,6 +12991,113 @@ export function SignInWithNetSuite() {
12991
12991
  }
12992
12992
  `;
12993
12993
  }
12994
+ function netsuiteLoginPageTsx() {
12995
+ return `import { SignInWithNetSuite } from './SignInWithNetSuite';
12996
+
12997
+ /**
12998
+ * Default unauthenticated landing page. Replaces any password gate.
12999
+ * Customise copy / imagery freely but keep <SignInWithNetSuite /> as the
13000
+ * only call-to-action -- it points to the Worker's /auth/netsuite/start.
13001
+ */
13002
+ export function LoginPage() {
13003
+ return (
13004
+ <div className="min-h-screen bg-background text-foreground flex">
13005
+ <div className="hidden lg:flex lg:w-1/2 bg-primary text-primary-foreground p-12 flex-col justify-between">
13006
+ <div className="text-xl font-semibold">NetSuite App</div>
13007
+ <div>
13008
+ <h1 className="text-4xl font-bold leading-tight">
13009
+ Connect your NetSuite account
13010
+ </h1>
13011
+ <p className="mt-4 text-primary-foreground/80 max-w-md">
13012
+ Sign in with your NetSuite credentials to access your data securely.
13013
+ Tokens stay server-side on HelloLeo Cloud -- they never reach the
13014
+ browser.
13015
+ </p>
13016
+ </div>
13017
+ <div className="text-xs text-primary-foreground/60">
13018
+ Powered by HelloLeo
13019
+ </div>
13020
+ </div>
13021
+ <div className="flex-1 flex items-center justify-center px-6 py-12">
13022
+ <div className="w-full max-w-sm space-y-6">
13023
+ <div>
13024
+ <h2 className="text-2xl font-semibold">Welcome</h2>
13025
+ <p className="mt-2 text-sm text-muted-foreground">
13026
+ Use the button below to sign in. You'll be redirected to NetSuite
13027
+ to authorise access, then back here.
13028
+ </p>
13029
+ </div>
13030
+ <SignInWithNetSuite />
13031
+ </div>
13032
+ </div>
13033
+ </div>
13034
+ );
13035
+ }
13036
+ `;
13037
+ }
13038
+ function netsuiteAuthGateTsx() {
13039
+ return `import { useEffect, useState } from 'react';
13040
+ import { LoginPage } from './LoginPage';
13041
+ import { getNetSuiteSession, type NetSuiteSession } from '../../lib/netsuite';
13042
+
13043
+ /**
13044
+ * Mandatory auth gate for NetSuite projects. Wrap your real App content with
13045
+ * <AuthGate>...</AuthGate>. Never render NetSuite data without this gate -- it
13046
+ * is the ONLY supported authentication for a NetSuite project (no password
13047
+ * gates, no sessionStorage flags, no shared credential).
13048
+ *
13049
+ * Pattern in src/App.tsx:
13050
+ *
13051
+ * import { AuthGate } from './components/auth/AuthGate';
13052
+ * export default function App() {
13053
+ * return (
13054
+ * <AuthGate>
13055
+ * <Dashboard /> {/* your authenticated content */}
13056
+ * </AuthGate>
13057
+ * );
13058
+ * }
13059
+ */
13060
+ export function AuthGate({ children }: { children: React.ReactNode }) {
13061
+ const [session, setSession] = useState<NetSuiteSession | null>(null);
13062
+
13063
+ useEffect(() => { getNetSuiteSession().then(setSession); }, []);
13064
+
13065
+ if (session === null) {
13066
+ return (
13067
+ <div className="min-h-screen flex items-center justify-center bg-background">
13068
+ <div className="h-8 w-8 rounded-full border-2 border-primary border-t-transparent animate-spin" />
13069
+ </div>
13070
+ );
13071
+ }
13072
+
13073
+ if (!session.authenticated) return <LoginPage />;
13074
+
13075
+ return <>{children}</>;
13076
+ }
13077
+ `;
13078
+ }
13079
+ function netsuiteAppTsx() {
13080
+ return `import { AuthGate } from './components/auth/AuthGate';
13081
+
13082
+ /**
13083
+ * NetSuite project entry point. KEEP <AuthGate> as the outermost element --
13084
+ * removing it disables real OAuth and exposes NetSuite data to anonymous
13085
+ * users. Build your dashboard / pages inside the gate.
13086
+ */
13087
+ export default function App() {
13088
+ return (
13089
+ <AuthGate>
13090
+ <div className="min-h-screen bg-background text-foreground p-8">
13091
+ <h1 className="text-2xl font-bold">You are signed in to NetSuite.</h1>
13092
+ <p className="text-muted-foreground mt-2">
13093
+ Replace this placeholder with your dashboard.
13094
+ </p>
13095
+ </div>
13096
+ </AuthGate>
13097
+ );
13098
+ }
13099
+ `;
13100
+ }
12994
13101
  var scaffold = tool({
12995
13102
  description: "Scaffold a new React + Vite + Tailwind CSS project with the HelloLeo design system. " + "Creates all mandatory boilerplate: package.json, index.html (with Google Fonts), " + "vite.config.js, tailwind.config.js, postcss.config.js, src/index.css (CSS variables " + "light+dark), src/lib/utils.ts (cn helper), and src/main.tsx. " + "Call this FIRST for any new project before writing App.tsx and components.",
12996
13103
  args: {
@@ -12998,7 +13105,7 @@ var scaffold = tool({
12998
13105
  primaryColor: tool.schema.string().optional().describe("Primary brand color. Prefer raw HSL triplet (no hsl() wrapper, no # prefix), " + 'e.g. "221.2 83.2% 53.3%". Hex like "#3b82f6" is also accepted and will ' + "be auto-converted. Default: blue."),
12999
13106
  fontFamily: tool.schema.string().optional().describe('Google Font family name, e.g. "Inter", "Poppins" (default: Inter)'),
13000
13107
  borderRadius: tool.schema.string().optional().describe('Base border radius, e.g. "0.5rem", "0.75rem" (default: 0.5rem)'),
13001
- integrations: tool.schema.array(tool.schema.string()).optional().describe("Connected integration slugs for this project (live MCP list). " + 'Pass ["netsuite"] when the project uses NetSuite -- the scaffold ' + "will add src/lib/netsuite.ts (OAuth client helper) and a " + "src/components/auth/SignInWithNetSuite.tsx button so OAuth is " + "wired from the start. Forbids hardcoded password gates by " + "replacing them with the real flow against the project's Worker.")
13108
+ integrations: tool.schema.array(tool.schema.string()).optional().describe("Connected integration slugs for this project (live MCP list). " + 'Pass ["netsuite"] when the project uses NetSuite -- the scaffold ' + "will add src/lib/netsuite.ts (OAuth client helper), " + "src/components/auth/{SignInWithNetSuite,LoginPage,AuthGate}.tsx " + "and a src/App.tsx wrapped in <AuthGate>. OAuth is wired from " + "day one. Forbids hardcoded password gates -- the gate IS the " + "real flow against the project's Worker.")
13002
13109
  },
13003
13110
  async execute(args, context) {
13004
13111
  const dir = context.directory;
@@ -13029,7 +13136,13 @@ var scaffold = tool({
13029
13136
  files.push([path2.join("src", "lib", "netsuite.ts"), netsuiteTs()], [
13030
13137
  path2.join("src", "components", "auth", "SignInWithNetSuite.tsx"),
13031
13138
  signInButtonTsx()
13032
- ]);
13139
+ ], [
13140
+ path2.join("src", "components", "auth", "LoginPage.tsx"),
13141
+ netsuiteLoginPageTsx()
13142
+ ], [
13143
+ path2.join("src", "components", "auth", "AuthGate.tsx"),
13144
+ netsuiteAuthGateTsx()
13145
+ ], [path2.join("src", "App.tsx"), netsuiteAppTsx()]);
13033
13146
  }
13034
13147
  const created = [];
13035
13148
  for (const [filePath, content] of files) {
@@ -13049,7 +13162,7 @@ var scaffold = tool({
13049
13162
  "Update package.json with any extra packages you need after scaffolding."
13050
13163
  ];
13051
13164
  if (hasNetsuite) {
13052
- out.push("", "NetSuite project detected:", " - Use <SignInWithNetSuite/> from src/components/auth/ as your", " login button. NEVER use a hardcoded password gate.", " - signInWithNetSuite() in src/lib/netsuite.ts redirects to the", " project's Worker at /auth/netsuite/start. The Worker must be", " deployed via the cloudflare-backend MCP with routes:", " GET /auth/netsuite/start -- redirect to NetSuite OAuth", " GET /auth/netsuite/callback -- code -> token exchange", " GET /api/me -- current session", " POST /auth/netsuite/logout -- clear session", " Worker reads env.NETSUITE_ACCOUNT_ID, env.NETSUITE_CLIENT_ID,", " env.NETSUITE_CLIENT_SECRET (set by the runtime-setup wizard).", " - VITE_WORKER_URL must be set in the project env so the frontend", " knows where to send users. The HelloLeo Cloud provisioner", " seeds this automatically.");
13165
+ out.push("", "NetSuite project -- auth is already wired:", " - App.tsx wraps everything in <AuthGate>. Build your dashboard", " INSIDE the gate. NEVER remove the gate or build a password", " placeholder -- real OAuth is the only allowed login.", " - LoginPage.tsx + SignInWithNetSuite.tsx are the unauthenticated", " view. Customise the copy / imagery, keep <SignInWithNetSuite/>", " as the call-to-action.", " - signInWithNetSuite() in src/lib/netsuite.ts hits the Worker at", " /auth/netsuite/start. Deploy the Worker via cloudflare-backend", " MCP with routes:", " GET /auth/netsuite/start -- redirect to NetSuite OAuth", " GET /auth/netsuite/callback -- code -> token exchange", " GET /api/me -- current session", " POST /auth/netsuite/logout -- clear session", " Worker reads env.NETSUITE_ACCOUNT_ID, env.NETSUITE_CLIENT_ID,", " env.NETSUITE_CLIENT_SECRET (set by the runtime-setup wizard).", " - VITE_WORKER_URL must be set in the project env so the frontend", " knows where to send users. The HelloLeo Cloud provisioner", " seeds this automatically.");
13053
13166
  }
13054
13167
  return out.join(`
13055
13168
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helloleo/plugins",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "OpenCode plugins for HelloLeo",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -457,7 +457,7 @@ export async function signOutNetSuite(): Promise<void> {
457
457
  }
458
458
 
459
459
  function signInButtonTsx() {
460
- return `import { signInWithNetSuite } from '../lib/netsuite';
460
+ return `import { signInWithNetSuite } from '../../lib/netsuite';
461
461
 
462
462
  export function SignInWithNetSuite() {
463
463
  return (
@@ -472,6 +472,116 @@ export function SignInWithNetSuite() {
472
472
  `;
473
473
  }
474
474
 
475
+ function netsuiteLoginPageTsx() {
476
+ return `import { SignInWithNetSuite } from './SignInWithNetSuite';
477
+
478
+ /**
479
+ * Default unauthenticated landing page. Replaces any password gate.
480
+ * Customise copy / imagery freely but keep <SignInWithNetSuite /> as the
481
+ * only call-to-action -- it points to the Worker's /auth/netsuite/start.
482
+ */
483
+ export function LoginPage() {
484
+ return (
485
+ <div className="min-h-screen bg-background text-foreground flex">
486
+ <div className="hidden lg:flex lg:w-1/2 bg-primary text-primary-foreground p-12 flex-col justify-between">
487
+ <div className="text-xl font-semibold">NetSuite App</div>
488
+ <div>
489
+ <h1 className="text-4xl font-bold leading-tight">
490
+ Connect your NetSuite account
491
+ </h1>
492
+ <p className="mt-4 text-primary-foreground/80 max-w-md">
493
+ Sign in with your NetSuite credentials to access your data securely.
494
+ Tokens stay server-side on HelloLeo Cloud -- they never reach the
495
+ browser.
496
+ </p>
497
+ </div>
498
+ <div className="text-xs text-primary-foreground/60">
499
+ Powered by HelloLeo
500
+ </div>
501
+ </div>
502
+ <div className="flex-1 flex items-center justify-center px-6 py-12">
503
+ <div className="w-full max-w-sm space-y-6">
504
+ <div>
505
+ <h2 className="text-2xl font-semibold">Welcome</h2>
506
+ <p className="mt-2 text-sm text-muted-foreground">
507
+ Use the button below to sign in. You'll be redirected to NetSuite
508
+ to authorise access, then back here.
509
+ </p>
510
+ </div>
511
+ <SignInWithNetSuite />
512
+ </div>
513
+ </div>
514
+ </div>
515
+ );
516
+ }
517
+ `;
518
+ }
519
+
520
+ function netsuiteAuthGateTsx() {
521
+ return `import { useEffect, useState } from 'react';
522
+ import { LoginPage } from './LoginPage';
523
+ import { getNetSuiteSession, type NetSuiteSession } from '../../lib/netsuite';
524
+
525
+ /**
526
+ * Mandatory auth gate for NetSuite projects. Wrap your real App content with
527
+ * <AuthGate>...</AuthGate>. Never render NetSuite data without this gate -- it
528
+ * is the ONLY supported authentication for a NetSuite project (no password
529
+ * gates, no sessionStorage flags, no shared credential).
530
+ *
531
+ * Pattern in src/App.tsx:
532
+ *
533
+ * import { AuthGate } from './components/auth/AuthGate';
534
+ * export default function App() {
535
+ * return (
536
+ * <AuthGate>
537
+ * <Dashboard /> {/* your authenticated content */}
538
+ * </AuthGate>
539
+ * );
540
+ * }
541
+ */
542
+ export function AuthGate({ children }: { children: React.ReactNode }) {
543
+ const [session, setSession] = useState<NetSuiteSession | null>(null);
544
+
545
+ useEffect(() => { getNetSuiteSession().then(setSession); }, []);
546
+
547
+ if (session === null) {
548
+ return (
549
+ <div className="min-h-screen flex items-center justify-center bg-background">
550
+ <div className="h-8 w-8 rounded-full border-2 border-primary border-t-transparent animate-spin" />
551
+ </div>
552
+ );
553
+ }
554
+
555
+ if (!session.authenticated) return <LoginPage />;
556
+
557
+ return <>{children}</>;
558
+ }
559
+ `;
560
+ }
561
+
562
+ function netsuiteAppTsx() {
563
+ return `import { AuthGate } from './components/auth/AuthGate';
564
+
565
+ /**
566
+ * NetSuite project entry point. KEEP <AuthGate> as the outermost element --
567
+ * removing it disables real OAuth and exposes NetSuite data to anonymous
568
+ * users. Build your dashboard / pages inside the gate.
569
+ */
570
+ export default function App() {
571
+ return (
572
+ <AuthGate>
573
+ <div className="min-h-screen bg-background text-foreground p-8">
574
+ <h1 className="text-2xl font-bold">You are signed in to NetSuite.</h1>
575
+ <p className="text-muted-foreground mt-2">
576
+ Replace this placeholder with your dashboard.
577
+ </p>
578
+ </div>
579
+ </AuthGate>
580
+ );
581
+ }
582
+ `;
583
+ }
584
+
475
585
  export const scaffold = tool({
476
586
  description:
477
587
  "Scaffold a new React + Vite + Tailwind CSS project with the HelloLeo design system. " +
@@ -510,10 +620,11 @@ export const scaffold = tool({
510
620
  .describe(
511
621
  "Connected integration slugs for this project (live MCP list). " +
512
622
  'Pass ["netsuite"] when the project uses NetSuite -- the scaffold ' +
513
- "will add src/lib/netsuite.ts (OAuth client helper) and a " +
514
- "src/components/auth/SignInWithNetSuite.tsx button so OAuth is " +
515
- "wired from the start. Forbids hardcoded password gates by " +
516
- "replacing them with the real flow against the project's Worker.",
623
+ "will add src/lib/netsuite.ts (OAuth client helper), " +
624
+ "src/components/auth/{SignInWithNetSuite,LoginPage,AuthGate}.tsx " +
625
+ "and a src/App.tsx wrapped in <AuthGate>. OAuth is wired from " +
626
+ "day one. Forbids hardcoded password gates -- the gate IS the " +
627
+ "real flow against the project's Worker.",
517
628
  ),
518
629
  },
519
630
  async execute(args, context) {
@@ -553,6 +664,15 @@ export const scaffold = tool({
553
664
  path.join("src", "components", "auth", "SignInWithNetSuite.tsx"),
554
665
  signInButtonTsx(),
555
666
  ],
667
+ [
668
+ path.join("src", "components", "auth", "LoginPage.tsx"),
669
+ netsuiteLoginPageTsx(),
670
+ ],
671
+ [
672
+ path.join("src", "components", "auth", "AuthGate.tsx"),
673
+ netsuiteAuthGateTsx(),
674
+ ],
675
+ [path.join("src", "App.tsx"), netsuiteAppTsx()],
556
676
  );
557
677
  }
558
678
 
@@ -578,12 +698,16 @@ export const scaffold = tool({
578
698
  if (hasNetsuite) {
579
699
  out.push(
580
700
  "",
581
- "NetSuite project detected:",
582
- " - Use <SignInWithNetSuite/> from src/components/auth/ as your",
583
- " login button. NEVER use a hardcoded password gate.",
584
- " - signInWithNetSuite() in src/lib/netsuite.ts redirects to the",
585
- " project's Worker at /auth/netsuite/start. The Worker must be",
586
- " deployed via the cloudflare-backend MCP with routes:",
701
+ "NetSuite project -- auth is already wired:",
702
+ " - App.tsx wraps everything in <AuthGate>. Build your dashboard",
703
+ " INSIDE the gate. NEVER remove the gate or build a password",
704
+ " placeholder -- real OAuth is the only allowed login.",
705
+ " - LoginPage.tsx + SignInWithNetSuite.tsx are the unauthenticated",
706
+ " view. Customise the copy / imagery, keep <SignInWithNetSuite/>",
707
+ " as the call-to-action.",
708
+ " - signInWithNetSuite() in src/lib/netsuite.ts hits the Worker at",
709
+ " /auth/netsuite/start. Deploy the Worker via cloudflare-backend",
710
+ " MCP with routes:",
587
711
  " GET /auth/netsuite/start -- redirect to NetSuite OAuth",
588
712
  " GET /auth/netsuite/callback -- code -> token exchange",
589
713
  " GET /api/me -- current session",