@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 +117 -4
- package/package.json +1 -1
- package/src/tools/scaffold.ts +135 -11
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 '
|
|
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)
|
|
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
|
|
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
package/src/tools/scaffold.ts
CHANGED
|
@@ -457,7 +457,7 @@ export async function signOutNetSuite(): Promise<void> {
|
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
function signInButtonTsx() {
|
|
460
|
-
return `import { signInWithNetSuite } from '
|
|
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)
|
|
514
|
-
"src/components/auth/SignInWithNetSuite.tsx
|
|
515
|
-
"
|
|
516
|
-
"
|
|
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
|
|
582
|
-
" -
|
|
583
|
-
"
|
|
584
|
-
"
|
|
585
|
-
"
|
|
586
|
-
"
|
|
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",
|