@fluid-app/fluid-cli 0.1.7 → 0.1.9

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.
@@ -5,7 +5,7 @@ import { Command } from "commander";
5
5
  import chalk from "chalk";
6
6
  import prompts from "prompts";
7
7
  import ora from "ora";
8
- import { existsSync, readFileSync, readdirSync } from "node:fs";
8
+ import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
9
9
  import { dirname, join } from "node:path";
10
10
  import { fileURLToPath, pathToFileURL } from "node:url";
11
11
  //#region src/commands/login.ts
@@ -404,7 +404,21 @@ function isPluginName(packageName) {
404
404
  function discoverFromNodeModules(basePath) {
405
405
  const scopeDir = join(basePath, "node_modules", PLUGIN_SCOPE);
406
406
  if (!existsSync(scopeDir)) return [];
407
- return readdirSync(scopeDir, { withFileTypes: true }).filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isPluginName(`${PLUGIN_SCOPE}/${entry.name}`)).map((entry) => `${PLUGIN_SCOPE}/${entry.name}`);
407
+ return readdirSync(scopeDir, { withFileTypes: true }).filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isPluginName(`${PLUGIN_SCOPE}/${entry.name}`)).map((entry) => {
408
+ const name = `${PLUGIN_SCOPE}/${entry.name}`;
409
+ const entryDir = join(scopeDir, entry.name);
410
+ try {
411
+ const distEntry = join(realpathSync(entryDir), "dist", "index.mjs");
412
+ if (existsSync(distEntry)) return {
413
+ name,
414
+ importSpecifier: pathToFileURL(distEntry).href
415
+ };
416
+ } catch {}
417
+ return {
418
+ name,
419
+ importSpecifier: name
420
+ };
421
+ });
408
422
  }
409
423
  function findWorkspaceRoot(startDir) {
410
424
  let dir = startDir;
@@ -470,21 +484,15 @@ const corePackageDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))))
470
484
  function discoverPlugins(basePath, searchFrom) {
471
485
  const seen = /* @__PURE__ */ new Set();
472
486
  const results = [];
473
- for (const name of discoverFromNodeModules(basePath)) if (!seen.has(name)) {
474
- seen.add(name);
475
- results.push({
476
- name,
477
- importSpecifier: name
478
- });
487
+ for (const plugin of discoverFromNodeModules(basePath)) if (!seen.has(plugin.name)) {
488
+ seen.add(plugin.name);
489
+ results.push(plugin);
479
490
  }
480
491
  const cliParent = dirname(dirname(dirname(corePackageDir)));
481
492
  if (cliParent !== basePath) {
482
- for (const name of discoverFromNodeModules(cliParent)) if (!seen.has(name)) {
483
- seen.add(name);
484
- results.push({
485
- name,
486
- importSpecifier: name
487
- });
493
+ for (const plugin of discoverFromNodeModules(cliParent)) if (!seen.has(plugin.name)) {
494
+ seen.add(plugin.name);
495
+ results.push(plugin);
488
496
  }
489
497
  }
490
498
  if (searchFrom !== null) {
@@ -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 companyChoices = companies.map((c, i) => ({\n title: `${c.name} (${c.shopName})`,\n value: i,\n }));\n\n const selectResponse = await prompts({\n type: \"autocomplete\",\n name: \"companyIndex\",\n message: \"Select a company (type to search)\",\n choices: companyChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n const idx = selectResponse[\"companyIndex\"] as number | undefined;\n\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 companies (fetched from API, with local fallback)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport {\n fetchUserCompanies,\n switchCompany,\n FLUID_API_ERROR,\n type UserCompany,\n} from \"../auth/fluid-api.js\";\nimport { getActiveProfile } from \"../auth/token.js\";\nimport { readConfig, updateConfig } from \"../config/config.js\";\n\nasync function localSwitch(profileArg?: string): Promise<void> {\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 profileChoices = profileNames.map((name) => ({\n title: name === config.activeProfile ? `${name} (active)` : name,\n value: name,\n }));\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"profile\",\n message: \"Select a profile (type to search)\",\n choices: profileChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.value.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n targetProfile = response[\"profile\"] as string | undefined;\n\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 updateConfig((c) => ({ ...c, activeProfile: targetProfile! }));\n console.log(chalk.green(`Switched to profile ${chalk.bold(targetProfile)}.`));\n}\n\nasync function selectCompany(\n companies: UserCompany[],\n activeCompanyName: string | undefined,\n): Promise<UserCompany | undefined> {\n const companyChoices = companies.map((c) => ({\n title:\n c.name === activeCompanyName\n ? `${c.name} ${chalk.dim(\"(active)\")}`\n : c.name,\n value: c.id,\n }));\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"companyId\",\n message: \"Select a company (type to search)\",\n choices: companyChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n const companyId = response[\"companyId\"] as number | undefined;\n if (companyId === undefined) return undefined;\n return companies.find((c) => c.id === companyId);\n}\n\nasync function performSwitch(\n token: string,\n company: UserCompany,\n): Promise<void> {\n const spinner = ora(`Switching to ${chalk.bold(company.name)}...`).start();\n const result = await switchCompany(token, company.id);\n\n if (!result.success) {\n if (result.error.code === FLUID_API_ERROR.INVALID_TOKEN.code) {\n spinner.fail(\n chalk.red(\n \"Your session has expired. Please run `fluid login` to re-authenticate.\",\n ),\n );\n } else {\n spinner.fail(chalk.red(result.error.message));\n if (result.error.details) {\n console.log(chalk.dim(result.error.details));\n }\n }\n process.exitCode = 1;\n return;\n }\n\n const { companyName, jwt } = result.value;\n\n updateConfig((config) => ({\n ...config,\n activeProfile: companyName,\n profiles: {\n ...config.profiles,\n [companyName]: {\n name: companyName,\n token: jwt,\n companyName,\n storedAt: new Date().toISOString(),\n },\n },\n }));\n\n spinner.succeed(\n chalk.green(\n `Switched to ${chalk.bold(companyName)} (profile: ${chalk.bold(companyName)})`,\n ),\n );\n}\n\nexport const switchCommand = new Command(\"switch\")\n .description(\"Switch between companies\")\n .argument(\"[profile]\", \"Company or profile name to switch to\")\n .action(async (profileArg?: string) => {\n const activeProfile = getActiveProfile();\n\n if (!activeProfile) {\n console.log(chalk.yellow(\"Not logged in. Run `fluid login` first.\"));\n process.exitCode = 1;\n return;\n }\n\n const token = activeProfile.token;\n const activeCompanyName = activeProfile.companyName;\n\n // Fetch companies from API\n const spinner = ora(\"Fetching companies...\").start();\n const result = await fetchUserCompanies(token);\n\n if (!result.success) {\n if (result.error.code === FLUID_API_ERROR.INVALID_TOKEN.code) {\n spinner.fail(\n chalk.red(\n \"Your session has expired. Please run `fluid login` to re-authenticate.\",\n ),\n );\n process.exitCode = 1;\n return;\n }\n spinner.warn(\n chalk.yellow(\n `Could not fetch companies from API: ${result.error.message}`,\n ),\n );\n console.log(chalk.dim(\"Falling back to locally stored profiles.\"));\n await localSwitch(profileArg);\n return;\n }\n\n const companies = result.value;\n spinner.succeed(\n `Found ${companies.length} company${companies.length === 1 ? \"\" : \"ies\"}`,\n );\n\n if (companies.length === 0) {\n console.log(chalk.yellow(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n\n // If a profile argument was passed, match against API companies first\n if (profileArg) {\n const match = companies.find(\n (c) => c.name.toLowerCase() === profileArg.toLowerCase(),\n );\n\n if (match) {\n await performSwitch(token, match);\n return;\n }\n\n // Fall back to local profile matching\n const config = readConfig();\n if (config.profiles[profileArg]) {\n updateConfig((c) => ({ ...c, activeProfile: profileArg! }));\n console.log(\n chalk.green(`Switched to profile ${chalk.bold(profileArg)}.`),\n );\n return;\n }\n\n console.log(chalk.red(`Company or profile \"${profileArg}\" not found.`));\n process.exitCode = 1;\n return;\n }\n\n // Interactive selection\n const selected = await selectCompany(companies, activeCompanyName);\n if (!selected) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n await performSwitch(token, selected);\n });\n","/**\n * Auto-discover @fluid-app CLI plugins.\n *\n * Three discovery strategies run in order:\n *\n * 1a. **node_modules scan (cwd)** — look in `<cwd>/node_modules/@fluid-app/`\n * for directories whose names match the plugin naming convention.\n * This is the primary mechanism for project-local plugin installs.\n *\n * 1b. **node_modules scan (CLI install location)** — look in the\n * `node_modules/@fluid-app/` directory containing the CLI core package\n * itself. This covers global installs where plugins are sibling packages\n * under the same global `node_modules/`.\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 1a: node_modules relative to cwd (project-local plugins)\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 1b: node_modules relative to the CLI's own install location\n // (for global installs where plugins are siblings under the same\n // node_modules/@fluid-app/ directory)\n const cliParent = dirname(dirname(dirname(corePackageDir)));\n if (cliParent !== basePath) {\n for (const name of discoverFromNodeModules(cliParent)) {\n if (!seen.has(name)) {\n seen.add(name);\n results.push({ name, importSpecifier: name });\n }\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 companyChoices = companies.map((c, i) => ({\n title: `${c.name} (${c.shopName})`,\n value: i,\n }));\n\n const selectResponse = await prompts({\n type: \"autocomplete\",\n name: \"companyIndex\",\n message: \"Select a company (type to search)\",\n choices: companyChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n const idx = selectResponse[\"companyIndex\"] as number | undefined;\n\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 companies (fetched from API, with local fallback)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport {\n fetchUserCompanies,\n switchCompany,\n FLUID_API_ERROR,\n type UserCompany,\n} from \"../auth/fluid-api.js\";\nimport { getActiveProfile } from \"../auth/token.js\";\nimport { readConfig, updateConfig } from \"../config/config.js\";\n\nasync function localSwitch(profileArg?: string): Promise<void> {\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 profileChoices = profileNames.map((name) => ({\n title: name === config.activeProfile ? `${name} (active)` : name,\n value: name,\n }));\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"profile\",\n message: \"Select a profile (type to search)\",\n choices: profileChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.value.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n targetProfile = response[\"profile\"] as string | undefined;\n\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 updateConfig((c) => ({ ...c, activeProfile: targetProfile! }));\n console.log(chalk.green(`Switched to profile ${chalk.bold(targetProfile)}.`));\n}\n\nasync function selectCompany(\n companies: UserCompany[],\n activeCompanyName: string | undefined,\n): Promise<UserCompany | undefined> {\n const companyChoices = companies.map((c) => ({\n title:\n c.name === activeCompanyName\n ? `${c.name} ${chalk.dim(\"(active)\")}`\n : c.name,\n value: c.id,\n }));\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"companyId\",\n message: \"Select a company (type to search)\",\n choices: companyChoices,\n suggest: (input, choices) =>\n Promise.resolve(\n input\n ? choices.filter((c) =>\n c.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n const companyId = response[\"companyId\"] as number | undefined;\n if (companyId === undefined) return undefined;\n return companies.find((c) => c.id === companyId);\n}\n\nasync function performSwitch(\n token: string,\n company: UserCompany,\n): Promise<void> {\n const spinner = ora(`Switching to ${chalk.bold(company.name)}...`).start();\n const result = await switchCompany(token, company.id);\n\n if (!result.success) {\n if (result.error.code === FLUID_API_ERROR.INVALID_TOKEN.code) {\n spinner.fail(\n chalk.red(\n \"Your session has expired. Please run `fluid login` to re-authenticate.\",\n ),\n );\n } else {\n spinner.fail(chalk.red(result.error.message));\n if (result.error.details) {\n console.log(chalk.dim(result.error.details));\n }\n }\n process.exitCode = 1;\n return;\n }\n\n const { companyName, jwt } = result.value;\n\n updateConfig((config) => ({\n ...config,\n activeProfile: companyName,\n profiles: {\n ...config.profiles,\n [companyName]: {\n name: companyName,\n token: jwt,\n companyName,\n storedAt: new Date().toISOString(),\n },\n },\n }));\n\n spinner.succeed(\n chalk.green(\n `Switched to ${chalk.bold(companyName)} (profile: ${chalk.bold(companyName)})`,\n ),\n );\n}\n\nexport const switchCommand = new Command(\"switch\")\n .description(\"Switch between companies\")\n .argument(\"[profile]\", \"Company or profile name to switch to\")\n .action(async (profileArg?: string) => {\n const activeProfile = getActiveProfile();\n\n if (!activeProfile) {\n console.log(chalk.yellow(\"Not logged in. Run `fluid login` first.\"));\n process.exitCode = 1;\n return;\n }\n\n const token = activeProfile.token;\n const activeCompanyName = activeProfile.companyName;\n\n // Fetch companies from API\n const spinner = ora(\"Fetching companies...\").start();\n const result = await fetchUserCompanies(token);\n\n if (!result.success) {\n if (result.error.code === FLUID_API_ERROR.INVALID_TOKEN.code) {\n spinner.fail(\n chalk.red(\n \"Your session has expired. Please run `fluid login` to re-authenticate.\",\n ),\n );\n process.exitCode = 1;\n return;\n }\n spinner.warn(\n chalk.yellow(\n `Could not fetch companies from API: ${result.error.message}`,\n ),\n );\n console.log(chalk.dim(\"Falling back to locally stored profiles.\"));\n await localSwitch(profileArg);\n return;\n }\n\n const companies = result.value;\n spinner.succeed(\n `Found ${companies.length} company${companies.length === 1 ? \"\" : \"ies\"}`,\n );\n\n if (companies.length === 0) {\n console.log(chalk.yellow(\"No companies found for this account.\"));\n process.exitCode = 1;\n return;\n }\n\n // If a profile argument was passed, match against API companies first\n if (profileArg) {\n const match = companies.find(\n (c) => c.name.toLowerCase() === profileArg.toLowerCase(),\n );\n\n if (match) {\n await performSwitch(token, match);\n return;\n }\n\n // Fall back to local profile matching\n const config = readConfig();\n if (config.profiles[profileArg]) {\n updateConfig((c) => ({ ...c, activeProfile: profileArg! }));\n console.log(\n chalk.green(`Switched to profile ${chalk.bold(profileArg)}.`),\n );\n return;\n }\n\n console.log(chalk.red(`Company or profile \"${profileArg}\" not found.`));\n process.exitCode = 1;\n return;\n }\n\n // Interactive selection\n const selected = await selectCompany(companies, activeCompanyName);\n if (!selected) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n\n await performSwitch(token, selected);\n });\n","/**\n * Auto-discover @fluid-app CLI plugins.\n *\n * Three discovery strategies run in order:\n *\n * 1a. **node_modules scan (cwd)** — look in `<cwd>/node_modules/@fluid-app/`\n * for directories whose names match the plugin naming convention.\n * This is the primary mechanism for project-local plugin installs.\n *\n * 1b. **node_modules scan (CLI install location)** — look in the\n * `node_modules/@fluid-app/` directory containing the CLI core package\n * itself. This covers global installs where plugins are sibling packages\n * under the same global `node_modules/`.\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, realpathSync } 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): DiscoveredPlugin[] {\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) => {\n const name = `${PLUGIN_SCOPE}/${entry.name}`;\n const entryDir = join(scopeDir, entry.name);\n // Resolve symlinks (pnpm workspace links) to file:// URLs so the\n // dynamic import works regardless of pnpm's strict module resolution.\n try {\n const realDir = realpathSync(entryDir);\n const distEntry = join(realDir, \"dist\", \"index.mjs\");\n if (existsSync(distEntry)) {\n return { name, importSpecifier: pathToFileURL(distEntry).href };\n }\n } catch {\n // Broken symlink or other fs error — fall through to bare specifier\n }\n // Fallback to bare specifier for non-workspace (published) installs\n return { name, importSpecifier: name };\n });\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 1a: node_modules relative to cwd (project-local plugins)\n for (const plugin of discoverFromNodeModules(basePath)) {\n if (!seen.has(plugin.name)) {\n seen.add(plugin.name);\n results.push(plugin);\n }\n }\n\n // Strategy 1b: node_modules relative to the CLI's own install location\n // (for global installs where plugins are siblings under the same\n // node_modules/@fluid-app/ directory)\n const cliParent = dirname(dirname(dirname(corePackageDir)));\n if (cliParent !== basePath) {\n for (const plugin of discoverFromNodeModules(cliParent)) {\n if (!seen.has(plugin.name)) {\n seen.add(plugin.name);\n results.push(plugin);\n }\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 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/types.ts","../src/config/paths.ts","../src/config/config.ts","../src/config/project-config.ts","../src/utils/errors.ts","../src/utils/result.ts","../src/auth/fluid-api.ts","../src/auth/token.ts","../src/plugins/types.ts","../../../platform/api-client-core/src/fetch-client.ts","../src/domain/types.ts","../src/domain/command.ts","../src/domain/context.ts","../src/domain/output.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/types.ts","../src/config/paths.ts","../src/config/config.ts","../src/config/project-config.ts","../src/utils/errors.ts","../src/utils/result.ts","../src/auth/fluid-api.ts","../src/auth/token.ts","../src/plugins/types.ts","../../../platform/api-client-core/src/fetch-client.ts","../src/domain/types.ts","../src/domain/command.ts","../src/domain/context.ts","../src/domain/output.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -88,7 +88,8 @@ function createFetchClient(config) {
88
88
  } catch {
89
89
  throw new ApiError(errorText.slice(0, 200) || `${method} request failed with status ${response.status}`, response.status, null);
90
90
  }
91
- throw new ApiError(data.message || data.error_message || `${method} request failed`, response.status, data.errors || data);
91
+ const nestedError = typeof data.error === "object" && data.error !== null ? data.error.message : void 0;
92
+ throw new ApiError(data.message || data.error_message || (typeof nestedError === "string" ? nestedError : void 0) || `${method} request failed`, response.status, data.errors || data);
92
93
  } else throw new ApiError(`${method} request failed with status ${response.status}`, response.status, null);
93
94
  }
94
95
  if (response.status === 204 || response.headers.get("content-length") === "0") return null;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../platform/api-client-core/src/fetch-client.ts","../src/domain/output.ts","../src/domain/context.ts","../src/domain/command.ts"],"sourcesContent":["/**\n * Minimal, framework-agnostic fetch client for Fluid APIs\n * Compatible with fluid-admin patterns but usable standalone\n */\n\nexport interface FetchClientConfig {\n /**\n * Base URL for all requests (e.g., \"https://api.fluid.app/api\")\n */\n baseUrl: string;\n\n /**\n * Optional function to get auth token\n * Return null/undefined if no token available\n */\n getAuthToken?: () => string | null | Promise<string | null>;\n\n /**\n * Optional callback when 401 auth error occurs\n */\n onAuthError?: () => void;\n\n /**\n * Default headers to include in all requests\n * Example: { \"x-fluid-client\": \"admin\" }\n */\n defaultHeaders?: Record<string, string>;\n\n /**\n * Credentials mode for fetch requests.\n * Set to `\"include\"` for cookie-based (same-origin BFF) authentication.\n * @default undefined (browser default: \"same-origin\")\n */\n credentials?: RequestCredentials;\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n headers?: Record<string, string>;\n params?: Record<string, unknown>;\n body?: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * API Error class compatible with fluid-admin's ApiError\n */\nexport class ApiError extends Error {\n public readonly status: number;\n public readonly data: unknown;\n\n constructor(message: string, status: number, data?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.data = data;\n\n if (\"captureStackTrace\" in Error) {\n (\n Error as {\n captureStackTrace: (\n target: Error,\n constructor: NewableFunction,\n ) => void;\n }\n ).captureStackTrace(this, ApiError);\n }\n }\n\n toJSON(): { name: string; message: string; status: number; data: unknown } {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n data: this.data,\n };\n }\n}\n\n/**\n * Type guard for ApiError\n */\nexport function isApiError(error: unknown): error is ApiError {\n return error instanceof ApiError;\n}\n\nexport interface FetchClientInstance {\n request: <TResponse = unknown>(\n endpoint: string,\n options?: RequestOptions,\n ) => Promise<TResponse>;\n requestWithFormData: <TResponse = unknown>(\n endpoint: string,\n formData: FormData,\n options?: Omit<RequestOptions, \"body\" | \"params\"> & {\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n },\n ) => Promise<TResponse>;\n get: <TResponse = unknown>(\n endpoint: string,\n params?: Record<string, unknown>,\n options?: Omit<RequestOptions, \"method\" | \"params\">,\n ) => Promise<TResponse>;\n post: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n put: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n patch: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n delete: <TResponse = unknown>(\n endpoint: string,\n options?: Omit<RequestOptions, \"method\">,\n ) => Promise<TResponse>;\n}\n\n/**\n * Creates a configured fetch client instance\n */\nexport function createFetchClient(\n config: FetchClientConfig,\n): FetchClientInstance {\n const {\n baseUrl,\n getAuthToken,\n onAuthError,\n defaultHeaders = {},\n credentials,\n } = config;\n\n /**\n * Build headers for a request\n */\n async function buildHeaders(\n customHeaders?: Record<string, string>,\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...customHeaders,\n };\n\n // Add auth token if available\n if (getAuthToken) {\n const token = await getAuthToken();\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n /**\n * Join baseUrl + endpoint via string concatenation (matches fetchApi).\n * Using `new URL(endpoint, baseUrl)` would strip any path prefix from\n * baseUrl (e.g. \"/api\") when the endpoint starts with \"/\".\n */\n function joinUrl(endpoint: string): string {\n return `${baseUrl}${endpoint}`;\n }\n\n /**\n * Build URL with query parameters for GET requests\n * Compatible with fluid-admin's query param handling\n */\n function buildUrl(\n endpoint: string,\n params?: Record<string, unknown>,\n ): string {\n const fullUrl = joinUrl(endpoint);\n\n if (!params || Object.keys(params).length === 0) {\n return fullUrl;\n }\n\n const queryString = new URLSearchParams();\n\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return; // Skip undefined/null values\n }\n\n if (Array.isArray(value)) {\n // Handle arrays like Rails expects: key[]\n value.forEach((item) => queryString.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n // Handle nested objects: key[subkey]\n Object.entries(value).forEach(([subKey, subValue]) => {\n if (subValue === undefined || subValue === null) {\n return;\n }\n\n if (Array.isArray(subValue)) {\n subValue.forEach((item) =>\n queryString.append(`${key}[${subKey}][]`, String(item)),\n );\n } else {\n queryString.append(`${key}[${subKey}]`, String(subValue));\n }\n });\n } else {\n queryString.append(key, String(value));\n }\n });\n\n const qs = queryString.toString();\n return qs ? `${fullUrl}?${qs}` : fullUrl;\n }\n\n /**\n * Shared response handler for both JSON and FormData requests.\n * Handles auth errors, non-OK responses, 204 No Content, and JSON parsing.\n */\n async function handleResponse<TResponse>(\n response: Response,\n method: string,\n _url: string,\n ): Promise<TResponse> {\n if (response.status === 401 && onAuthError) {\n onAuthError();\n }\n\n if (!response.ok) {\n // Read body as text first to avoid SyntaxError from response.json()\n // when server returns non-JSON bodies with application/json content-type.\n const errorText = await response.text().catch(() => \"\");\n const contentType = response.headers.get(\"content-type\");\n\n if (contentType?.includes(\"application/json\")) {\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(errorText);\n } catch {\n throw new ApiError(\n errorText.slice(0, 200) ||\n `${method} request failed with status ${response.status}`,\n response.status,\n null,\n );\n }\n const msg = (data.message || data.error_message) as string | undefined;\n throw new ApiError(\n msg || `${method} request failed`,\n response.status,\n data.errors || data,\n );\n } else {\n throw new ApiError(\n `${method} request failed with status ${response.status}`,\n response.status,\n null,\n );\n }\n }\n\n if (\n response.status === 204 ||\n response.headers.get(\"content-length\") === \"0\"\n ) {\n return null as TResponse;\n }\n\n const contentType = response.headers.get(\"content-type\");\n\n if (contentType?.includes(\"application/json\")) {\n try {\n const data = await response.json();\n return data as TResponse;\n } catch {\n try {\n // API declared JSON content-type but body isn't valid JSON\n const text = await response.text();\n return text as TResponse;\n } catch {\n return null as TResponse;\n }\n }\n }\n\n // Non-JSON response (text/plain, text/html, etc.)\n return null as TResponse;\n }\n\n /**\n * Main request function\n */\n async function request<TResponse = unknown>(\n endpoint: string,\n options: RequestOptions = {},\n ): Promise<TResponse> {\n const {\n method = \"GET\",\n headers: customHeaders,\n params,\n body,\n signal,\n } = options;\n\n const url = params ? buildUrl(endpoint, params) : joinUrl(endpoint);\n\n const headers = await buildHeaders(customHeaders);\n\n let response: Response;\n\n try {\n const fetchOptions: RequestInit = { method, headers };\n if (credentials) fetchOptions.credentials = credentials;\n const serializedBody =\n body && method !== \"GET\" ? JSON.stringify(body) : null;\n if (serializedBody) fetchOptions.body = serializedBody;\n if (signal) fetchOptions.signal = signal;\n response = await fetch(url, fetchOptions);\n } catch (networkError) {\n throw new ApiError(\n `Network error: ${networkError instanceof Error ? networkError.message : \"Unknown network error\"}`,\n 0,\n null,\n );\n }\n\n return handleResponse<TResponse>(response, method, url);\n }\n\n /**\n * Request with FormData (for file uploads)\n */\n async function requestWithFormData<TResponse = unknown>(\n endpoint: string,\n formData: FormData,\n options: Omit<RequestOptions, \"body\" | \"params\"> & {\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n } = {},\n ): Promise<TResponse> {\n const { method = \"POST\", headers: customHeaders, signal } = options;\n\n const url = joinUrl(endpoint);\n const headers = await buildHeaders(customHeaders);\n\n // Remove Content-Type to let browser set it with boundary\n delete headers[\"Content-Type\"];\n\n let response: Response;\n\n try {\n const fetchOptions: RequestInit = { method, headers, body: formData };\n if (credentials) fetchOptions.credentials = credentials;\n if (signal) fetchOptions.signal = signal;\n response = await fetch(url, fetchOptions);\n } catch (networkError) {\n throw new ApiError(\n `Network error: ${networkError instanceof Error ? networkError.message : \"Unknown network error\"}`,\n 0,\n null,\n );\n }\n\n return handleResponse<TResponse>(response, method, url);\n }\n\n // Return client with convenience methods\n return {\n request: request,\n requestWithFormData: requestWithFormData,\n\n // Convenience methods for common HTTP verbs\n get: <TResponse = unknown>(\n endpoint: string,\n params?: Record<string, unknown>,\n options?: Omit<RequestOptions, \"method\" | \"params\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"GET\" as const,\n ...(params && { params }),\n }),\n\n post: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"POST\",\n body,\n }),\n\n put: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"PUT\",\n body,\n }),\n\n patch: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"PATCH\",\n body,\n }),\n\n delete: <TResponse = unknown>(\n endpoint: string,\n options?: Omit<RequestOptions, \"method\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"DELETE\",\n }),\n };\n}\n\nexport type FetchClient = FetchClientInstance;\n","import type { OutputOptions } from \"./types.js\";\n\nexport function formatOutput(data: unknown, options: OutputOptions): string {\n let result = data;\n\n if (options.jq) {\n result = extractPath(result, options.jq);\n }\n\n if (options.format === \"table\") {\n return formatTable(result);\n }\n\n if (options.compact) {\n return JSON.stringify(result);\n }\n return JSON.stringify(result, null, 2);\n}\n\nfunction extractPath(data: unknown, path: string): unknown {\n const parts = path\n .replace(/^\\.*/, \"\")\n .split(/\\.|\\[/)\n .filter(Boolean)\n .map((p) => p.replace(/\\]$/, \"\"));\n\n let current: any = data;\n for (const part of parts) {\n if (current == null) return undefined;\n current = current[part];\n }\n return current;\n}\n\nfunction formatTable(data: unknown): string {\n if (Array.isArray(data)) {\n if (data.length === 0) return \"(empty)\";\n\n if (typeof data[0] === \"object\" && data[0] !== null) {\n const keys = Object.keys(data[0]);\n const widths = keys.map((k) =>\n Math.max(k.length, ...data.map((row) => String(row[k] ?? \"\").length)),\n );\n\n const header = keys\n .map((k, i) => k.toUpperCase().padEnd(widths[i]!))\n .join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const rows = data.map((row) =>\n keys.map((k, i) => String(row[k] ?? \"\").padEnd(widths[i]!)).join(\" \"),\n );\n\n return [header, separator, ...rows].join(\"\\n\");\n }\n\n return data.map(String).join(\"\\n\");\n }\n\n if (typeof data === \"object\" && data !== null) {\n const entries = Object.entries(data);\n const maxKeyLen = Math.max(...entries.map(([k]) => k.length));\n return entries\n .map(([k, v]) => `${k.padEnd(maxKeyLen)} ${JSON.stringify(v)}`)\n .join(\"\\n\");\n }\n\n return String(data);\n}\n","import { createFetchClient } from \"@fluid-app/api-client-core\";\nimport type { FetchClient } from \"@fluid-app/api-client-core\";\n\nimport { getAuthToken } from \"../auth/token.js\";\nimport { findProjectConfig } from \"../config/project-config.js\";\nimport { formatOutput } from \"./output.js\";\nimport type { CommandContext, OutputOptions } from \"./types.js\";\n\nexport function createCommandContext(opts: {\n token?: string;\n baseUrl?: string;\n profile?: string;\n format: \"json\" | \"table\";\n compact: boolean;\n jq?: string;\n verbose: boolean;\n}): CommandContext {\n let clientInstance: FetchClient | null = null;\n\n const outputOptions: OutputOptions = {\n format: opts.format,\n compact: opts.compact,\n jq: opts.jq,\n };\n\n return {\n verbose: opts.verbose,\n\n async getClient(): Promise<FetchClient> {\n if (clientInstance) return clientInstance;\n\n const token = resolveToken(opts);\n if (!token) {\n if (opts.profile) {\n throw new Error(\n `No API token found for profile \"${opts.profile}\". Run \\`fluid login\\` for that profile or provide --token <token>`,\n );\n }\n\n throw new Error(\n \"No API token found. Run `fluid login` or provide --token <token>\",\n );\n }\n\n const baseUrl =\n opts.baseUrl ??\n process.env[\"FLUID_API_BASE\"] ??\n \"https://api.fluid.app\";\n\n clientInstance = createFetchClient({\n baseUrl,\n getAuthToken: () => token,\n });\n\n return clientInstance;\n },\n\n output(data: unknown): void {\n const formatted = formatOutput(data, outputOptions);\n process.stdout.write(formatted + \"\\n\");\n },\n\n parseBody(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON body: ${raw}`);\n }\n },\n };\n}\n\nfunction resolveToken(opts: {\n token?: string;\n profile?: string;\n}): string | null {\n if (opts.token) return opts.token;\n\n if (opts.profile) return getAuthToken(opts.profile);\n\n const envToken = process.env[\"FLUID_TOKEN\"] ?? process.env[\"FLUID_API_TOKEN\"];\n if (envToken) return envToken;\n\n // Project-level .fluidrc profile (before global active profile)\n const projectConfig = findProjectConfig(process.cwd());\n if (projectConfig?.profile) {\n const rcToken = getAuthToken(projectConfig.profile);\n if (rcToken) return rcToken;\n }\n\n return getAuthToken();\n}\n","import { Command } from \"commander\";\n\nimport { createCommandContext } from \"./context.js\";\nimport type { CommandContext } from \"./types.js\";\n\ntype RegisterFn = (parent: Command, ctx: CommandContext) => void;\n\nexport function createDomainCommand(\n name: string,\n description: string,\n register: RegisterFn,\n): Command {\n const cmd = new Command(name)\n .description(description)\n // Keep global domain flags usable before or after nested subcommands.\n .enablePositionalOptions(false)\n .option(\"--token <token>\", \"API authentication token\")\n .option(\"--base-url <url>\", \"API base URL\")\n .option(\"--profile <name>\", \"Config profile name\")\n .option(\"--format <format>\", \"Output format (json|table)\", \"json\")\n .option(\"--compact\", \"Compact JSON output\", false)\n .option(\"--jq <path>\", \"Extract value at JSON path\")\n .option(\"--verbose\", \"Print request details to stderr\", false);\n\n let resolvedCtx: CommandContext | null = null;\n\n function getCtx(): CommandContext {\n if (!resolvedCtx) {\n const opts = cmd.opts();\n resolvedCtx = createCommandContext({\n token: opts.token,\n baseUrl: opts.baseUrl,\n profile: opts.profile,\n format: opts.format as \"json\" | \"table\",\n compact: opts.compact,\n jq: opts.jq,\n verbose: opts.verbose,\n });\n }\n return resolvedCtx;\n }\n\n const lazyCtx: CommandContext = {\n get verbose() {\n return getCtx().verbose;\n },\n getClient() {\n return getCtx().getClient();\n },\n output(data: unknown) {\n return getCtx().output(data);\n },\n parseBody(raw: string) {\n return getCtx().parseBody(raw);\n },\n };\n\n register(cmd, lazyCtx);\n\n return cmd;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../platform/api-client-core/src/fetch-client.ts","../src/domain/output.ts","../src/domain/context.ts","../src/domain/command.ts"],"sourcesContent":["/**\n * Minimal, framework-agnostic fetch client for Fluid APIs\n * Compatible with fluid-admin patterns but usable standalone\n */\n\nexport interface FetchClientConfig {\n /**\n * Base URL for all requests (e.g., \"https://api.fluid.app/api\")\n */\n baseUrl: string;\n\n /**\n * Optional function to get auth token\n * Return null/undefined if no token available\n */\n getAuthToken?: () => string | null | Promise<string | null>;\n\n /**\n * Optional callback when 401 auth error occurs\n */\n onAuthError?: () => void;\n\n /**\n * Default headers to include in all requests\n * Example: { \"x-fluid-client\": \"admin\" }\n */\n defaultHeaders?: Record<string, string>;\n\n /**\n * Credentials mode for fetch requests.\n * Set to `\"include\"` for cookie-based (same-origin BFF) authentication.\n * @default undefined (browser default: \"same-origin\")\n */\n credentials?: RequestCredentials;\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n headers?: Record<string, string>;\n params?: Record<string, unknown>;\n body?: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * API Error class compatible with fluid-admin's ApiError\n */\nexport class ApiError extends Error {\n public readonly status: number;\n public readonly data: unknown;\n\n constructor(message: string, status: number, data?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.data = data;\n\n if (\"captureStackTrace\" in Error) {\n (\n Error as {\n captureStackTrace: (\n target: Error,\n constructor: NewableFunction,\n ) => void;\n }\n ).captureStackTrace(this, ApiError);\n }\n }\n\n toJSON(): { name: string; message: string; status: number; data: unknown } {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n data: this.data,\n };\n }\n}\n\n/**\n * Type guard for ApiError\n */\nexport function isApiError(error: unknown): error is ApiError {\n return error instanceof ApiError;\n}\n\nexport interface FetchClientInstance {\n request: <TResponse = unknown>(\n endpoint: string,\n options?: RequestOptions,\n ) => Promise<TResponse>;\n requestWithFormData: <TResponse = unknown>(\n endpoint: string,\n formData: FormData,\n options?: Omit<RequestOptions, \"body\" | \"params\"> & {\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n },\n ) => Promise<TResponse>;\n get: <TResponse = unknown>(\n endpoint: string,\n params?: Record<string, unknown>,\n options?: Omit<RequestOptions, \"method\" | \"params\">,\n ) => Promise<TResponse>;\n post: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n put: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n patch: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ) => Promise<TResponse>;\n delete: <TResponse = unknown>(\n endpoint: string,\n options?: Omit<RequestOptions, \"method\">,\n ) => Promise<TResponse>;\n}\n\n/**\n * Creates a configured fetch client instance\n */\nexport function createFetchClient(\n config: FetchClientConfig,\n): FetchClientInstance {\n const {\n baseUrl,\n getAuthToken,\n onAuthError,\n defaultHeaders = {},\n credentials,\n } = config;\n\n /**\n * Build headers for a request\n */\n async function buildHeaders(\n customHeaders?: Record<string, string>,\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n ...defaultHeaders,\n ...customHeaders,\n };\n\n // Add auth token if available\n if (getAuthToken) {\n const token = await getAuthToken();\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n /**\n * Join baseUrl + endpoint via string concatenation (matches fetchApi).\n * Using `new URL(endpoint, baseUrl)` would strip any path prefix from\n * baseUrl (e.g. \"/api\") when the endpoint starts with \"/\".\n */\n function joinUrl(endpoint: string): string {\n return `${baseUrl}${endpoint}`;\n }\n\n /**\n * Build URL with query parameters for GET requests\n * Compatible with fluid-admin's query param handling\n */\n function buildUrl(\n endpoint: string,\n params?: Record<string, unknown>,\n ): string {\n const fullUrl = joinUrl(endpoint);\n\n if (!params || Object.keys(params).length === 0) {\n return fullUrl;\n }\n\n const queryString = new URLSearchParams();\n\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return; // Skip undefined/null values\n }\n\n if (Array.isArray(value)) {\n // Handle arrays like Rails expects: key[]\n value.forEach((item) => queryString.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n // Handle nested objects: key[subkey]\n Object.entries(value).forEach(([subKey, subValue]) => {\n if (subValue === undefined || subValue === null) {\n return;\n }\n\n if (Array.isArray(subValue)) {\n subValue.forEach((item) =>\n queryString.append(`${key}[${subKey}][]`, String(item)),\n );\n } else {\n queryString.append(`${key}[${subKey}]`, String(subValue));\n }\n });\n } else {\n queryString.append(key, String(value));\n }\n });\n\n const qs = queryString.toString();\n return qs ? `${fullUrl}?${qs}` : fullUrl;\n }\n\n /**\n * Shared response handler for both JSON and FormData requests.\n * Handles auth errors, non-OK responses, 204 No Content, and JSON parsing.\n */\n async function handleResponse<TResponse>(\n response: Response,\n method: string,\n _url: string,\n ): Promise<TResponse> {\n if (response.status === 401 && onAuthError) {\n onAuthError();\n }\n\n if (!response.ok) {\n // Read body as text first to avoid SyntaxError from response.json()\n // when server returns non-JSON bodies with application/json content-type.\n const errorText = await response.text().catch(() => \"\");\n const contentType = response.headers.get(\"content-type\");\n\n if (contentType?.includes(\"application/json\")) {\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(errorText);\n } catch {\n throw new ApiError(\n errorText.slice(0, 200) ||\n `${method} request failed with status ${response.status}`,\n response.status,\n null,\n );\n }\n // Some Rails BFF endpoints return `{ error: { message, details } }`\n // instead of `{ message }` or `{ error_message }`. Fall through to\n // that shape last so callers always surface the real reason.\n const nestedError =\n typeof data.error === \"object\" && data.error !== null\n ? (data.error as { message?: unknown }).message\n : undefined;\n const msg =\n (data.message as string | undefined) ||\n (data.error_message as string | undefined) ||\n (typeof nestedError === \"string\" ? nestedError : undefined);\n throw new ApiError(\n msg || `${method} request failed`,\n response.status,\n data.errors || data,\n );\n } else {\n throw new ApiError(\n `${method} request failed with status ${response.status}`,\n response.status,\n null,\n );\n }\n }\n\n if (\n response.status === 204 ||\n response.headers.get(\"content-length\") === \"0\"\n ) {\n return null as TResponse;\n }\n\n const contentType = response.headers.get(\"content-type\");\n\n if (contentType?.includes(\"application/json\")) {\n try {\n const data = await response.json();\n return data as TResponse;\n } catch {\n try {\n // API declared JSON content-type but body isn't valid JSON\n const text = await response.text();\n return text as TResponse;\n } catch {\n return null as TResponse;\n }\n }\n }\n\n // Non-JSON response (text/plain, text/html, etc.)\n return null as TResponse;\n }\n\n /**\n * Main request function\n */\n async function request<TResponse = unknown>(\n endpoint: string,\n options: RequestOptions = {},\n ): Promise<TResponse> {\n const {\n method = \"GET\",\n headers: customHeaders,\n params,\n body,\n signal,\n } = options;\n\n const url = params ? buildUrl(endpoint, params) : joinUrl(endpoint);\n\n const headers = await buildHeaders(customHeaders);\n\n let response: Response;\n\n try {\n const fetchOptions: RequestInit = { method, headers };\n if (credentials) fetchOptions.credentials = credentials;\n const serializedBody =\n body && method !== \"GET\" ? JSON.stringify(body) : null;\n if (serializedBody) fetchOptions.body = serializedBody;\n if (signal) fetchOptions.signal = signal;\n response = await fetch(url, fetchOptions);\n } catch (networkError) {\n throw new ApiError(\n `Network error: ${networkError instanceof Error ? networkError.message : \"Unknown network error\"}`,\n 0,\n null,\n );\n }\n\n return handleResponse<TResponse>(response, method, url);\n }\n\n /**\n * Request with FormData (for file uploads)\n */\n async function requestWithFormData<TResponse = unknown>(\n endpoint: string,\n formData: FormData,\n options: Omit<RequestOptions, \"body\" | \"params\"> & {\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n } = {},\n ): Promise<TResponse> {\n const { method = \"POST\", headers: customHeaders, signal } = options;\n\n const url = joinUrl(endpoint);\n const headers = await buildHeaders(customHeaders);\n\n // Remove Content-Type to let browser set it with boundary\n delete headers[\"Content-Type\"];\n\n let response: Response;\n\n try {\n const fetchOptions: RequestInit = { method, headers, body: formData };\n if (credentials) fetchOptions.credentials = credentials;\n if (signal) fetchOptions.signal = signal;\n response = await fetch(url, fetchOptions);\n } catch (networkError) {\n throw new ApiError(\n `Network error: ${networkError instanceof Error ? networkError.message : \"Unknown network error\"}`,\n 0,\n null,\n );\n }\n\n return handleResponse<TResponse>(response, method, url);\n }\n\n // Return client with convenience methods\n return {\n request: request,\n requestWithFormData: requestWithFormData,\n\n // Convenience methods for common HTTP verbs\n get: <TResponse = unknown>(\n endpoint: string,\n params?: Record<string, unknown>,\n options?: Omit<RequestOptions, \"method\" | \"params\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"GET\" as const,\n ...(params && { params }),\n }),\n\n post: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"POST\",\n body,\n }),\n\n put: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"PUT\",\n body,\n }),\n\n patch: <TResponse = unknown>(\n endpoint: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"PATCH\",\n body,\n }),\n\n delete: <TResponse = unknown>(\n endpoint: string,\n options?: Omit<RequestOptions, \"method\">,\n ): Promise<TResponse> =>\n request<TResponse>(endpoint, {\n ...options,\n method: \"DELETE\",\n }),\n };\n}\n\nexport type FetchClient = FetchClientInstance;\n","import type { OutputOptions } from \"./types.js\";\n\nexport function formatOutput(data: unknown, options: OutputOptions): string {\n let result = data;\n\n if (options.jq) {\n result = extractPath(result, options.jq);\n }\n\n if (options.format === \"table\") {\n return formatTable(result);\n }\n\n if (options.compact) {\n return JSON.stringify(result);\n }\n return JSON.stringify(result, null, 2);\n}\n\nfunction extractPath(data: unknown, path: string): unknown {\n const parts = path\n .replace(/^\\.*/, \"\")\n .split(/\\.|\\[/)\n .filter(Boolean)\n .map((p) => p.replace(/\\]$/, \"\"));\n\n let current: any = data;\n for (const part of parts) {\n if (current == null) return undefined;\n current = current[part];\n }\n return current;\n}\n\nfunction formatTable(data: unknown): string {\n if (Array.isArray(data)) {\n if (data.length === 0) return \"(empty)\";\n\n if (typeof data[0] === \"object\" && data[0] !== null) {\n const keys = Object.keys(data[0]);\n const widths = keys.map((k) =>\n Math.max(k.length, ...data.map((row) => String(row[k] ?? \"\").length)),\n );\n\n const header = keys\n .map((k, i) => k.toUpperCase().padEnd(widths[i]!))\n .join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const rows = data.map((row) =>\n keys.map((k, i) => String(row[k] ?? \"\").padEnd(widths[i]!)).join(\" \"),\n );\n\n return [header, separator, ...rows].join(\"\\n\");\n }\n\n return data.map(String).join(\"\\n\");\n }\n\n if (typeof data === \"object\" && data !== null) {\n const entries = Object.entries(data);\n const maxKeyLen = Math.max(...entries.map(([k]) => k.length));\n return entries\n .map(([k, v]) => `${k.padEnd(maxKeyLen)} ${JSON.stringify(v)}`)\n .join(\"\\n\");\n }\n\n return String(data);\n}\n","import { createFetchClient } from \"@fluid-app/api-client-core\";\nimport type { FetchClient } from \"@fluid-app/api-client-core\";\n\nimport { getAuthToken } from \"../auth/token.js\";\nimport { findProjectConfig } from \"../config/project-config.js\";\nimport { formatOutput } from \"./output.js\";\nimport type { CommandContext, OutputOptions } from \"./types.js\";\n\nexport function createCommandContext(opts: {\n token?: string;\n baseUrl?: string;\n profile?: string;\n format: \"json\" | \"table\";\n compact: boolean;\n jq?: string;\n verbose: boolean;\n}): CommandContext {\n let clientInstance: FetchClient | null = null;\n\n const outputOptions: OutputOptions = {\n format: opts.format,\n compact: opts.compact,\n jq: opts.jq,\n };\n\n return {\n verbose: opts.verbose,\n\n async getClient(): Promise<FetchClient> {\n if (clientInstance) return clientInstance;\n\n const token = resolveToken(opts);\n if (!token) {\n if (opts.profile) {\n throw new Error(\n `No API token found for profile \"${opts.profile}\". Run \\`fluid login\\` for that profile or provide --token <token>`,\n );\n }\n\n throw new Error(\n \"No API token found. Run `fluid login` or provide --token <token>\",\n );\n }\n\n const baseUrl =\n opts.baseUrl ??\n process.env[\"FLUID_API_BASE\"] ??\n \"https://api.fluid.app\";\n\n clientInstance = createFetchClient({\n baseUrl,\n getAuthToken: () => token,\n });\n\n return clientInstance;\n },\n\n output(data: unknown): void {\n const formatted = formatOutput(data, outputOptions);\n process.stdout.write(formatted + \"\\n\");\n },\n\n parseBody(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON body: ${raw}`);\n }\n },\n };\n}\n\nfunction resolveToken(opts: {\n token?: string;\n profile?: string;\n}): string | null {\n if (opts.token) return opts.token;\n\n if (opts.profile) return getAuthToken(opts.profile);\n\n const envToken = process.env[\"FLUID_TOKEN\"] ?? process.env[\"FLUID_API_TOKEN\"];\n if (envToken) return envToken;\n\n // Project-level .fluidrc profile (before global active profile)\n const projectConfig = findProjectConfig(process.cwd());\n if (projectConfig?.profile) {\n const rcToken = getAuthToken(projectConfig.profile);\n if (rcToken) return rcToken;\n }\n\n return getAuthToken();\n}\n","import { Command } from \"commander\";\n\nimport { createCommandContext } from \"./context.js\";\nimport type { CommandContext } from \"./types.js\";\n\ntype RegisterFn = (parent: Command, ctx: CommandContext) => void;\n\nexport function createDomainCommand(\n name: string,\n description: string,\n register: RegisterFn,\n): Command {\n const cmd = new Command(name)\n .description(description)\n // Keep global domain flags usable before or after nested subcommands.\n .enablePositionalOptions(false)\n .option(\"--token <token>\", \"API authentication token\")\n .option(\"--base-url <url>\", \"API base URL\")\n .option(\"--profile <name>\", \"Config profile name\")\n .option(\"--format <format>\", \"Output format (json|table)\", \"json\")\n .option(\"--compact\", \"Compact JSON output\", false)\n .option(\"--jq <path>\", \"Extract value at JSON path\")\n .option(\"--verbose\", \"Print request details to stderr\", false);\n\n let resolvedCtx: CommandContext | null = null;\n\n function getCtx(): CommandContext {\n if (!resolvedCtx) {\n const opts = cmd.opts();\n resolvedCtx = createCommandContext({\n token: opts.token,\n baseUrl: opts.baseUrl,\n profile: opts.profile,\n format: opts.format as \"json\" | \"table\",\n compact: opts.compact,\n jq: opts.jq,\n verbose: opts.verbose,\n });\n }\n return resolvedCtx;\n }\n\n const lazyCtx: CommandContext = {\n get verbose() {\n return getCtx().verbose;\n },\n getClient() {\n return getCtx().getClient();\n },\n output(data: unknown) {\n return getCtx().output(data);\n },\n parseBody(raw: string) {\n return getCtx().parseBody(raw);\n },\n };\n\n register(cmd, lazyCtx);\n\n return cmd;\n}\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.7",
3
+ "version": "0.1.9",
4
4
  "description": "Core CLI for Fluid Commerce — auth, config, and plugin system",
5
5
  "bin": {
6
6
  "fluid": "./dist/bin/fluid.mjs"