@fluid-app/fluid-cli 0.1.0 → 0.1.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/dist/bin/fluid.mjs +1 -8
- package/dist/bin/fluid.mjs.map +1 -1
- package/package.json +3 -3
package/dist/bin/fluid.mjs
CHANGED
|
@@ -333,10 +333,6 @@ function discoverFromWorkspace(startDir) {
|
|
|
333
333
|
} catch {
|
|
334
334
|
return [];
|
|
335
335
|
}
|
|
336
|
-
for (const domainDir of domainDirs) {
|
|
337
|
-
const name = readPackageName(domainDir);
|
|
338
|
-
if (name && isPluginName(name)) console.debug(`[fluid-cli] Skipping plugin "${name}" at packages/${domainDir.split("/").pop()} — expected depth is packages/<domain>/<package>/. Move it one level deeper to enable auto-discovery.`);
|
|
339
|
-
}
|
|
340
336
|
for (const domainDir of domainDirs) {
|
|
341
337
|
let subDirs;
|
|
342
338
|
try {
|
|
@@ -348,10 +344,7 @@ function discoverFromWorkspace(startDir) {
|
|
|
348
344
|
const name = readPackageName(subDir);
|
|
349
345
|
if (!name || !name.startsWith(`${PLUGIN_SCOPE}/`) || !isPluginName(name)) continue;
|
|
350
346
|
const distEntry = join(subDir, "dist", "index.mjs");
|
|
351
|
-
if (!existsSync(distEntry))
|
|
352
|
-
console.debug(`[fluid-cli] Found plugin "${name}" but dist/index.mjs is missing — run "pnpm build" in the plugin package.`);
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
347
|
+
if (!existsSync(distEntry)) continue;
|
|
355
348
|
results.push({
|
|
356
349
|
name,
|
|
357
350
|
importSpecifier: pathToFileURL(distEntry).href
|
package/dist/bin/fluid.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluid.mjs","names":[],"sources":["../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/whoami.ts","../../src/commands/switch.ts","../../src/plugins/discovery.ts","../../src/plugins/loader.ts","../../src/bin/fluid.ts"],"sourcesContent":["/**\n * fluid login — authenticate via email MFA or API token\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport {\n validateToken,\n sendMfa,\n confirmMfa,\n type CompanyChoice,\n} from \"../auth/fluid-api.js\";\nimport { updateConfig, readConfig } from \"../config/config.js\";\n\nasync function confirmOverwrite(profileName: string): Promise<boolean> {\n const existing = readConfig().profiles[profileName];\n if (!existing) return true;\n\n const response = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Profile \"${profileName}\" already exists (${existing.companyName}). Overwrite?`,\n initial: false,\n });\n return Boolean(response[\"overwrite\"]);\n}\n\nfunction storeProfile(\n profileName: string,\n token: string,\n companyName: string,\n): void {\n updateConfig((config) => ({\n ...config,\n activeProfile: profileName,\n profiles: {\n ...config.profiles,\n [profileName]: {\n name: profileName,\n token,\n companyName,\n storedAt: new Date().toISOString(),\n },\n },\n }));\n}\n\nasync function loginWithToken(\n token: string,\n profileName?: string,\n): Promise<void> {\n const spinner = ora(\"Validating token...\").start();\n const result = await validateToken(token);\n\n if (!result.success) {\n spinner.fail(chalk.red(result.error.message));\n if (result.error.details) {\n console.log(chalk.dim(result.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n const name = profileName ?? result.value.name;\n\n // Stop spinner before potential interactive prompt to avoid terminal corruption\n spinner.succeed(\n chalk.green(`Token valid — ${chalk.bold(result.value.name)}`),\n );\n\n if (!(await confirmOverwrite(name))) {\n console.log(\n chalk.yellow(\"Login cancelled — existing profile not overwritten.\"),\n );\n return;\n }\n\n storeProfile(name, token, result.value.name);\n console.log(\n chalk.green(\n `Logged in as ${chalk.bold(result.value.name)} (profile: ${chalk.bold(name)})`,\n ),\n );\n}\n\nasync function loginWithEmail(\n initialEmail?: string,\n profileName?: string,\n): Promise<void> {\n // 1. Prompt for email\n let email = initialEmail;\n if (!email) {\n const response = await prompts({\n type: \"text\",\n name: \"email\",\n message: \"Enter your email\",\n });\n email = response[\"email\"] as string | undefined;\n if (!email) {\n console.log(chalk.red(\"No email provided. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n }\n\n // 2. Send MFA code\n const sendSpinner = ora(\"Sending verification code...\").start();\n const sendResult = await sendMfa(email);\n\n if (!sendResult.success) {\n sendSpinner.fail(chalk.red(sendResult.error.message));\n if (sendResult.error.details) {\n console.log(chalk.dim(sendResult.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n const expiresAt = new Date(sendResult.value.expiresAt);\n const expiresInMs = expiresAt.getTime() - Date.now();\n const expiresInMin = Math.round(expiresInMs / 1000 / 60);\n const expiryNote =\n expiresInMin > 0\n ? `expires in ~${expiresInMin} min`\n : \"code may expire soon — check your email quickly\";\n sendSpinner.succeed(\n `Verification code sent — check your email (${expiryNote})`,\n );\n\n // 3. Prompt for verification code (retry up to 3 times on invalid code)\n const MAX_CODE_ATTEMPTS = 3;\n let confirmResult;\n for (let attempt = 1; attempt <= MAX_CODE_ATTEMPTS; attempt++) {\n const codeResponse = await prompts({\n type: \"text\",\n name: \"code\",\n message: \"Enter the 6-digit code\",\n });\n const code = codeResponse[\"code\"] as string | undefined;\n if (!code) {\n console.log(chalk.red(\"No code provided. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n if (!/^\\d{6}$/.test(code)) {\n console.log(chalk.red(\"Code must be exactly 6 digits.\"));\n if (attempt < MAX_CODE_ATTEMPTS) continue;\n console.log(chalk.red(\"Too many invalid attempts. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n\n const confirmSpinner = ora(\"Verifying code...\").start();\n confirmResult = await confirmMfa(sendResult.value.uuid, code);\n\n if (confirmResult.success) {\n confirmSpinner.succeed(\"Verified\");\n break;\n }\n\n // Retryable: wrong code, still have attempts left\n if (\n confirmResult.error.code === \"INVALID_CODE\" &&\n attempt < MAX_CODE_ATTEMPTS\n ) {\n confirmSpinner.fail(\n chalk.red(\n `${confirmResult.error.message} (attempt ${attempt}/${MAX_CODE_ATTEMPTS})`,\n ),\n );\n continue;\n }\n\n // Non-retryable (expired, unreachable, etc.) or final attempt\n confirmSpinner.fail(chalk.red(confirmResult.error.message));\n if (confirmResult.error.details) {\n console.log(chalk.dim(confirmResult.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n if (!confirmResult?.success) return;\n\n const { companies } = confirmResult.value;\n\n if (companies.length === 0) {\n console.log(chalk.red(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n\n // 4. Select company (auto-select if only one)\n let selected: CompanyChoice;\n if (companies.length === 1) {\n const first = companies[0];\n if (!first) {\n console.log(chalk.red(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n selected = first;\n } else {\n const selectResponse = await prompts({\n type: \"select\",\n name: \"companyIndex\",\n message: \"Select a company\",\n choices: companies.map((c, i) => ({\n title: `${c.name} (${c.shopName})`,\n value: i,\n })),\n });\n const idx = selectResponse[\"companyIndex\"] as number | undefined;\n if (idx === undefined) {\n console.log(chalk.red(\"No company selected. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n const choice = companies[idx];\n if (!choice) {\n console.log(chalk.red(\"Invalid selection. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n selected = choice;\n }\n\n // 5. Store profile\n const name = profileName ?? selected.name;\n\n if (!(await confirmOverwrite(name))) {\n console.log(\n chalk.yellow(\"Login cancelled — existing profile not overwritten.\"),\n );\n return;\n }\n\n storeProfile(name, selected.jwt, selected.name);\n console.log(\n chalk.green(\n `Logged in as ${chalk.bold(selected.name)} (profile: ${chalk.bold(name)})`,\n ),\n );\n}\n\nexport const loginCommand = new Command(\"login\")\n .description(\"Authenticate with the Fluid API\")\n .option(\n \"-t, --token <token>\",\n \"API token (skips email flow). Prefer FLUID_TOKEN env var to avoid shell history exposure\",\n )\n .option(\"-e, --email <email>\", \"Email address for MFA login\")\n .option(\"-n, --name <name>\", \"Profile name (defaults to company name)\")\n .action(async (opts: { token?: string; email?: string; name?: string }) => {\n // Accept token from env var to avoid shell history / process listing exposure\n const token = opts.token ?? process.env[\"FLUID_TOKEN\"];\n if (token) {\n if (opts.token) {\n console.log(\n chalk.yellow(\n \"Warning: token passed via --token flag is visible in shell history and process listings. \" +\n \"Prefer the FLUID_TOKEN environment variable.\",\n ),\n );\n }\n await loginWithToken(token, opts.name);\n } else {\n await loginWithEmail(opts.email, opts.name);\n }\n });\n","/**\n * fluid logout — remove stored auth profile(s)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { readConfig, writeConfig } from \"../config/config.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Remove stored authentication\")\n .option(\"-a, --all\", \"Remove all profiles\")\n .action((opts: { all?: boolean }) => {\n const config = readConfig();\n\n if (opts.all) {\n writeConfig({\n ...config,\n activeProfile: null,\n profiles: {},\n });\n console.log(chalk.green(\"All profiles removed.\"));\n return;\n }\n\n if (!config.activeProfile) {\n console.log(chalk.yellow(\"Not currently logged in.\"));\n return;\n }\n\n const profileName = config.activeProfile;\n const { [profileName]: _, ...remainingProfiles } = config.profiles;\n\n // Pick the first remaining profile as active, or null\n const nextActive = Object.keys(remainingProfiles)[0] ?? null;\n\n writeConfig({\n ...config,\n activeProfile: nextActive,\n profiles: remainingProfiles,\n });\n\n console.log(\n chalk.green(`Logged out of profile ${chalk.bold(profileName)}.`),\n );\n if (nextActive) {\n console.log(chalk.dim(`Switched to profile ${nextActive}.`));\n }\n });\n","/**\n * fluid whoami — show current auth profile and validate against API\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { getActiveProfile } from \"../auth/token.js\";\nimport { validateToken } from \"../auth/fluid-api.js\";\n\nexport const whoamiCommand = new Command(\"whoami\")\n .description(\"Show the current authenticated profile\")\n .action(async () => {\n const profile = getActiveProfile();\n\n if (!profile) {\n console.log(\n chalk.yellow(\"Not logged in. Run `fluid login` to authenticate.\"),\n );\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Verifying token...\").start();\n const result = await validateToken(profile.token);\n\n if (!result.success) {\n spinner.fail(chalk.red(\"Token is no longer valid.\"));\n console.log(chalk.dim(`Profile: ${profile.name}`));\n console.log(chalk.dim(\"Run `fluid login` to re-authenticate.\"));\n process.exitCode = 1;\n return;\n }\n\n spinner.succeed(chalk.green(\"Authenticated\"));\n console.log(` Profile: ${chalk.bold(profile.name)}`);\n console.log(` Company: ${chalk.bold(result.value.name)}`);\n console.log(` Stored: ${chalk.dim(profile.storedAt)}`);\n });\n","/**\n * fluid switch — switch between stored auth profiles\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport { readConfig, writeConfig } from \"../config/config.js\";\n\nexport const switchCommand = new Command(\"switch\")\n .description(\"Switch between stored auth profiles\")\n .argument(\"[profile]\", \"Profile name to switch to\")\n .action(async (profileArg?: string) => {\n const config = readConfig();\n const profileNames = Object.keys(config.profiles);\n\n if (profileNames.length === 0) {\n console.log(chalk.yellow(\"No profiles stored. Run `fluid login` first.\"));\n process.exitCode = 1;\n return;\n }\n\n let targetProfile = profileArg;\n\n if (!targetProfile) {\n const response = await prompts({\n type: \"select\",\n name: \"profile\",\n message: \"Select a profile\",\n choices: profileNames.map((name) => ({\n title: name === config.activeProfile ? `${name} (active)` : name,\n value: name,\n })),\n });\n\n targetProfile = response[\"profile\"] as string | undefined;\n if (!targetProfile) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n }\n\n if (!config.profiles[targetProfile]) {\n console.log(chalk.red(`Profile \"${targetProfile}\" not found.`));\n console.log(chalk.dim(`Available: ${profileNames.join(\", \")}`));\n process.exitCode = 1;\n return;\n }\n\n writeConfig({ ...config, activeProfile: targetProfile });\n console.log(\n chalk.green(`Switched to profile ${chalk.bold(targetProfile)}.`),\n );\n });\n","/**\n * Auto-discover @fluid-app CLI plugins.\n *\n * Two discovery strategies run in order:\n *\n * 1. **node_modules scan** — look in `<basePath>/node_modules/@fluid-app/`\n * for directories whose names match the plugin naming convention.\n * This is the primary mechanism for published installs.\n *\n * 2. **Workspace scan** — walk upward from the CLI core package root to the\n * monorepo workspace root, then scan `packages/` for sibling plugin\n * packages. This covers the pnpm-workspace development case where\n * plugins are not symlinked into `node_modules`.\n *\n * Only first-party @fluid-app scoped packages are loaded.\n */\n\nimport { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\nconst PLUGIN_SCOPE = \"@fluid-app\";\n\n/**\n * A plugin package name must match one of these patterns:\n * - @fluid-app/fluid-cli-* (standard)\n * - @fluid-app/*-cli-commands (v2025-06 style)\n */\nfunction isPluginName(packageName: string): boolean {\n if (!packageName.startsWith(`${PLUGIN_SCOPE}/`)) return false;\n const bare = packageName.slice(`${PLUGIN_SCOPE}/`.length);\n return bare.startsWith(\"fluid-cli-\") || bare.endsWith(\"-cli-commands\");\n}\n\n// ---------------------------------------------------------------------------\n// Strategy 1: node_modules scan (production / published installs)\n// ---------------------------------------------------------------------------\n\nfunction discoverFromNodeModules(basePath: string): string[] {\n const scopeDir = join(basePath, \"node_modules\", PLUGIN_SCOPE);\n\n if (!existsSync(scopeDir)) return [];\n\n return readdirSync(scopeDir, { withFileTypes: true })\n .filter(\n (entry) =>\n (entry.isDirectory() || entry.isSymbolicLink()) &&\n isPluginName(`${PLUGIN_SCOPE}/${entry.name}`),\n )\n .map((entry) => `${PLUGIN_SCOPE}/${entry.name}`);\n}\n\n// ---------------------------------------------------------------------------\n// Strategy 2: workspace scan (pnpm monorepo development)\n// ---------------------------------------------------------------------------\n\nfunction findWorkspaceRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n if (\n existsSync(join(dir, \"pnpm-workspace.yaml\")) ||\n existsSync(join(dir, \"pnpm-workspace.yml\"))\n ) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nfunction readPackageName(dir: string): string | null {\n const pkgPath = join(dir, \"package.json\");\n if (!existsSync(pkgPath)) return null;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n name?: string;\n };\n return pkg.name ?? null;\n } catch {\n return null;\n }\n}\n\nexport interface DiscoveredPlugin {\n name: string;\n importSpecifier: string;\n}\n\nfunction discoverFromWorkspace(startDir: string): DiscoveredPlugin[] {\n const workspaceRoot = findWorkspaceRoot(startDir);\n if (!workspaceRoot) return [];\n\n const results: DiscoveredPlugin[] = [];\n const packagesDir = join(workspaceRoot, \"packages\");\n if (!existsSync(packagesDir)) return [];\n\n // Scan exactly two levels deep under packages/ (e.g. packages/cli/themes,\n // packages/cli/v2025-06). This matches the monorepo convention where all\n // packages live at packages/<domain>/<package>/.\n // If a plugin is added at a different depth, update this scan accordingly.\n let domainDirs: string[];\n try {\n domainDirs = readdirSync(packagesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => join(packagesDir, e.name));\n } catch {\n return [];\n }\n\n // Warn about plugin-like packages at the wrong nesting depth (depth 1\n // instead of the expected depth 2). This helps developers diagnose why\n // a plugin isn't being discovered after a directory restructure.\n for (const domainDir of domainDirs) {\n const name = readPackageName(domainDir);\n if (name && isPluginName(name)) {\n console.debug(\n `[fluid-cli] Skipping plugin \"${name}\" at packages/${domainDir.split(\"/\").pop()} — ` +\n `expected depth is packages/<domain>/<package>/. Move it one level deeper to enable auto-discovery.`,\n );\n }\n }\n\n for (const domainDir of domainDirs) {\n let subDirs: string[];\n try {\n subDirs = readdirSync(domainDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => join(domainDir, e.name));\n } catch {\n continue;\n }\n\n for (const subDir of subDirs) {\n const name = readPackageName(subDir);\n if (!name || !name.startsWith(`${PLUGIN_SCOPE}/`) || !isPluginName(name))\n continue;\n\n const distEntry = join(subDir, \"dist\", \"index.mjs\");\n if (!existsSync(distEntry)) {\n console.debug(\n `[fluid-cli] Found plugin \"${name}\" but dist/index.mjs is missing — run \"pnpm build\" in the plugin package.`,\n );\n continue;\n }\n\n results.push({\n name,\n importSpecifier: pathToFileURL(distEntry).href,\n });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Exported API\n// ---------------------------------------------------------------------------\n\n/** Resolved once — the CLI core package root (`packages/cli/core/`). */\nexport const corePackageDir = dirname(\n dirname(dirname(fileURLToPath(import.meta.url))),\n);\n\n/**\n * Discover installed plugin packages. Returns a de-duplicated, sorted list\n * of `{ name, importSpecifier }` objects.\n *\n * @param basePath - Directory to scan for `node_modules/@fluid-app/`\n * @param searchFrom - Starting directory for workspace root detection\n * (walked upward via `findWorkspaceRoot`). Pass `null` to skip workspace\n * scanning (useful in tests). Defaults to `corePackageDir`.\n */\nexport function discoverPlugins(\n basePath: string,\n searchFrom?: string | null,\n): DiscoveredPlugin[] {\n const seen = new Set<string>();\n const results: DiscoveredPlugin[] = [];\n\n // Strategy 1: node_modules (package name is the import specifier)\n for (const name of discoverFromNodeModules(basePath)) {\n if (!seen.has(name)) {\n seen.add(name);\n results.push({ name, importSpecifier: name });\n }\n }\n\n // Strategy 2: workspace siblings\n if (searchFrom !== null) {\n const wsDir = searchFrom ?? corePackageDir;\n for (const wp of discoverFromWorkspace(wsDir)) {\n if (!seen.has(wp.name)) {\n seen.add(wp.name);\n results.push({ name: wp.name, importSpecifier: wp.importSpecifier });\n }\n }\n }\n\n return results.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Dynamically import a plugin module and return its default export.\n *\n * @internal Not intended for external callers. Only called with specifiers\n * produced by {@link discoverPlugins}:\n * - bare package names (e.g. `@fluid-app/fluid-cli-portal`) from the\n * node_modules scan, validated by {@link isPluginName}.\n * - `file://` URLs from {@link discoverFromWorkspace}, which constructs\n * them internally from validated workspace paths (`packages/` tree,\n * `dist/index.mjs`). These are trusted internal specifiers.\n */\nexport async function importPlugin(specifier: string): Promise<unknown> {\n // file:// URLs are trusted — they originate from discoverFromWorkspace\n // which builds them from validated workspace paths under packages/.\n // For bare package names, verify they match the plugin naming convention.\n if (!specifier.startsWith(\"file://\") && !isPluginName(specifier)) {\n throw new Error(`Refusing to import non-plugin package: ${specifier}`);\n }\n const mod = (await import(specifier)) as Record<string, unknown>;\n return mod[\"default\"] ?? mod;\n}\n","/**\n * Plugin loader — orchestrates discovery and registration\n */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { getConfigDir } from \"../config/paths.js\";\nimport { getAuthToken } from \"../auth/token.js\";\nimport { readConfig } from \"../config/config.js\";\nimport type { FluidPlugin, PluginContext } from \"./types.js\";\nimport { discoverPlugins, importPlugin } from \"./discovery.js\";\n\nfunction isFluidPlugin(value: unknown): value is FluidPlugin {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (\n typeof obj[\"name\"] === \"string\" &&\n typeof obj[\"version\"] === \"string\" &&\n typeof obj[\"register\"] === \"function\"\n );\n}\n\n/**\n * Discover, import, and register all installed plugins\n */\nexport async function loadPlugins(\n program: Command,\n basePath: string,\n): Promise<void> {\n const discovered = discoverPlugins(basePath);\n const { enabledPlugins } = readConfig();\n\n // If enabledPlugins is set, only load explicitly allowed plugins.\n // This acts as an allow-list to prevent accidental supply-chain execution.\n const allowedSet = enabledPlugins !== null ? new Set(enabledPlugins) : null;\n const plugins = allowedSet\n ? discovered.filter((p) => allowedSet.has(p.name))\n : discovered;\n\n if (plugins.length === 0) return;\n\n const ctx: PluginContext = {\n program,\n getAuthToken,\n configDir: getConfigDir(),\n };\n\n for (const { name, importSpecifier } of plugins) {\n try {\n const exported = await importPlugin(importSpecifier);\n\n if (!isFluidPlugin(exported)) {\n console.warn(\n chalk.yellow(`Warning: ${name} does not export a valid FluidPlugin`),\n );\n continue;\n }\n\n await exported.register(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(`Warning: Failed to load plugin ${name}: ${message}`),\n );\n }\n }\n}\n","#!/usr/bin/env node\n\nimport { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { loginCommand } from \"../commands/login.js\";\nimport { logoutCommand } from \"../commands/logout.js\";\nimport { whoamiCommand } from \"../commands/whoami.js\";\nimport { switchCommand } from \"../commands/switch.js\";\nimport { loadPlugins } from \"../plugins/loader.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\nconst packageRoot = process.cwd();\n\nconst program = new Command();\n\nprogram.name(\"fluid\").description(\"Fluid Commerce CLI\").version(version);\n\n// Built-in auth commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(switchCommand);\n\n// Discover and load all plugins (auto-discovered from node_modules and workspace)\nawait loadPlugins(program, packageRoot);\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fluid.mjs","names":[],"sources":["../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/whoami.ts","../../src/commands/switch.ts","../../src/plugins/discovery.ts","../../src/plugins/loader.ts","../../src/bin/fluid.ts"],"sourcesContent":["/**\n * fluid login — authenticate via email MFA or API token\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport {\n validateToken,\n sendMfa,\n confirmMfa,\n type CompanyChoice,\n} from \"../auth/fluid-api.js\";\nimport { updateConfig, readConfig } from \"../config/config.js\";\n\nasync function confirmOverwrite(profileName: string): Promise<boolean> {\n const existing = readConfig().profiles[profileName];\n if (!existing) return true;\n\n const response = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Profile \"${profileName}\" already exists (${existing.companyName}). Overwrite?`,\n initial: false,\n });\n return Boolean(response[\"overwrite\"]);\n}\n\nfunction storeProfile(\n profileName: string,\n token: string,\n companyName: string,\n): void {\n updateConfig((config) => ({\n ...config,\n activeProfile: profileName,\n profiles: {\n ...config.profiles,\n [profileName]: {\n name: profileName,\n token,\n companyName,\n storedAt: new Date().toISOString(),\n },\n },\n }));\n}\n\nasync function loginWithToken(\n token: string,\n profileName?: string,\n): Promise<void> {\n const spinner = ora(\"Validating token...\").start();\n const result = await validateToken(token);\n\n if (!result.success) {\n spinner.fail(chalk.red(result.error.message));\n if (result.error.details) {\n console.log(chalk.dim(result.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n const name = profileName ?? result.value.name;\n\n // Stop spinner before potential interactive prompt to avoid terminal corruption\n spinner.succeed(\n chalk.green(`Token valid — ${chalk.bold(result.value.name)}`),\n );\n\n if (!(await confirmOverwrite(name))) {\n console.log(\n chalk.yellow(\"Login cancelled — existing profile not overwritten.\"),\n );\n return;\n }\n\n storeProfile(name, token, result.value.name);\n console.log(\n chalk.green(\n `Logged in as ${chalk.bold(result.value.name)} (profile: ${chalk.bold(name)})`,\n ),\n );\n}\n\nasync function loginWithEmail(\n initialEmail?: string,\n profileName?: string,\n): Promise<void> {\n // 1. Prompt for email\n let email = initialEmail;\n if (!email) {\n const response = await prompts({\n type: \"text\",\n name: \"email\",\n message: \"Enter your email\",\n });\n email = response[\"email\"] as string | undefined;\n if (!email) {\n console.log(chalk.red(\"No email provided. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n }\n\n // 2. Send MFA code\n const sendSpinner = ora(\"Sending verification code...\").start();\n const sendResult = await sendMfa(email);\n\n if (!sendResult.success) {\n sendSpinner.fail(chalk.red(sendResult.error.message));\n if (sendResult.error.details) {\n console.log(chalk.dim(sendResult.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n const expiresAt = new Date(sendResult.value.expiresAt);\n const expiresInMs = expiresAt.getTime() - Date.now();\n const expiresInMin = Math.round(expiresInMs / 1000 / 60);\n const expiryNote =\n expiresInMin > 0\n ? `expires in ~${expiresInMin} min`\n : \"code may expire soon — check your email quickly\";\n sendSpinner.succeed(\n `Verification code sent — check your email (${expiryNote})`,\n );\n\n // 3. Prompt for verification code (retry up to 3 times on invalid code)\n const MAX_CODE_ATTEMPTS = 3;\n let confirmResult;\n for (let attempt = 1; attempt <= MAX_CODE_ATTEMPTS; attempt++) {\n const codeResponse = await prompts({\n type: \"text\",\n name: \"code\",\n message: \"Enter the 6-digit code\",\n });\n const code = codeResponse[\"code\"] as string | undefined;\n if (!code) {\n console.log(chalk.red(\"No code provided. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n if (!/^\\d{6}$/.test(code)) {\n console.log(chalk.red(\"Code must be exactly 6 digits.\"));\n if (attempt < MAX_CODE_ATTEMPTS) continue;\n console.log(chalk.red(\"Too many invalid attempts. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n\n const confirmSpinner = ora(\"Verifying code...\").start();\n confirmResult = await confirmMfa(sendResult.value.uuid, code);\n\n if (confirmResult.success) {\n confirmSpinner.succeed(\"Verified\");\n break;\n }\n\n // Retryable: wrong code, still have attempts left\n if (\n confirmResult.error.code === \"INVALID_CODE\" &&\n attempt < MAX_CODE_ATTEMPTS\n ) {\n confirmSpinner.fail(\n chalk.red(\n `${confirmResult.error.message} (attempt ${attempt}/${MAX_CODE_ATTEMPTS})`,\n ),\n );\n continue;\n }\n\n // Non-retryable (expired, unreachable, etc.) or final attempt\n confirmSpinner.fail(chalk.red(confirmResult.error.message));\n if (confirmResult.error.details) {\n console.log(chalk.dim(confirmResult.error.details));\n }\n process.exitCode = 1;\n return;\n }\n\n if (!confirmResult?.success) return;\n\n const { companies } = confirmResult.value;\n\n if (companies.length === 0) {\n console.log(chalk.red(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n\n // 4. Select company (auto-select if only one)\n let selected: CompanyChoice;\n if (companies.length === 1) {\n const first = companies[0];\n if (!first) {\n console.log(chalk.red(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n selected = first;\n } else {\n const selectResponse = await prompts({\n type: \"select\",\n name: \"companyIndex\",\n message: \"Select a company\",\n choices: companies.map((c, i) => ({\n title: `${c.name} (${c.shopName})`,\n value: i,\n })),\n });\n const idx = selectResponse[\"companyIndex\"] as number | undefined;\n if (idx === undefined) {\n console.log(chalk.red(\"No company selected. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n const choice = companies[idx];\n if (!choice) {\n console.log(chalk.red(\"Invalid selection. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n selected = choice;\n }\n\n // 5. Store profile\n const name = profileName ?? selected.name;\n\n if (!(await confirmOverwrite(name))) {\n console.log(\n chalk.yellow(\"Login cancelled — existing profile not overwritten.\"),\n );\n return;\n }\n\n storeProfile(name, selected.jwt, selected.name);\n console.log(\n chalk.green(\n `Logged in as ${chalk.bold(selected.name)} (profile: ${chalk.bold(name)})`,\n ),\n );\n}\n\nexport const loginCommand = new Command(\"login\")\n .description(\"Authenticate with the Fluid API\")\n .option(\n \"-t, --token <token>\",\n \"API token (skips email flow). Prefer FLUID_TOKEN env var to avoid shell history exposure\",\n )\n .option(\"-e, --email <email>\", \"Email address for MFA login\")\n .option(\"-n, --name <name>\", \"Profile name (defaults to company name)\")\n .action(async (opts: { token?: string; email?: string; name?: string }) => {\n // Accept token from env var to avoid shell history / process listing exposure\n const token = opts.token ?? process.env[\"FLUID_TOKEN\"];\n if (token) {\n if (opts.token) {\n console.log(\n chalk.yellow(\n \"Warning: token passed via --token flag is visible in shell history and process listings. \" +\n \"Prefer the FLUID_TOKEN environment variable.\",\n ),\n );\n }\n await loginWithToken(token, opts.name);\n } else {\n await loginWithEmail(opts.email, opts.name);\n }\n });\n","/**\n * fluid logout — remove stored auth profile(s)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { readConfig, writeConfig } from \"../config/config.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Remove stored authentication\")\n .option(\"-a, --all\", \"Remove all profiles\")\n .action((opts: { all?: boolean }) => {\n const config = readConfig();\n\n if (opts.all) {\n writeConfig({\n ...config,\n activeProfile: null,\n profiles: {},\n });\n console.log(chalk.green(\"All profiles removed.\"));\n return;\n }\n\n if (!config.activeProfile) {\n console.log(chalk.yellow(\"Not currently logged in.\"));\n return;\n }\n\n const profileName = config.activeProfile;\n const { [profileName]: _, ...remainingProfiles } = config.profiles;\n\n // Pick the first remaining profile as active, or null\n const nextActive = Object.keys(remainingProfiles)[0] ?? null;\n\n writeConfig({\n ...config,\n activeProfile: nextActive,\n profiles: remainingProfiles,\n });\n\n console.log(\n chalk.green(`Logged out of profile ${chalk.bold(profileName)}.`),\n );\n if (nextActive) {\n console.log(chalk.dim(`Switched to profile ${nextActive}.`));\n }\n });\n","/**\n * fluid whoami — show current auth profile and validate against API\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { getActiveProfile } from \"../auth/token.js\";\nimport { validateToken } from \"../auth/fluid-api.js\";\n\nexport const whoamiCommand = new Command(\"whoami\")\n .description(\"Show the current authenticated profile\")\n .action(async () => {\n const profile = getActiveProfile();\n\n if (!profile) {\n console.log(\n chalk.yellow(\"Not logged in. Run `fluid login` to authenticate.\"),\n );\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Verifying token...\").start();\n const result = await validateToken(profile.token);\n\n if (!result.success) {\n spinner.fail(chalk.red(\"Token is no longer valid.\"));\n console.log(chalk.dim(`Profile: ${profile.name}`));\n console.log(chalk.dim(\"Run `fluid login` to re-authenticate.\"));\n process.exitCode = 1;\n return;\n }\n\n spinner.succeed(chalk.green(\"Authenticated\"));\n console.log(` Profile: ${chalk.bold(profile.name)}`);\n console.log(` Company: ${chalk.bold(result.value.name)}`);\n console.log(` Stored: ${chalk.dim(profile.storedAt)}`);\n });\n","/**\n * fluid switch — switch between stored auth profiles\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport { readConfig, writeConfig } from \"../config/config.js\";\n\nexport const switchCommand = new Command(\"switch\")\n .description(\"Switch between stored auth profiles\")\n .argument(\"[profile]\", \"Profile name to switch to\")\n .action(async (profileArg?: string) => {\n const config = readConfig();\n const profileNames = Object.keys(config.profiles);\n\n if (profileNames.length === 0) {\n console.log(chalk.yellow(\"No profiles stored. Run `fluid login` first.\"));\n process.exitCode = 1;\n return;\n }\n\n let targetProfile = profileArg;\n\n if (!targetProfile) {\n const response = await prompts({\n type: \"select\",\n name: \"profile\",\n message: \"Select a profile\",\n choices: profileNames.map((name) => ({\n title: name === config.activeProfile ? `${name} (active)` : name,\n value: name,\n })),\n });\n\n targetProfile = response[\"profile\"] as string | undefined;\n if (!targetProfile) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n }\n\n if (!config.profiles[targetProfile]) {\n console.log(chalk.red(`Profile \"${targetProfile}\" not found.`));\n console.log(chalk.dim(`Available: ${profileNames.join(\", \")}`));\n process.exitCode = 1;\n return;\n }\n\n writeConfig({ ...config, activeProfile: targetProfile });\n console.log(\n chalk.green(`Switched to profile ${chalk.bold(targetProfile)}.`),\n );\n });\n","/**\n * Auto-discover @fluid-app CLI plugins.\n *\n * Two discovery strategies run in order:\n *\n * 1. **node_modules scan** — look in `<basePath>/node_modules/@fluid-app/`\n * for directories whose names match the plugin naming convention.\n * This is the primary mechanism for published installs.\n *\n * 2. **Workspace scan** — walk upward from the CLI core package root to the\n * monorepo workspace root, then scan `packages/` for sibling plugin\n * packages. This covers the pnpm-workspace development case where\n * plugins are not symlinked into `node_modules`.\n *\n * Only first-party @fluid-app scoped packages are loaded.\n */\n\nimport { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\nconst PLUGIN_SCOPE = \"@fluid-app\";\n\n/**\n * A plugin package name must match one of these patterns:\n * - @fluid-app/fluid-cli-* (standard)\n * - @fluid-app/*-cli-commands (v2025-06 style)\n */\nfunction isPluginName(packageName: string): boolean {\n if (!packageName.startsWith(`${PLUGIN_SCOPE}/`)) return false;\n const bare = packageName.slice(`${PLUGIN_SCOPE}/`.length);\n return bare.startsWith(\"fluid-cli-\") || bare.endsWith(\"-cli-commands\");\n}\n\n// ---------------------------------------------------------------------------\n// Strategy 1: node_modules scan (production / published installs)\n// ---------------------------------------------------------------------------\n\nfunction discoverFromNodeModules(basePath: string): string[] {\n const scopeDir = join(basePath, \"node_modules\", PLUGIN_SCOPE);\n\n if (!existsSync(scopeDir)) return [];\n\n return readdirSync(scopeDir, { withFileTypes: true })\n .filter(\n (entry) =>\n (entry.isDirectory() || entry.isSymbolicLink()) &&\n isPluginName(`${PLUGIN_SCOPE}/${entry.name}`),\n )\n .map((entry) => `${PLUGIN_SCOPE}/${entry.name}`);\n}\n\n// ---------------------------------------------------------------------------\n// Strategy 2: workspace scan (pnpm monorepo development)\n// ---------------------------------------------------------------------------\n\nfunction findWorkspaceRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n if (\n existsSync(join(dir, \"pnpm-workspace.yaml\")) ||\n existsSync(join(dir, \"pnpm-workspace.yml\"))\n ) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nfunction readPackageName(dir: string): string | null {\n const pkgPath = join(dir, \"package.json\");\n if (!existsSync(pkgPath)) return null;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n name?: string;\n };\n return pkg.name ?? null;\n } catch {\n return null;\n }\n}\n\nexport interface DiscoveredPlugin {\n name: string;\n importSpecifier: string;\n}\n\nfunction discoverFromWorkspace(startDir: string): DiscoveredPlugin[] {\n const workspaceRoot = findWorkspaceRoot(startDir);\n if (!workspaceRoot) return [];\n\n const results: DiscoveredPlugin[] = [];\n const packagesDir = join(workspaceRoot, \"packages\");\n if (!existsSync(packagesDir)) return [];\n\n // Scan exactly two levels deep under packages/ (e.g. packages/cli/themes,\n // packages/cli/v2025-06). This matches the monorepo convention where all\n // packages live at packages/<domain>/<package>/.\n // If a plugin is added at a different depth, update this scan accordingly.\n let domainDirs: string[];\n try {\n domainDirs = readdirSync(packagesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => join(packagesDir, e.name));\n } catch {\n return [];\n }\n\n for (const domainDir of domainDirs) {\n let subDirs: string[];\n try {\n subDirs = readdirSync(domainDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => join(domainDir, e.name));\n } catch {\n continue;\n }\n\n for (const subDir of subDirs) {\n const name = readPackageName(subDir);\n if (!name || !name.startsWith(`${PLUGIN_SCOPE}/`) || !isPluginName(name))\n continue;\n\n const distEntry = join(subDir, \"dist\", \"index.mjs\");\n if (!existsSync(distEntry)) {\n continue;\n }\n\n results.push({\n name,\n importSpecifier: pathToFileURL(distEntry).href,\n });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Exported API\n// ---------------------------------------------------------------------------\n\n/** Resolved once — the CLI core package root (`packages/cli/core/`). */\nexport const corePackageDir = dirname(\n dirname(dirname(fileURLToPath(import.meta.url))),\n);\n\n/**\n * Discover installed plugin packages. Returns a de-duplicated, sorted list\n * of `{ name, importSpecifier }` objects.\n *\n * @param basePath - Directory to scan for `node_modules/@fluid-app/`\n * @param searchFrom - Starting directory for workspace root detection\n * (walked upward via `findWorkspaceRoot`). Pass `null` to skip workspace\n * scanning (useful in tests). Defaults to `corePackageDir`.\n */\nexport function discoverPlugins(\n basePath: string,\n searchFrom?: string | null,\n): DiscoveredPlugin[] {\n const seen = new Set<string>();\n const results: DiscoveredPlugin[] = [];\n\n // Strategy 1: node_modules (package name is the import specifier)\n for (const name of discoverFromNodeModules(basePath)) {\n if (!seen.has(name)) {\n seen.add(name);\n results.push({ name, importSpecifier: name });\n }\n }\n\n // Strategy 2: workspace siblings\n if (searchFrom !== null) {\n const wsDir = searchFrom ?? corePackageDir;\n for (const wp of discoverFromWorkspace(wsDir)) {\n if (!seen.has(wp.name)) {\n seen.add(wp.name);\n results.push({ name: wp.name, importSpecifier: wp.importSpecifier });\n }\n }\n }\n\n return results.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Dynamically import a plugin module and return its default export.\n *\n * @internal Not intended for external callers. Only called with specifiers\n * produced by {@link discoverPlugins}:\n * - bare package names (e.g. `@fluid-app/fluid-cli-portal`) from the\n * node_modules scan, validated by {@link isPluginName}.\n * - `file://` URLs from {@link discoverFromWorkspace}, which constructs\n * them internally from validated workspace paths (`packages/` tree,\n * `dist/index.mjs`). These are trusted internal specifiers.\n */\nexport async function importPlugin(specifier: string): Promise<unknown> {\n // file:// URLs are trusted — they originate from discoverFromWorkspace\n // which builds them from validated workspace paths under packages/.\n // For bare package names, verify they match the plugin naming convention.\n if (!specifier.startsWith(\"file://\") && !isPluginName(specifier)) {\n throw new Error(`Refusing to import non-plugin package: ${specifier}`);\n }\n const mod = (await import(specifier)) as Record<string, unknown>;\n return mod[\"default\"] ?? mod;\n}\n","/**\n * Plugin loader — orchestrates discovery and registration\n */\n\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { getConfigDir } from \"../config/paths.js\";\nimport { getAuthToken } from \"../auth/token.js\";\nimport { readConfig } from \"../config/config.js\";\nimport type { FluidPlugin, PluginContext } from \"./types.js\";\nimport { discoverPlugins, importPlugin } from \"./discovery.js\";\n\nfunction isFluidPlugin(value: unknown): value is FluidPlugin {\n if (typeof value !== \"object\" || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (\n typeof obj[\"name\"] === \"string\" &&\n typeof obj[\"version\"] === \"string\" &&\n typeof obj[\"register\"] === \"function\"\n );\n}\n\n/**\n * Discover, import, and register all installed plugins\n */\nexport async function loadPlugins(\n program: Command,\n basePath: string,\n): Promise<void> {\n const discovered = discoverPlugins(basePath);\n const { enabledPlugins } = readConfig();\n\n // If enabledPlugins is set, only load explicitly allowed plugins.\n // This acts as an allow-list to prevent accidental supply-chain execution.\n const allowedSet = enabledPlugins !== null ? new Set(enabledPlugins) : null;\n const plugins = allowedSet\n ? discovered.filter((p) => allowedSet.has(p.name))\n : discovered;\n\n if (plugins.length === 0) return;\n\n const ctx: PluginContext = {\n program,\n getAuthToken,\n configDir: getConfigDir(),\n };\n\n for (const { name, importSpecifier } of plugins) {\n try {\n const exported = await importPlugin(importSpecifier);\n\n if (!isFluidPlugin(exported)) {\n console.warn(\n chalk.yellow(`Warning: ${name} does not export a valid FluidPlugin`),\n );\n continue;\n }\n\n await exported.register(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(`Warning: Failed to load plugin ${name}: ${message}`),\n );\n }\n }\n}\n","#!/usr/bin/env node\n\nimport { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { loginCommand } from \"../commands/login.js\";\nimport { logoutCommand } from \"../commands/logout.js\";\nimport { whoamiCommand } from \"../commands/whoami.js\";\nimport { switchCommand } from \"../commands/switch.js\";\nimport { loadPlugins } from \"../plugins/loader.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\nconst packageRoot = process.cwd();\n\nconst program = new Command();\n\nprogram.name(\"fluid\").description(\"Fluid Commerce CLI\").version(version);\n\n// Built-in auth commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(switchCommand);\n\n// Discover and load all plugins (auto-discovered from node_modules and workspace)\nawait loadPlugins(program, packageRoot);\n\nprogram.parse();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/fluid-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Core CLI for Fluid Commerce — auth, config, and plugin system",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fluid": "./dist/bin/fluid.mjs"
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"tsdown": "^0.21.0",
|
|
33
33
|
"typescript": "^5",
|
|
34
34
|
"vitest": "^4.0.18",
|
|
35
|
-
"@fluid-app/
|
|
36
|
-
"@fluid-app/
|
|
35
|
+
"@fluid-app/typescript-config": "0.0.0",
|
|
36
|
+
"@fluid-app/api-client-core": "0.1.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=18.0.0"
|