@fluid-app/fluid-cli 0.1.49 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/fluid.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluid.mjs","names":[],"sources":["../../src/commands/commands.ts","../../src/completion/complete.ts","../../src/commands/completion.ts","../../src/auth/profiles.ts","../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/whoami.ts","../../src/commands/switch.ts","../../src/plugins/discovery.ts","../../src/plugins/format-load-line.ts","../../src/plugins/resolve-load-path.ts","../../src/plugins/should-log-plugin-loads.ts","../../src/plugins/loader.ts","../../src/update/notifier.ts","../../src/bin/fluid.ts"],"sourcesContent":["/**\n * fluid commands — list every installed command, or print the catalog as\n * JSON for agents and documentation generators.\n */\n\nimport { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport {\n filterCatalog,\n rootCommand,\n walkCommands,\n type CatalogEntry,\n} from \"../catalog/walk.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\nfunction usage(entry: CatalogEntry): string {\n const args = entry.arguments.map((arg) => {\n const name = arg.variadic ? `${arg.name}...` : arg.name;\n return arg.required ? `<${name}>` : `[${name}]`;\n });\n return [entry.command, ...args].join(\" \");\n}\n\nexport function formatCatalogText(entries: readonly CatalogEntry[]): string {\n const runnable = entries.filter((entry) => entry.runnable);\n const width = Math.min(\n 48,\n Math.max(0, ...runnable.map((entry) => usage(entry).length)),\n );\n return runnable\n .map((entry) => {\n const line = usage(entry);\n const summary = entry.description.split(\"\\n\", 1)[0] ?? \"\";\n return summary ? `${line.padEnd(width)} ${summary}` : line;\n })\n .join(\"\\n\");\n}\n\nexport const commandsCommand = new Command(\"commands\")\n .description(\"List installed commands; --json prints the full catalog\")\n .argument(\"[prefix...]\", \"Only list commands under this one, e.g. `domains`\")\n .option(\"--json\", \"Print the catalog as JSON\", false)\n .action((prefix: string[], opts: { json: boolean }, command: Command) => {\n const root = rootCommand(command);\n\n const entries = filterCatalog(walkCommands(root), root, prefix);\n if (entries.length === 0) {\n process.stderr.write(\n JSON.stringify({ error: `No command named \"${prefix.join(\" \")}\".` }) +\n \"\\n\",\n );\n process.exitCode = 1;\n return;\n }\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n { cli: root.name(), version, commands: entries },\n null,\n 2,\n ) + \"\\n\",\n );\n return;\n }\n process.stdout.write(formatCatalogText(entries) + \"\\n\");\n });\n","/**\n * Shell completion. The scripts `fluid completion <shell>` prints pipe the\n * words typed so far into the hidden `fluid __complete`, so completions come\n * from the plugins actually installed rather than a list frozen at install.\n * The words go over stdin, never argv: a half-typed `--token <value>` must\n * not show up in another user's process list.\n */\n\nimport type { Command, Option } from \"commander\";\nimport { visibleOptions, visibleSubcommands } from \"../catalog/walk.js\";\n\nexport const SHELLS = [\"bash\", \"zsh\", \"fish\"] as const;\nexport type Shell = (typeof SHELLS)[number];\n\nexport interface Candidate {\n readonly value: string;\n readonly description: string;\n}\n\nfunction findOption(chain: readonly Command[], word: string): Option | null {\n const flag = word.split(\"=\", 1)[0] ?? word;\n for (const command of [...chain].reverse()) {\n const match = command.options.find(\n (option) => option.long === flag || option.short === flag,\n );\n if (match) return match;\n }\n return null;\n}\n\nfunction splitAssignment(word: string): [string, string] {\n const at = word.indexOf(\"=\");\n return [word.slice(0, at), word.slice(at + 1)];\n}\n\nfunction choicesFor(option: Option, typed: string): Candidate[] {\n return (option.argChoices ?? [])\n .filter((choice) => choice.startsWith(typed))\n .map((choice) => ({ value: choice, description: \"\" }));\n}\n\nfunction findSubcommand(command: Command, word: string): Command | null {\n return (\n visibleSubcommands(command).find(\n (sub) => sub.name() === word || sub.aliases().includes(word),\n ) ?? null\n );\n}\n\n/**\n * Complete the last of `words`, the words after the program name.\n * Where a flag's value is expected, offers the flag's choices if it has\n * any; otherwise returns nothing so the shell falls back to files.\n */\nexport function completeWords(\n root: Command,\n words: readonly string[],\n): Candidate[] {\n const partial = words.at(-1) ?? \"\";\n const chain: Command[] = [root];\n let current = root;\n let awaitingValue: Option | null = null;\n let operandsOnly = false;\n // Operands typed for `current`; only the first can name a subcommand.\n let operands = 0;\n\n const prior = words.slice(0, -1);\n for (const [index, word] of prior.entries()) {\n if (awaitingValue) {\n // Bash splits `--flag=value` at the `=`, so the value follows it.\n if (word !== \"=\" || prior[index - 1] === \"=\") awaitingValue = null;\n continue;\n }\n if (!operandsOnly && word === \"--\") {\n operandsOnly = true;\n continue;\n }\n if (!operandsOnly && word.startsWith(\"-\")) {\n const option = findOption(chain, word);\n awaitingValue = option?.required && !word.includes(\"=\") ? option : null;\n continue;\n }\n const sub = operands === 0 ? findSubcommand(current, word) : null;\n if (sub) {\n current = sub;\n chain.push(sub);\n } else {\n operands += 1;\n }\n }\n\n if (awaitingValue) {\n // After bash's split the partial is the bare `=` before any value.\n const typed = partial === \"=\" ? \"\" : partial;\n return choicesFor(awaitingValue, typed);\n }\n\n if (!operandsOnly && partial.startsWith(\"--\") && partial.includes(\"=\")) {\n // zsh and fish keep `--flag=value` as one word, and so must the reply.\n const [flag = \"\", typed = \"\"] = splitAssignment(partial);\n const option = findOption(chain, flag);\n if (!option?.required && !option?.optional) return [];\n return choicesFor(option, typed).map((choice) => ({\n ...choice,\n value: `${flag}=${choice.value}`,\n }));\n }\n\n if (!operandsOnly && partial.startsWith(\"-\")) {\n const seen = new Set<string>();\n const candidates: Candidate[] = [];\n for (const command of [...chain].reverse()) {\n for (const option of visibleOptions(command)) {\n // The program's --version belongs to `fluid` alone.\n if (option.long === \"--version\" && command !== current) continue;\n for (const value of [option.long, option.short]) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n candidates.push({ value, description: option.description });\n }\n }\n }\n candidates.push({ value: \"--help\", description: \"Show help\" });\n return candidates.filter((c) => c.value.startsWith(partial));\n }\n\n const candidates: Candidate[] = [];\n if (operands === 0) {\n for (const sub of visibleSubcommands(current)) {\n for (const name of [sub.name(), ...sub.aliases()]) {\n candidates.push({ value: name, description: sub.description() });\n }\n }\n }\n const args = current.registeredArguments;\n const last = args.at(-1);\n const argument = args[operands] ?? (last?.variadic ? last : undefined);\n for (const choice of argument?.argChoices ?? []) {\n candidates.push({\n value: choice,\n description: argument?.description ?? \"\",\n });\n }\n return candidates.filter((c) => c.value.startsWith(partial));\n}\n\n/**\n * The words the scripts pipe in: each followed by a NUL, the program name\n * first. Returns the words after the program name, at least one.\n */\nexport function parseCompletionInput(input: string): string[] {\n const words = input.split(\"\\0\");\n // The text after the last NUL is the empty tail of the final terminator.\n words.pop();\n const typed = words.slice(1);\n return typed.length > 0 ? typed : [\"\"];\n}\n\nfunction firstLine(text: string): string {\n return text.split(\"\\n\", 1)[0]?.trim() ?? \"\";\n}\n\n/** One candidate per line, in the shape each shell's script reads. */\nexport function formatCandidates(\n candidates: readonly Candidate[],\n shell: Shell,\n): string {\n const lines = candidates.map(({ value, description }) => {\n const summary = firstLine(description);\n switch (shell) {\n case \"bash\":\n return value;\n case \"zsh\":\n // _describe splits on the first unescaped colon.\n return summary\n ? `${value.replaceAll(\":\", \"\\\\:\")}:${summary}`\n : value.replaceAll(\":\", \"\\\\:\");\n case \"fish\":\n return summary ? `${value}\\t${summary}` : value;\n }\n });\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n\nexport function completionScript(shell: Shell): string {\n switch (shell) {\n case \"bash\":\n // Words are copied one by one: bash 3.2, macOS's /bin/bash, joins\n // a quoted array slice into one word while IFS is a newline.\n return `# fluid bash completion\n_fluid_complete() {\n local -a words\n local i\n for ((i = 0; i <= COMP_CWORD; i++)); do words+=(\"\\${COMP_WORDS[i]}\"); done\n local IFS=$'\\\\n'\n COMPREPLY=($(printf '%s\\\\0' \"\\${words[@]}\" | fluid __complete --shell bash 2>/dev/null))\n}\ncomplete -o default -F _fluid_complete fluid\n`;\n case \"zsh\":\n return `#compdef fluid\n# fluid zsh completion\n_fluid() {\n local -a candidates\n candidates=(\"\\${(@f)$(printf '%s\\\\0' \"\\${(@)words[1,CURRENT]}\" | fluid __complete --shell zsh 2>/dev/null)}\")\n candidates=(\\${candidates:#})\n if (( \\${#candidates} )); then\n _describe -t fluid 'fluid' candidates\n else\n _files\n fi\n}\nif [[ \\${zsh_eval_context[-1]} == loadautofunc ]]; then\n _fluid \"$@\"\nelse\n compdef _fluid fluid\nfi\n`;\n case \"fish\":\n // The current token goes through a variable so an empty one is still\n // sent: a bare (commandline -ct) expands to no argument at all.\n return `# fluid fish completion\nfunction __fluid_complete\n set -l current (commandline -ct)\n set -l candidates (string join0 -- (commandline -opc) \"$current\" | fluid __complete --shell fish 2>/dev/null)\n if set -q candidates[1]\n printf '%s\\\\n' $candidates\n else\n __fish_complete_path \"$current\"\n end\nend\ncomplete -c fluid -f -a '(__fluid_complete)'\n`;\n }\n}\n\nexport const COMPLETION_INSTALL_HELP = `\nInstall:\n bash echo 'eval \"$(fluid completion bash)\"' >> ~/.bashrc\n zsh fluid completion zsh > \"\\${fpath[1]}/_fluid\" (or eval \"$(fluid completion zsh)\" in ~/.zshrc)\n fish fluid completion fish > ~/.config/fish/completions/fluid.fish\n`;\n","/**\n * fluid completion <shell> — print a shell completion script.\n * fluid __complete — the hidden command that script pipes the typed words\n * into on each tab.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { Argument, Command, Option } from \"commander\";\nimport {\n completeWords,\n completionScript,\n COMPLETION_INSTALL_HELP,\n formatCandidates,\n parseCompletionInput,\n SHELLS,\n type Shell,\n} from \"../completion/complete.js\";\nimport { rootCommand } from \"../catalog/walk.js\";\n\nexport const completionCommand = new Command(\"completion\")\n .description(\"Print a shell completion script for bash, zsh or fish\")\n .addArgument(new Argument(\"<shell>\", \"Shell to complete in\").choices(SHELLS))\n .addHelpText(\"after\", COMPLETION_INSTALL_HELP)\n .action((shell: Shell) => {\n process.stdout.write(completionScript(shell));\n });\n\nexport const completeCommand = new Command(\"__complete\")\n .description(\"Print completions for the words typed so far\")\n .addOption(\n new Option(\"--shell <shell>\", \"Output format\")\n .choices(SHELLS)\n .default(\"bash\"),\n )\n .action((opts: { shell: Shell }, command: Command) => {\n // The words arrive on stdin, NUL-terminated, never as arguments.\n const typed = parseCompletionInput(readFileSync(0, \"utf8\"));\n process.stdout.write(\n formatCandidates(completeWords(rootCommand(command), typed), opts.shell),\n );\n });\n","/**\n * Helpers for constructing stored auth profiles.\n */\n\nimport type { FluidProfile } from \"../config/types.js\";\n\ninterface CreateFluidProfileInput {\n readonly name: string;\n readonly token: string;\n readonly companyName: string;\n readonly baseUrl: string;\n readonly storedAt?: string;\n}\n\nexport function createFluidProfile({\n name,\n token,\n companyName,\n baseUrl,\n storedAt,\n}: CreateFluidProfileInput): FluidProfile {\n return {\n name,\n token,\n companyName,\n storedAt: storedAt ?? new Date().toISOString(),\n baseUrl,\n };\n}\n","/**\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 getFluidApiBase,\n type CompanyChoice,\n} from \"../auth/fluid-api.js\";\nimport { createFluidProfile } from \"../auth/profiles.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]: createFluidProfile({\n name: profileName,\n token,\n companyName,\n baseUrl: getFluidApiBase(),\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 // Key on `c.id`, not the array index, and resolve via `.find` —\n // `prompts` autocomplete returns the raw typed string on Enter when\n // the user hasn't arrow-key-highlighted a filtered choice, so an\n // array-index lookup ends up asking for `companies[\"Fluid\"]` and\n // failing with \"Invalid selection\". Same shape as `switch.ts`.\n const companyChoices = companies.map((c) => ({\n title: `${c.name} (${c.shopName})`,\n value: c.id,\n }));\n\n const selectResponse = 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 = selectResponse[\"companyId\"] as number | undefined;\n\n if (companyId === undefined) {\n console.log(chalk.red(\"No company selected. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n const choice = companies.find((c) => c.id === companyId);\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 getFluidApiBase,\n type UserCompany,\n} from \"../auth/fluid-api.js\";\nimport { createFluidProfile } from \"../auth/profiles.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 baseUrl?: string,\n): Promise<void> {\n const spinner = ora(`Switching to ${chalk.bold(company.name)}...`).start();\n const result = await switchCompany(token, company.id, baseUrl);\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]: createFluidProfile({\n name: companyName,\n token: jwt,\n companyName,\n baseUrl: baseUrl ?? getFluidApiBase(),\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 const activeBaseUrl = activeProfile.baseUrl ?? getFluidApiBase();\n\n // Fetch companies from API\n const spinner = ora(\"Fetching companies...\").start();\n const result = await fetchUserCompanies(token, activeBaseUrl);\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, activeBaseUrl);\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, activeBaseUrl);\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\nexport interface PluginDiscoveryOptions {\n /**\n * Prefer plugins installed beside this CLI over same-named packages in the\n * target project's node_modules. Desktop callers use this so an older\n * project-local plugin cannot override the command contract bundled with\n * the app. Normal project-local CLI invocations keep the existing order.\n */\n preferInstallation?: boolean;\n /** Test seam for the directory containing the CLI installation. */\n installationBasePath?: 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 options: PluginDiscoveryOptions = {},\n): DiscoveredPlugin[] {\n const seen = new Set<string>();\n const results: DiscoveredPlugin[] = [];\n\n const addPlugins = (plugins: DiscoveredPlugin[]) => {\n for (const plugin of plugins) {\n if (seen.has(plugin.name)) continue;\n seen.add(plugin.name);\n results.push(plugin);\n }\n };\n\n const cliParent =\n options.installationBasePath ?? dirname(dirname(dirname(corePackageDir)));\n const projectPlugins = () => discoverFromNodeModules(basePath);\n const installationPlugins = () =>\n cliParent === basePath ? [] : discoverFromNodeModules(cliParent);\n const workspacePlugins = () => {\n if (searchFrom === null) return [];\n return discoverFromWorkspace(searchFrom ?? corePackageDir);\n };\n\n if (options.preferInstallation) {\n addPlugins(installationPlugins());\n // In a monorepo development build, resolveFluidBinary() deliberately\n // realpaths the CLI into packages/cli/core. Its sibling plugins are then\n // discovered from the workspace rather than an installation-level\n // node_modules, so they are part of the same trusted CLI installation.\n addPlugins(workspacePlugins());\n addPlugins(projectPlugins());\n } else {\n addPlugins(projectPlugins());\n addPlugins(installationPlugins());\n addPlugins(workspacePlugins());\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 * Format one-line diagnostics naming a CLI plugin build and where it was\n * resolved from. The resolved path is the load-bearing detail: a workspace\n * build and a published copy share the same name and version, so the path is\n * the only thing that distinguishes them. See CURRENT-3218.\n */\n\nimport { fileURLToPath } from \"node:url\";\n\nexport interface PluginLoadInfo {\n readonly name: string;\n readonly version: string;\n /**\n * Where the plugin was resolved from — an absolute `file://` URL (workspace\n * or published install, resolved upstream) or, only when resolution failed,\n * a bare package name.\n */\n readonly importSpecifier: string;\n /**\n * Whether installation-bundled plugins were preferred over project-local\n * ones (`FLUID_CLI_PREFER_INSTALLATION_PLUGINS=1`, set by Mist).\n */\n readonly preferInstallation: boolean;\n}\n\nexport interface PluginLoadFailureInfo {\n readonly name: string;\n readonly importSpecifier: string;\n readonly preferInstallation: boolean;\n readonly message: string;\n}\n\nexport function modeLabel(preferInstallation: boolean): string {\n return preferInstallation ? \"installation-preferred\" : \"project-first\";\n}\n\n/**\n * Render a specifier for display: `file://` URLs become absolute filesystem\n * paths; anything else is shown verbatim. Never throws — a malformed `file://`\n * specifier falls back to its raw form so a diagnostic can never turn a\n * successful load into a reported failure.\n */\nexport function displaySource(importSpecifier: string): string {\n if (!importSpecifier.startsWith(\"file://\")) return importSpecifier;\n try {\n return fileURLToPath(importSpecifier);\n } catch {\n return importSpecifier;\n }\n}\n\nexport function formatPluginLoadLine(info: PluginLoadInfo): string {\n return `Loaded plugin ${info.name}@${info.version} from ${displaySource(\n info.importSpecifier,\n )} [${modeLabel(info.preferInstallation)}]`;\n}\n\nexport function formatPluginLoadFailure(info: PluginLoadFailureInfo): string {\n return `Failed to load plugin ${info.name} from ${displaySource(\n info.importSpecifier,\n )} [${modeLabel(info.preferInstallation)}]: ${info.message}`;\n}\n","/**\n * Resolve a plugin import specifier to a concrete path for diagnostics.\n *\n * The node_modules discovery strategy falls back to a bare package name when it\n * cannot realpath a workspace symlink — precisely the published-install case\n * where the path is the only thing that would reveal a stale plugin winning.\n * Resolving the bare specifier recovers that path. See CURRENT-3218.\n */\n\nexport type Resolver = (specifier: string) => string;\n\nconst defaultResolver: Resolver = (specifier) => import.meta.resolve(specifier);\n\n/**\n * Return a `file://` URL for `importSpecifier`. `file://` specifiers (already\n * resolved by discovery) pass through untouched; bare specifiers are resolved\n * via `resolve`. If resolution throws, the bare specifier is returned so a\n * diagnostic degrades gracefully rather than failing the load.\n */\nexport function resolveLoadPath(\n importSpecifier: string,\n resolve: Resolver = defaultResolver,\n): string {\n if (importSpecifier.startsWith(\"file://\")) return importSpecifier;\n try {\n return resolve(importSpecifier);\n } catch {\n return importSpecifier;\n }\n}\n","/**\n * Decide whether to print the per-plugin \"Loaded plugin …\" diagnostics.\n *\n * A monorepo checkout resolves ~a dozen plugin packages, so emitting a line\n * for each on every command (including `--help` and tab-completion, and into\n * Mist's captured stderr dialog) is noise. The success diagnostics are gated\n * behind an explicit opt-in; load *failures* are always surfaced by the loader\n * regardless of this flag. See CURRENT-3218.\n */\n\nfunction isTruthyFlag(value: string | undefined): boolean {\n return value === \"1\" || value === \"true\";\n}\n\nexport function shouldLogPluginLoads(\n argv: readonly string[] = process.argv,\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (argv.includes(\"--verbose\")) return true;\n return isTruthyFlag(env[\"FLUID_CLI_DEBUG\"] ?? env[\"FLUID_CLI_VERBOSE\"]);\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\";\nimport {\n formatPluginLoadLine,\n formatPluginLoadFailure,\n} from \"./format-load-line.js\";\nimport { resolveLoadPath } from \"./resolve-load-path.js\";\nimport { shouldLogPluginLoads } from \"./should-log-plugin-loads.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 preferInstallation =\n process.env[\"FLUID_CLI_PREFER_INSTALLATION_PLUGINS\"] === \"1\";\n const discovered = discoverPlugins(basePath, undefined, {\n preferInstallation,\n });\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 // Success diagnostics are opt-in (a checkout resolves ~a dozen plugins);\n // load failures below are always surfaced. See CURRENT-3218.\n const verbose = shouldLogPluginLoads();\n\n for (const { name, importSpecifier } of plugins) {\n // Resolve to a concrete path so a published install is distinguishable\n // from a workspace build at a glance.\n const source = resolveLoadPath(importSpecifier);\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\n if (verbose) {\n console.error(\n chalk.dim(\n formatPluginLoadLine({\n name: exported.name,\n version: exported.version,\n importSpecifier: source,\n preferInstallation,\n }),\n ),\n );\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Always name what failed to load and from where — this is exactly the\n // invisible-artifact failure the diagnostic exists to catch.\n console.warn(\n chalk.yellow(\n `Warning: ${formatPluginLoadFailure({\n name,\n importSpecifier: source,\n preferInstallation,\n message,\n })}`,\n ),\n );\n }\n }\n}\n","/**\n * Tell an interactive user, at most once a day, that a newer fluid CLI is\n * on npm. The check never holds a command open: a run prints what the last\n * check found, and a stale cache is refreshed by a detached child process\n * for next time.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, sep } from \"node:path\";\n\nexport const PACKAGE_NAME = \"@fluid-app/fluid-cli\";\nexport const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n/** The argument the CLI entry point treats as \"refresh the cache and exit\". */\nexport const REFRESH_ARG = \"__refresh-update-check\";\nconst FETCH_TIMEOUT_MS = 5000;\nconst CACHE_FILE = \"update-check.json\";\n\nexport interface UpdateCache {\n /** When a check was last started, successful or not. */\n readonly checkedAt: string;\n /** The newest version any check has seen; null before the first success. */\n readonly latest: string | null;\n}\n\nexport interface UpdateCheckEnvironment {\n readonly env: NodeJS.ProcessEnv;\n readonly stdoutIsTTY: boolean;\n readonly stderrIsTTY: boolean;\n /** Real path of the running CLI entry point. */\n readonly entryPath: string;\n /** `process.versions`; an `electron` entry means a bundling app runs us. */\n readonly versions?: Partial<Record<string, string>>;\n}\n\n/** Whether an Electron app, such as Mist, is running the CLI it bundles. */\nexport function isElectronRun(\n env: NodeJS.ProcessEnv,\n versions: Partial<Record<string, string>> = {},\n): boolean {\n return Boolean(env[\"ELECTRON_RUN_AS_NODE\"] || versions[\"electron\"]);\n}\n\n/**\n * Only an installed CLI used by a person checks. Scripts, CI, piped output\n * (completion and `--json` among them), a workspace build and a copy an\n * Electron app bundles (the app updates it) stay quiet.\n */\nexport function shouldCheckForUpdates({\n env,\n stdoutIsTTY,\n stderrIsTTY,\n entryPath,\n versions,\n}: UpdateCheckEnvironment): boolean {\n if (env[\"FLUID_NO_UPDATE_CHECK\"] || env[\"NO_UPDATE_NOTIFIER\"]) return false;\n if (env[\"CI\"]) return false;\n if (isElectronRun(env, versions)) return false;\n if (!stdoutIsTTY || !stderrIsTTY) return false;\n return entryPath.includes(`${sep}node_modules${sep}`);\n}\n\nfunction parseVersion(version: string): [number, number, number] | null {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(version.trim());\n if (!match) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\n/** True when `latest` is a newer release than `current`. Prereleases never count. */\nexport function isNewer(current: string, latest: string): boolean {\n const a = parseVersion(current);\n const b = parseVersion(latest);\n if (!a || !b) return false;\n for (let i = 0; i < 3; i += 1) {\n if (b[i]! !== a[i]!) return b[i]! > a[i]!;\n }\n return false;\n}\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"yarn-classic\" | \"bun\";\n\n/** How the running CLI was installed, which decides the update command. */\nexport type InstallKind =\n | { readonly kind: \"global\"; readonly manager: PackageManager }\n | { readonly kind: \"npx\" }\n | {\n readonly kind: \"local\";\n readonly manager: PackageManager;\n readonly projectDir: string;\n };\n\nconst LOCKFILES: ReadonlyArray<readonly [string, PackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"bun.lock\", \"bun\"],\n [\"bun.lockb\", \"bun\"],\n [\"yarn.lock\", \"yarn\"],\n [\"package-lock.json\", \"npm\"],\n [\"npm-shrinkwrap.json\", \"npm\"],\n];\n\nfunction projectManager(\n projectDir: string,\n exists: (path: string) => boolean,\n): PackageManager {\n // A workspace member has its lockfile at the workspace root, above it.\n for (let dir = projectDir; ; dir = dirname(dir)) {\n for (const [file, manager] of LOCKFILES) {\n if (!exists(join(dir, file))) continue;\n if (manager !== \"yarn\") return manager;\n return exists(join(dir, \".yarnrc.yml\")) ? \"yarn\" : \"yarn-classic\";\n }\n if (dirname(dir) === dir) return \"npm\";\n }\n}\n\n/**\n * Work out from the entry point's real path how the CLI was installed: an\n * npx cache run, a global install (npm's `lib/node_modules`, or npm on\n * Windows, pnpm's and yarn's global directories), or else a project's own\n * `node_modules`.\n */\nexport function detectInstallKind(\n entryPath: string,\n exists: (path: string) => boolean = existsSync,\n): InstallKind {\n const path = entryPath.replaceAll(\"\\\\\", \"/\");\n if (path.includes(\"/_npx/\")) return { kind: \"npx\" };\n if (/\\/pnpm\\/global\\//i.test(path))\n return { kind: \"global\", manager: \"pnpm\" };\n if (/\\/yarn\\/global\\//i.test(path) || path.includes(\"/.yarn-global/\")) {\n return { kind: \"global\", manager: \"yarn-classic\" };\n }\n if (/\\/\\.bun\\/install\\/global\\//.test(path)) {\n return { kind: \"global\", manager: \"bun\" };\n }\n const modules = path.indexOf(\"/node_modules/\");\n const before = modules === -1 ? path : path.slice(0, modules);\n const projectDir = entryPath.slice(0, modules === -1 ? 0 : modules);\n // npm's global root is `<prefix>/lib`, or `%APPDATA%\\npm` on Windows, and\n // neither holds a package.json. A project that happens to live in a\n // directory named `lib` does, and is updated in place.\n const npmGlobalShape = before.endsWith(\"/lib\") || /\\/npm$/i.test(before);\n if (npmGlobalShape && !exists(join(projectDir, \"package.json\"))) {\n return { kind: \"global\", manager: \"npm\" };\n }\n return {\n kind: \"local\",\n manager: projectManager(projectDir, exists),\n projectDir,\n };\n}\n\nconst PLUGINS_NOTE = \"(and any @fluid-app/fluid-cli-* plugins you use)\";\n\nfunction globalCommand(manager: PackageManager): string {\n switch (manager) {\n case \"npm\":\n return `npm install -g ${PACKAGE_NAME}`;\n case \"pnpm\":\n return `pnpm add -g ${PACKAGE_NAME}@latest`;\n case \"yarn\":\n case \"yarn-classic\":\n return `yarn global add ${PACKAGE_NAME}@latest`;\n case \"bun\":\n return `bun add -g ${PACKAGE_NAME}@latest`;\n }\n}\n\nfunction localCommand(manager: PackageManager): string {\n switch (manager) {\n case \"npm\":\n return `npm install ${PACKAGE_NAME}@latest`;\n case \"pnpm\":\n return `pnpm update --latest ${PACKAGE_NAME}`;\n case \"yarn\":\n return `yarn up ${PACKAGE_NAME}@latest`;\n case \"yarn-classic\":\n return `yarn upgrade --latest ${PACKAGE_NAME}`;\n case \"bun\":\n return `bun update --latest ${PACKAGE_NAME}`;\n }\n}\n\nfunction updateInstruction(install: InstallKind): string {\n switch (install.kind) {\n case \"global\":\n return `Run: ${globalCommand(install.manager)} ${PLUGINS_NOTE}\\n`;\n case \"npx\":\n return `Run: npx ${PACKAGE_NAME}@latest to use the latest version\\n`;\n case \"local\":\n return `Run in ${install.projectDir}: ${localCommand(install.manager)} ${PLUGINS_NOTE}\\n`;\n }\n}\n\nexport function updateNotice(\n current: string,\n latest: string,\n install: InstallKind = { kind: \"global\", manager: \"npm\" },\n): string | null {\n if (!isNewer(current, latest)) return null;\n return (\n `\\nUpdate available for fluid: ${current} → ${latest}\\n` +\n updateInstruction(install) +\n `Set FLUID_NO_UPDATE_CHECK=1 to stop these notices.\\n`\n );\n}\n\nexport function readUpdateCache(configDir: string): UpdateCache | null {\n try {\n const data = JSON.parse(\n readFileSync(join(configDir, CACHE_FILE), \"utf8\"),\n ) as Partial<UpdateCache>;\n if (typeof data.checkedAt !== \"string\") return null;\n const latest = typeof data.latest === \"string\" ? data.latest : null;\n return { checkedAt: data.checkedAt, latest };\n } catch {\n return null;\n }\n}\n\n/**\n * Write the cache, reporting whether it landed. The JSON goes to a fresh\n * file opened exclusively, then is renamed over the cache: a rename replaces\n * a symlink planted at the cache path instead of writing through it, so a\n * shared FLUID_CONFIG_DIR can't aim this write at another of the user's files.\n */\nexport function writeUpdateCache(\n configDir: string,\n cache: UpdateCache,\n): boolean {\n const temp = join(\n configDir,\n `.${CACHE_FILE}.${randomBytes(6).toString(\"hex\")}`,\n );\n try {\n mkdirSync(configDir, { recursive: true });\n writeFileSync(temp, JSON.stringify(cache) + \"\\n\", { flag: \"wx\" });\n renameSync(temp, join(configDir, CACHE_FILE));\n return true;\n } catch {\n // A read-only config directory only costs the notice.\n try {\n rmSync(temp, { force: true });\n } catch {\n // Nothing was written if the directory itself is unusable.\n }\n return false;\n }\n}\n\nexport function isStale(cache: UpdateCache | null, now: Date): boolean {\n if (!cache) return true;\n const checkedAt = Date.parse(cache.checkedAt);\n return (\n Number.isNaN(checkedAt) || now.getTime() - checkedAt >= CHECK_INTERVAL_MS\n );\n}\n\nexport async function fetchLatestVersion(\n fetchImpl: typeof fetch = fetch,\n): Promise<string | null> {\n try {\n const response = await fetchImpl(\n `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,\n { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) },\n );\n if (!response.ok) return null;\n const body = (await response.json()) as { version?: unknown };\n return typeof body.version === \"string\" ? body.version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Ask npm for the latest version and record it. Runs in the detached child;\n * a failure leaves the last known version and the throttle stamp in place.\n */\nexport async function refreshUpdateCache(\n configDir: string,\n fetchImpl: typeof fetch = fetch,\n now: Date = new Date(),\n): Promise<void> {\n const latest = await fetchLatestVersion(fetchImpl);\n if (latest) {\n writeUpdateCache(configDir, { checkedAt: now.toISOString(), latest });\n }\n}\n\n/** Start `node <entry> __refresh-update-check` detached, so this run never waits on npm. */\nexport function spawnRefresh(entryPath: string): void {\n try {\n const child = spawn(process.execPath, [entryPath, REFRESH_ARG], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n });\n child.on(\"error\", () => {});\n child.unref();\n } catch {\n // No refresh this time; the throttle stamp retries tomorrow.\n }\n}\n\nexport interface StartUpdateCheckOptions extends UpdateCheckEnvironment {\n readonly currentVersion: string;\n readonly configDir: string;\n readonly now?: Date;\n /** Called with the notice when the process exits. */\n readonly onExit?: (listener: () => void) => void;\n readonly refresh?: (entryPath: string) => void;\n}\n\n/**\n * Print the cached notice when the command finishes and, when the last\n * check started a day ago or more, stamp the cache and refresh it in a\n * detached process. The stamp throttles failed and concurrent checks to one\n * a day while keeping the last known version, and its notice, in place.\n */\nexport function startUpdateCheck(options: StartUpdateCheckOptions): void {\n if (!shouldCheckForUpdates(options)) return;\n\n const now = options.now ?? new Date();\n const cache = readUpdateCache(options.configDir);\n const notice = cache?.latest\n ? updateNotice(\n options.currentVersion,\n cache.latest,\n detectInstallKind(options.entryPath),\n )\n : null;\n if (notice) {\n const onExit =\n options.onExit ??\n ((listener: () => void) => process.once(\"exit\", listener));\n onExit(() => process.stderr.write(notice));\n }\n\n if (!isStale(cache, now)) return;\n const stamped = writeUpdateCache(options.configDir, {\n checkedAt: now.toISOString(),\n latest: cache?.latest ?? null,\n });\n // Without the stamp there is no throttle, and the child couldn't record\n // what it found either, so an unwritable cache never starts a refresh.\n if (stamped) (options.refresh ?? spawnRefresh)(options.entryPath);\n}\n","#!/usr/bin/env node\n\nimport { createRequire } from \"node:module\";\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { commandsCommand } from \"../commands/commands.js\";\nimport { completeCommand, completionCommand } from \"../commands/completion.js\";\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\";\nimport { getConfigDir } from \"../config/paths.js\";\nimport {\n REFRESH_ARG,\n refreshUpdateCache,\n startUpdateCheck,\n} from \"../update/notifier.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\n// The detached update check re-runs this entry point; it only refreshes the\n// cache, without loading plugins or parsing commands.\nif (process.argv[2] === REFRESH_ARG) {\n await refreshUpdateCache(getConfigDir());\n process.exit(0);\n}\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// Discovery: the command catalog and shell completion\nprogram.addCommand(commandsCommand);\nprogram.addCommand(completionCommand);\nprogram.addCommand(completeCommand, { hidden: true });\n\nstartUpdateCheck({\n currentVersion: version,\n configDir: getConfigDir(),\n env: process.env,\n stdoutIsTTY: Boolean(process.stdout.isTTY),\n stderrIsTTY: Boolean(process.stderr.isTTY),\n entryPath: realpathSync(fileURLToPath(import.meta.url)),\n versions: process.versions,\n});\n\n// Discover and load all plugins (auto-discovered from node_modules and workspace)\nawait loadPlugins(program, packageRoot);\n\nprogram.parse(process.argv, { from: \"node\" });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fluid.mjs","names":[],"sources":["../../src/commands/commands.ts","../../src/completion/complete.ts","../../src/commands/completion.ts","../../src/auth/profiles.ts","../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/whoami.ts","../../src/commands/switch.ts","../../src/plugins/discovery.ts","../../src/plugins/format-load-line.ts","../../src/plugins/resolve-load-path.ts","../../src/plugins/should-log-plugin-loads.ts","../../src/plugins/loader.ts","../../src/update/notifier.ts","../../src/bin/fluid.ts"],"sourcesContent":["/**\n * fluid commands — list every installed command, or print the catalog as\n * JSON for agents and documentation generators.\n */\n\nimport { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport {\n filterCatalog,\n rootCommand,\n walkCommands,\n type CatalogEntry,\n} from \"../catalog/walk.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\nfunction usage(entry: CatalogEntry): string {\n const args = entry.arguments.map((arg) => {\n const name = arg.variadic ? `${arg.name}...` : arg.name;\n return arg.required ? `<${name}>` : `[${name}]`;\n });\n return [entry.command, ...args].join(\" \");\n}\n\nexport function formatCatalogText(entries: readonly CatalogEntry[]): string {\n const runnable = entries.filter((entry) => entry.runnable);\n const width = Math.min(\n 48,\n Math.max(0, ...runnable.map((entry) => usage(entry).length)),\n );\n return runnable\n .map((entry) => {\n const line = usage(entry);\n const summary = entry.description.split(\"\\n\", 1)[0] ?? \"\";\n return summary ? `${line.padEnd(width)} ${summary}` : line;\n })\n .join(\"\\n\");\n}\n\nexport const commandsCommand = new Command(\"commands\")\n .description(\"List installed commands; --json prints the full catalog\")\n .argument(\"[prefix...]\", \"Only list commands under this one, e.g. `domains`\")\n .option(\"--json\", \"Print the catalog as JSON\", false)\n .action((prefix: string[], opts: { json: boolean }, command: Command) => {\n const root = rootCommand(command);\n\n const entries = filterCatalog(walkCommands(root), root, prefix);\n if (entries.length === 0) {\n process.stderr.write(\n JSON.stringify({ error: `No command named \"${prefix.join(\" \")}\".` }) +\n \"\\n\",\n );\n process.exitCode = 1;\n return;\n }\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n { cli: root.name(), version, commands: entries },\n null,\n 2,\n ) + \"\\n\",\n );\n return;\n }\n process.stdout.write(formatCatalogText(entries) + \"\\n\");\n });\n","/**\n * Shell completion. The scripts `fluid completion <shell>` prints pipe the\n * words typed so far into the hidden `fluid __complete`, so completions come\n * from the plugins actually installed rather than a list frozen at install.\n * The words go over stdin, never argv: a half-typed `--token <value>` must\n * not show up in another user's process list.\n */\n\nimport type { Command, Option } from \"commander\";\nimport { visibleOptions, visibleSubcommands } from \"../catalog/walk.js\";\n\nexport const SHELLS = [\"bash\", \"zsh\", \"fish\"] as const;\nexport type Shell = (typeof SHELLS)[number];\n\nexport interface Candidate {\n readonly value: string;\n readonly description: string;\n}\n\nfunction findOption(chain: readonly Command[], word: string): Option | null {\n const flag = word.split(\"=\", 1)[0] ?? word;\n for (const command of [...chain].reverse()) {\n const match = command.options.find(\n (option) => option.long === flag || option.short === flag,\n );\n if (match) return match;\n }\n return null;\n}\n\nfunction splitAssignment(word: string): [string, string] {\n const at = word.indexOf(\"=\");\n return [word.slice(0, at), word.slice(at + 1)];\n}\n\nfunction choicesFor(option: Option, typed: string): Candidate[] {\n return (option.argChoices ?? [])\n .filter((choice) => choice.startsWith(typed))\n .map((choice) => ({ value: choice, description: \"\" }));\n}\n\nfunction findSubcommand(command: Command, word: string): Command | null {\n return (\n visibleSubcommands(command).find(\n (sub) => sub.name() === word || sub.aliases().includes(word),\n ) ?? null\n );\n}\n\n/**\n * Complete the last of `words`, the words after the program name.\n * Where a flag's value is expected, offers the flag's choices if it has\n * any; otherwise returns nothing so the shell falls back to files.\n */\nexport function completeWords(\n root: Command,\n words: readonly string[],\n): Candidate[] {\n const partial = words.at(-1) ?? \"\";\n const chain: Command[] = [root];\n let current = root;\n let awaitingValue: Option | null = null;\n let operandsOnly = false;\n // Operands typed for `current`; only the first can name a subcommand.\n let operands = 0;\n\n const prior = words.slice(0, -1);\n for (const [index, word] of prior.entries()) {\n if (awaitingValue) {\n // Bash splits `--flag=value` at the `=`, so the value follows it.\n if (word !== \"=\" || prior[index - 1] === \"=\") awaitingValue = null;\n continue;\n }\n if (!operandsOnly && word === \"--\") {\n operandsOnly = true;\n continue;\n }\n if (!operandsOnly && word.startsWith(\"-\")) {\n const option = findOption(chain, word);\n awaitingValue = option?.required && !word.includes(\"=\") ? option : null;\n continue;\n }\n const sub = operands === 0 ? findSubcommand(current, word) : null;\n if (sub) {\n current = sub;\n chain.push(sub);\n } else {\n operands += 1;\n }\n }\n\n if (awaitingValue) {\n // After bash's split the partial is the bare `=` before any value.\n const typed = partial === \"=\" ? \"\" : partial;\n return choicesFor(awaitingValue, typed);\n }\n\n if (!operandsOnly && partial.startsWith(\"--\") && partial.includes(\"=\")) {\n // zsh and fish keep `--flag=value` as one word, and so must the reply.\n const [flag = \"\", typed = \"\"] = splitAssignment(partial);\n const option = findOption(chain, flag);\n if (!option?.required && !option?.optional) return [];\n return choicesFor(option, typed).map((choice) => ({\n ...choice,\n value: `${flag}=${choice.value}`,\n }));\n }\n\n if (!operandsOnly && partial.startsWith(\"-\")) {\n const seen = new Set<string>();\n const candidates: Candidate[] = [];\n for (const command of [...chain].reverse()) {\n for (const option of visibleOptions(command)) {\n // The program's --version belongs to `fluid` alone.\n if (option.long === \"--version\" && command !== current) continue;\n for (const value of [option.long, option.short]) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n candidates.push({ value, description: option.description });\n }\n }\n }\n candidates.push({ value: \"--help\", description: \"Show help\" });\n return candidates.filter((c) => c.value.startsWith(partial));\n }\n\n const candidates: Candidate[] = [];\n if (operands === 0) {\n for (const sub of visibleSubcommands(current)) {\n for (const name of [sub.name(), ...sub.aliases()]) {\n candidates.push({ value: name, description: sub.description() });\n }\n }\n }\n const args = current.registeredArguments;\n const last = args.at(-1);\n const argument = args[operands] ?? (last?.variadic ? last : undefined);\n for (const choice of argument?.argChoices ?? []) {\n candidates.push({\n value: choice,\n description: argument?.description ?? \"\",\n });\n }\n return candidates.filter((c) => c.value.startsWith(partial));\n}\n\n/**\n * The words the scripts pipe in: each followed by a NUL, the program name\n * first. Returns the words after the program name, at least one.\n */\nexport function parseCompletionInput(input: string): string[] {\n const words = input.split(\"\\0\");\n // The text after the last NUL is the empty tail of the final terminator.\n words.pop();\n const typed = words.slice(1);\n return typed.length > 0 ? typed : [\"\"];\n}\n\nfunction firstLine(text: string): string {\n return text.split(\"\\n\", 1)[0]?.trim() ?? \"\";\n}\n\n/** One candidate per line, in the shape each shell's script reads. */\nexport function formatCandidates(\n candidates: readonly Candidate[],\n shell: Shell,\n): string {\n const lines = candidates.map(({ value, description }) => {\n const summary = firstLine(description);\n switch (shell) {\n case \"bash\":\n return value;\n case \"zsh\":\n // _describe splits on the first unescaped colon.\n return summary\n ? `${value.replaceAll(\":\", \"\\\\:\")}:${summary}`\n : value.replaceAll(\":\", \"\\\\:\");\n case \"fish\":\n return summary ? `${value}\\t${summary}` : value;\n }\n });\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n\nexport function completionScript(shell: Shell): string {\n switch (shell) {\n case \"bash\":\n // Words are copied one by one: bash 3.2, macOS's /bin/bash, joins\n // a quoted array slice into one word while IFS is a newline.\n return `# fluid bash completion\n_fluid_complete() {\n local -a words\n local i\n for ((i = 0; i <= COMP_CWORD; i++)); do words+=(\"\\${COMP_WORDS[i]}\"); done\n local IFS=$'\\\\n'\n COMPREPLY=($(printf '%s\\\\0' \"\\${words[@]}\" | fluid __complete --shell bash 2>/dev/null))\n}\ncomplete -o default -F _fluid_complete fluid\n`;\n case \"zsh\":\n return `#compdef fluid\n# fluid zsh completion\n_fluid() {\n local -a candidates\n candidates=(\"\\${(@f)$(printf '%s\\\\0' \"\\${(@)words[1,CURRENT]}\" | fluid __complete --shell zsh 2>/dev/null)}\")\n candidates=(\\${candidates:#})\n if (( \\${#candidates} )); then\n _describe -t fluid 'fluid' candidates\n else\n _files\n fi\n}\nif [[ \\${zsh_eval_context[-1]} == loadautofunc ]]; then\n _fluid \"$@\"\nelse\n compdef _fluid fluid\nfi\n`;\n case \"fish\":\n // The current token goes through a variable so an empty one is still\n // sent: a bare (commandline -ct) expands to no argument at all.\n return `# fluid fish completion\nfunction __fluid_complete\n set -l current (commandline -ct)\n set -l candidates (string join0 -- (commandline -opc) \"$current\" | fluid __complete --shell fish 2>/dev/null)\n if set -q candidates[1]\n printf '%s\\\\n' $candidates\n else\n __fish_complete_path \"$current\"\n end\nend\ncomplete -c fluid -f -a '(__fluid_complete)'\n`;\n }\n}\n\nexport const COMPLETION_INSTALL_HELP = `\nInstall:\n bash echo 'eval \"$(fluid completion bash)\"' >> ~/.bashrc\n zsh fluid completion zsh > \"\\${fpath[1]}/_fluid\" (or eval \"$(fluid completion zsh)\" in ~/.zshrc)\n fish fluid completion fish > ~/.config/fish/completions/fluid.fish\n`;\n","/**\n * fluid completion <shell> — print a shell completion script.\n * fluid __complete — the hidden command that script pipes the typed words\n * into on each tab.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { Argument, Command, Option } from \"commander\";\nimport {\n completeWords,\n completionScript,\n COMPLETION_INSTALL_HELP,\n formatCandidates,\n parseCompletionInput,\n SHELLS,\n type Shell,\n} from \"../completion/complete.js\";\nimport { rootCommand } from \"../catalog/walk.js\";\n\nexport const completionCommand = new Command(\"completion\")\n .description(\"Print a shell completion script for bash, zsh or fish\")\n .addArgument(new Argument(\"<shell>\", \"Shell to complete in\").choices(SHELLS))\n .addHelpText(\"after\", COMPLETION_INSTALL_HELP)\n .action((shell: Shell) => {\n process.stdout.write(completionScript(shell));\n });\n\nexport const completeCommand = new Command(\"__complete\")\n .description(\"Print completions for the words typed so far\")\n .addOption(\n new Option(\"--shell <shell>\", \"Output format\")\n .choices(SHELLS)\n .default(\"bash\"),\n )\n .action((opts: { shell: Shell }, command: Command) => {\n // The words arrive on stdin, NUL-terminated, never as arguments.\n const typed = parseCompletionInput(readFileSync(0, \"utf8\"));\n process.stdout.write(\n formatCandidates(completeWords(rootCommand(command), typed), opts.shell),\n );\n });\n","/**\n * Helpers for constructing stored auth profiles.\n */\n\nimport type { FluidProfile } from \"../config/types.js\";\n\ninterface CreateFluidProfileInput {\n readonly name: string;\n readonly token: string;\n readonly companyName: string;\n readonly baseUrl: string;\n readonly storedAt?: string;\n}\n\nexport function createFluidProfile({\n name,\n token,\n companyName,\n baseUrl,\n storedAt,\n}: CreateFluidProfileInput): FluidProfile {\n return {\n name,\n token,\n companyName,\n storedAt: storedAt ?? new Date().toISOString(),\n baseUrl,\n };\n}\n","/**\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 getFluidApiBase,\n type CompanyChoice,\n} from \"../auth/fluid-api.js\";\nimport { createFluidProfile } from \"../auth/profiles.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]: createFluidProfile({\n name: profileName,\n token,\n companyName,\n baseUrl: getFluidApiBase(),\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 // Key on `c.id`, not the array index, and resolve via `.find` —\n // `prompts` autocomplete returns the raw typed string on Enter when\n // the user hasn't arrow-key-highlighted a filtered choice, so an\n // array-index lookup ends up asking for `companies[\"Fluid\"]` and\n // failing with \"Invalid selection\". Same shape as `switch.ts`.\n const companyChoices = companies.map((c) => ({\n title: `${c.name} (${c.shopName})`,\n value: c.id,\n }));\n\n const selectResponse = 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 = selectResponse[\"companyId\"] as number | undefined;\n\n if (companyId === undefined) {\n console.log(chalk.red(\"No company selected. Aborting.\"));\n process.exitCode = 1;\n return;\n }\n const choice = companies.find((c) => c.id === companyId);\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 getFluidApiBase,\n type UserCompany,\n} from \"../auth/fluid-api.js\";\nimport { createFluidProfile } from \"../auth/profiles.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 baseUrl?: string,\n): Promise<void> {\n const spinner = ora(`Switching to ${chalk.bold(company.name)}...`).start();\n const result = await switchCompany(token, company.id, baseUrl);\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]: createFluidProfile({\n name: companyName,\n token: jwt,\n companyName,\n baseUrl: baseUrl ?? getFluidApiBase(),\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 const activeBaseUrl = activeProfile.baseUrl ?? getFluidApiBase();\n\n // Fetch companies from API\n const spinner = ora(\"Fetching companies...\").start();\n const result = await fetchUserCompanies(token, activeBaseUrl);\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, activeBaseUrl);\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, activeBaseUrl);\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// Strategy 1: node_modules scan (production / published installs)\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// Strategy 2: workspace scan (pnpm monorepo development)\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\nexport interface PluginDiscoveryOptions {\n /**\n * Prefer plugins installed beside this CLI over same-named packages in the\n * target project's node_modules. Desktop callers use this so an older\n * project-local plugin cannot override the command contract bundled with\n * the app. Normal project-local CLI invocations keep the existing order.\n */\n preferInstallation?: boolean;\n /** Test seam for the directory containing the CLI installation. */\n installationBasePath?: 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// Exported API\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 options: PluginDiscoveryOptions = {},\n): DiscoveredPlugin[] {\n const seen = new Set<string>();\n const results: DiscoveredPlugin[] = [];\n\n const addPlugins = (plugins: DiscoveredPlugin[]) => {\n for (const plugin of plugins) {\n if (seen.has(plugin.name)) continue;\n seen.add(plugin.name);\n results.push(plugin);\n }\n };\n\n const cliParent =\n options.installationBasePath ?? dirname(dirname(dirname(corePackageDir)));\n const projectPlugins = () => discoverFromNodeModules(basePath);\n const installationPlugins = () =>\n cliParent === basePath ? [] : discoverFromNodeModules(cliParent);\n const workspacePlugins = () => {\n if (searchFrom === null) return [];\n return discoverFromWorkspace(searchFrom ?? corePackageDir);\n };\n\n if (options.preferInstallation) {\n addPlugins(installationPlugins());\n // In a monorepo development build, resolveFluidBinary() deliberately\n // realpaths the CLI into packages/cli/core. Its sibling plugins are then\n // discovered from the workspace rather than an installation-level\n // node_modules, so they are part of the same trusted CLI installation.\n addPlugins(workspacePlugins());\n addPlugins(projectPlugins());\n } else {\n addPlugins(projectPlugins());\n addPlugins(installationPlugins());\n addPlugins(workspacePlugins());\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 * Format one-line diagnostics naming a CLI plugin build and where it was\n * resolved from. The resolved path is the load-bearing detail: a workspace\n * build and a published copy share the same name and version, so the path is\n * the only thing that distinguishes them. See CURRENT-3218.\n */\n\nimport { fileURLToPath } from \"node:url\";\n\nexport interface PluginLoadInfo {\n readonly name: string;\n readonly version: string;\n /**\n * Where the plugin was resolved from — an absolute `file://` URL (workspace\n * or published install, resolved upstream) or, only when resolution failed,\n * a bare package name.\n */\n readonly importSpecifier: string;\n /**\n * Whether installation-bundled plugins were preferred over project-local\n * ones (`FLUID_CLI_PREFER_INSTALLATION_PLUGINS=1`, set by Mist).\n */\n readonly preferInstallation: boolean;\n}\n\nexport interface PluginLoadFailureInfo {\n readonly name: string;\n readonly importSpecifier: string;\n readonly preferInstallation: boolean;\n readonly message: string;\n}\n\nexport function modeLabel(preferInstallation: boolean): string {\n return preferInstallation ? \"installation-preferred\" : \"project-first\";\n}\n\n/**\n * Render a specifier for display: `file://` URLs become absolute filesystem\n * paths; anything else is shown verbatim. Never throws — a malformed `file://`\n * specifier falls back to its raw form so a diagnostic can never turn a\n * successful load into a reported failure.\n */\nexport function displaySource(importSpecifier: string): string {\n if (!importSpecifier.startsWith(\"file://\")) return importSpecifier;\n try {\n return fileURLToPath(importSpecifier);\n } catch {\n return importSpecifier;\n }\n}\n\nexport function formatPluginLoadLine(info: PluginLoadInfo): string {\n return `Loaded plugin ${info.name}@${info.version} from ${displaySource(\n info.importSpecifier,\n )} [${modeLabel(info.preferInstallation)}]`;\n}\n\nexport function formatPluginLoadFailure(info: PluginLoadFailureInfo): string {\n return `Failed to load plugin ${info.name} from ${displaySource(\n info.importSpecifier,\n )} [${modeLabel(info.preferInstallation)}]: ${info.message}`;\n}\n","/**\n * Resolve a plugin import specifier to a concrete path for diagnostics.\n *\n * The node_modules discovery strategy falls back to a bare package name when it\n * cannot realpath a workspace symlink — precisely the published-install case\n * where the path is the only thing that would reveal a stale plugin winning.\n * Resolving the bare specifier recovers that path. See CURRENT-3218.\n */\n\nexport type Resolver = (specifier: string) => string;\n\nconst defaultResolver: Resolver = (specifier) => import.meta.resolve(specifier);\n\n/**\n * Return a `file://` URL for `importSpecifier`. `file://` specifiers (already\n * resolved by discovery) pass through untouched; bare specifiers are resolved\n * via `resolve`. If resolution throws, the bare specifier is returned so a\n * diagnostic degrades gracefully rather than failing the load.\n */\nexport function resolveLoadPath(\n importSpecifier: string,\n resolve: Resolver = defaultResolver,\n): string {\n if (importSpecifier.startsWith(\"file://\")) return importSpecifier;\n try {\n return resolve(importSpecifier);\n } catch {\n return importSpecifier;\n }\n}\n","/**\n * Decide whether to print the per-plugin \"Loaded plugin …\" diagnostics.\n *\n * A monorepo checkout resolves ~a dozen plugin packages, so emitting a line\n * for each on every command (including `--help` and tab-completion, and into\n * Mist's captured stderr dialog) is noise. The success diagnostics are gated\n * behind an explicit opt-in; load *failures* are always surfaced by the loader\n * regardless of this flag. See CURRENT-3218.\n */\n\nfunction isTruthyFlag(value: string | undefined): boolean {\n return value === \"1\" || value === \"true\";\n}\n\nexport function shouldLogPluginLoads(\n argv: readonly string[] = process.argv,\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (argv.includes(\"--verbose\")) return true;\n return isTruthyFlag(env[\"FLUID_CLI_DEBUG\"] ?? env[\"FLUID_CLI_VERBOSE\"]);\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\";\nimport {\n formatPluginLoadLine,\n formatPluginLoadFailure,\n} from \"./format-load-line.js\";\nimport { resolveLoadPath } from \"./resolve-load-path.js\";\nimport { shouldLogPluginLoads } from \"./should-log-plugin-loads.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 preferInstallation =\n process.env[\"FLUID_CLI_PREFER_INSTALLATION_PLUGINS\"] === \"1\";\n const discovered = discoverPlugins(basePath, undefined, {\n preferInstallation,\n });\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 // Success diagnostics are opt-in (a checkout resolves ~a dozen plugins);\n // load failures below are always surfaced. See CURRENT-3218.\n const verbose = shouldLogPluginLoads();\n\n for (const { name, importSpecifier } of plugins) {\n // Resolve to a concrete path so a published install is distinguishable\n // from a workspace build at a glance.\n const source = resolveLoadPath(importSpecifier);\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\n if (verbose) {\n console.error(\n chalk.dim(\n formatPluginLoadLine({\n name: exported.name,\n version: exported.version,\n importSpecifier: source,\n preferInstallation,\n }),\n ),\n );\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Always name what failed to load and from where — this is exactly the\n // invisible-artifact failure the diagnostic exists to catch.\n console.warn(\n chalk.yellow(\n `Warning: ${formatPluginLoadFailure({\n name,\n importSpecifier: source,\n preferInstallation,\n message,\n })}`,\n ),\n );\n }\n }\n}\n","/**\n * Tell an interactive user, at most once a day, that a newer fluid CLI is\n * on npm. The check never holds a command open: a run prints what the last\n * check found, and a stale cache is refreshed by a detached child process\n * for next time.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, sep } from \"node:path\";\n\nexport const PACKAGE_NAME = \"@fluid-app/fluid-cli\";\nexport const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n/** The argument the CLI entry point treats as \"refresh the cache and exit\". */\nexport const REFRESH_ARG = \"__refresh-update-check\";\nconst FETCH_TIMEOUT_MS = 5000;\nconst CACHE_FILE = \"update-check.json\";\n\nexport interface UpdateCache {\n /** When a check was last started, successful or not. */\n readonly checkedAt: string;\n /** The newest version any check has seen; null before the first success. */\n readonly latest: string | null;\n}\n\nexport interface UpdateCheckEnvironment {\n readonly env: NodeJS.ProcessEnv;\n readonly stdoutIsTTY: boolean;\n readonly stderrIsTTY: boolean;\n /** Real path of the running CLI entry point. */\n readonly entryPath: string;\n /** `process.versions`; an `electron` entry means a bundling app runs us. */\n readonly versions?: Partial<Record<string, string>>;\n}\n\n/** Whether an Electron app, such as Mist, is running the CLI it bundles. */\nexport function isElectronRun(\n env: NodeJS.ProcessEnv,\n versions: Partial<Record<string, string>> = {},\n): boolean {\n return Boolean(env[\"ELECTRON_RUN_AS_NODE\"] || versions[\"electron\"]);\n}\n\n/**\n * Only an installed CLI used by a person checks. Scripts, CI, piped output\n * (completion and `--json` among them), a workspace build and a copy an\n * Electron app bundles (the app updates it) stay quiet.\n */\nexport function shouldCheckForUpdates({\n env,\n stdoutIsTTY,\n stderrIsTTY,\n entryPath,\n versions,\n}: UpdateCheckEnvironment): boolean {\n if (env[\"FLUID_NO_UPDATE_CHECK\"] || env[\"NO_UPDATE_NOTIFIER\"]) return false;\n if (env[\"CI\"]) return false;\n if (isElectronRun(env, versions)) return false;\n if (!stdoutIsTTY || !stderrIsTTY) return false;\n return entryPath.includes(`${sep}node_modules${sep}`);\n}\n\nfunction parseVersion(version: string): [number, number, number] | null {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(version.trim());\n if (!match) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\n/** True when `latest` is a newer release than `current`. Prereleases never count. */\nexport function isNewer(current: string, latest: string): boolean {\n const a = parseVersion(current);\n const b = parseVersion(latest);\n if (!a || !b) return false;\n for (let i = 0; i < 3; i += 1) {\n if (b[i]! !== a[i]!) return b[i]! > a[i]!;\n }\n return false;\n}\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"yarn-classic\" | \"bun\";\n\n/** How the running CLI was installed, which decides the update command. */\nexport type InstallKind =\n | { readonly kind: \"global\"; readonly manager: PackageManager }\n | { readonly kind: \"npx\" }\n | {\n readonly kind: \"local\";\n readonly manager: PackageManager;\n readonly projectDir: string;\n };\n\nconst LOCKFILES: ReadonlyArray<readonly [string, PackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"bun.lock\", \"bun\"],\n [\"bun.lockb\", \"bun\"],\n [\"yarn.lock\", \"yarn\"],\n [\"package-lock.json\", \"npm\"],\n [\"npm-shrinkwrap.json\", \"npm\"],\n];\n\nfunction projectManager(\n projectDir: string,\n exists: (path: string) => boolean,\n): PackageManager {\n // A workspace member has its lockfile at the workspace root, above it.\n for (let dir = projectDir; ; dir = dirname(dir)) {\n for (const [file, manager] of LOCKFILES) {\n if (!exists(join(dir, file))) continue;\n if (manager !== \"yarn\") return manager;\n return exists(join(dir, \".yarnrc.yml\")) ? \"yarn\" : \"yarn-classic\";\n }\n if (dirname(dir) === dir) return \"npm\";\n }\n}\n\n/**\n * Work out from the entry point's real path how the CLI was installed: an\n * npx cache run, a global install (npm's `lib/node_modules`, or npm on\n * Windows, pnpm's and yarn's global directories), or else a project's own\n * `node_modules`.\n */\nexport function detectInstallKind(\n entryPath: string,\n exists: (path: string) => boolean = existsSync,\n): InstallKind {\n const path = entryPath.replaceAll(\"\\\\\", \"/\");\n if (path.includes(\"/_npx/\")) return { kind: \"npx\" };\n if (/\\/pnpm\\/global\\//i.test(path))\n return { kind: \"global\", manager: \"pnpm\" };\n if (/\\/yarn\\/global\\//i.test(path) || path.includes(\"/.yarn-global/\")) {\n return { kind: \"global\", manager: \"yarn-classic\" };\n }\n if (/\\/\\.bun\\/install\\/global\\//.test(path)) {\n return { kind: \"global\", manager: \"bun\" };\n }\n const modules = path.indexOf(\"/node_modules/\");\n const before = modules === -1 ? path : path.slice(0, modules);\n const projectDir = entryPath.slice(0, modules === -1 ? 0 : modules);\n // npm's global root is `<prefix>/lib`, or `%APPDATA%\\npm` on Windows, and\n // neither holds a package.json. A project that happens to live in a\n // directory named `lib` does, and is updated in place.\n const npmGlobalShape = before.endsWith(\"/lib\") || /\\/npm$/i.test(before);\n if (npmGlobalShape && !exists(join(projectDir, \"package.json\"))) {\n return { kind: \"global\", manager: \"npm\" };\n }\n return {\n kind: \"local\",\n manager: projectManager(projectDir, exists),\n projectDir,\n };\n}\n\nconst PLUGINS_NOTE = \"(and any @fluid-app/fluid-cli-* plugins you use)\";\n\nfunction globalCommand(manager: PackageManager): string {\n switch (manager) {\n case \"npm\":\n return `npm install -g ${PACKAGE_NAME}`;\n case \"pnpm\":\n return `pnpm add -g ${PACKAGE_NAME}@latest`;\n case \"yarn\":\n case \"yarn-classic\":\n return `yarn global add ${PACKAGE_NAME}@latest`;\n case \"bun\":\n return `bun add -g ${PACKAGE_NAME}@latest`;\n }\n}\n\nfunction localCommand(manager: PackageManager): string {\n switch (manager) {\n case \"npm\":\n return `npm install ${PACKAGE_NAME}@latest`;\n case \"pnpm\":\n return `pnpm update --latest ${PACKAGE_NAME}`;\n case \"yarn\":\n return `yarn up ${PACKAGE_NAME}@latest`;\n case \"yarn-classic\":\n return `yarn upgrade --latest ${PACKAGE_NAME}`;\n case \"bun\":\n return `bun update --latest ${PACKAGE_NAME}`;\n }\n}\n\nfunction updateInstruction(install: InstallKind): string {\n switch (install.kind) {\n case \"global\":\n return `Run: ${globalCommand(install.manager)} ${PLUGINS_NOTE}\\n`;\n case \"npx\":\n return `Run: npx ${PACKAGE_NAME}@latest to use the latest version\\n`;\n case \"local\":\n return `Run in ${install.projectDir}: ${localCommand(install.manager)} ${PLUGINS_NOTE}\\n`;\n }\n}\n\nexport function updateNotice(\n current: string,\n latest: string,\n install: InstallKind = { kind: \"global\", manager: \"npm\" },\n): string | null {\n if (!isNewer(current, latest)) return null;\n return (\n `\\nUpdate available for fluid: ${current} → ${latest}\\n` +\n updateInstruction(install) +\n `Set FLUID_NO_UPDATE_CHECK=1 to stop these notices.\\n`\n );\n}\n\nexport function readUpdateCache(configDir: string): UpdateCache | null {\n try {\n const data = JSON.parse(\n readFileSync(join(configDir, CACHE_FILE), \"utf8\"),\n ) as Partial<UpdateCache>;\n if (typeof data.checkedAt !== \"string\") return null;\n const latest = typeof data.latest === \"string\" ? data.latest : null;\n return { checkedAt: data.checkedAt, latest };\n } catch {\n return null;\n }\n}\n\n/**\n * Write the cache, reporting whether it landed. The JSON goes to a fresh\n * file opened exclusively, then is renamed over the cache: a rename replaces\n * a symlink planted at the cache path instead of writing through it, so a\n * shared FLUID_CONFIG_DIR can't aim this write at another of the user's files.\n */\nexport function writeUpdateCache(\n configDir: string,\n cache: UpdateCache,\n): boolean {\n const temp = join(\n configDir,\n `.${CACHE_FILE}.${randomBytes(6).toString(\"hex\")}`,\n );\n try {\n mkdirSync(configDir, { recursive: true });\n writeFileSync(temp, JSON.stringify(cache) + \"\\n\", { flag: \"wx\" });\n renameSync(temp, join(configDir, CACHE_FILE));\n return true;\n } catch {\n // A read-only config directory only costs the notice.\n try {\n rmSync(temp, { force: true });\n } catch {\n // Nothing was written if the directory itself is unusable.\n }\n return false;\n }\n}\n\nexport function isStale(cache: UpdateCache | null, now: Date): boolean {\n if (!cache) return true;\n const checkedAt = Date.parse(cache.checkedAt);\n return (\n Number.isNaN(checkedAt) || now.getTime() - checkedAt >= CHECK_INTERVAL_MS\n );\n}\n\nexport async function fetchLatestVersion(\n fetchImpl: typeof fetch = fetch,\n): Promise<string | null> {\n try {\n const response = await fetchImpl(\n `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,\n { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) },\n );\n if (!response.ok) return null;\n const body = (await response.json()) as { version?: unknown };\n return typeof body.version === \"string\" ? body.version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Ask npm for the latest version and record it. Runs in the detached child;\n * a failure leaves the last known version and the throttle stamp in place.\n */\nexport async function refreshUpdateCache(\n configDir: string,\n fetchImpl: typeof fetch = fetch,\n now: Date = new Date(),\n): Promise<void> {\n const latest = await fetchLatestVersion(fetchImpl);\n if (latest) {\n writeUpdateCache(configDir, { checkedAt: now.toISOString(), latest });\n }\n}\n\n/** Start `node <entry> __refresh-update-check` detached, so this run never waits on npm. */\nexport function spawnRefresh(entryPath: string): void {\n try {\n const child = spawn(process.execPath, [entryPath, REFRESH_ARG], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n });\n child.on(\"error\", () => {});\n child.unref();\n } catch {\n // No refresh this time; the throttle stamp retries tomorrow.\n }\n}\n\nexport interface StartUpdateCheckOptions extends UpdateCheckEnvironment {\n readonly currentVersion: string;\n readonly configDir: string;\n readonly now?: Date;\n /** Called with the notice when the process exits. */\n readonly onExit?: (listener: () => void) => void;\n readonly refresh?: (entryPath: string) => void;\n}\n\n/**\n * Print the cached notice when the command finishes and, when the last\n * check started a day ago or more, stamp the cache and refresh it in a\n * detached process. The stamp throttles failed and concurrent checks to one\n * a day while keeping the last known version, and its notice, in place.\n */\nexport function startUpdateCheck(options: StartUpdateCheckOptions): void {\n if (!shouldCheckForUpdates(options)) return;\n\n const now = options.now ?? new Date();\n const cache = readUpdateCache(options.configDir);\n const notice = cache?.latest\n ? updateNotice(\n options.currentVersion,\n cache.latest,\n detectInstallKind(options.entryPath),\n )\n : null;\n if (notice) {\n const onExit =\n options.onExit ??\n ((listener: () => void) => process.once(\"exit\", listener));\n onExit(() => process.stderr.write(notice));\n }\n\n if (!isStale(cache, now)) return;\n const stamped = writeUpdateCache(options.configDir, {\n checkedAt: now.toISOString(),\n latest: cache?.latest ?? null,\n });\n // Without the stamp there is no throttle, and the child couldn't record\n // what it found either, so an unwritable cache never starts a refresh.\n if (stamped) (options.refresh ?? spawnRefresh)(options.entryPath);\n}\n","#!/usr/bin/env node\n\nimport { createRequire } from \"node:module\";\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { commandsCommand } from \"../commands/commands.js\";\nimport { completeCommand, completionCommand } from \"../commands/completion.js\";\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\";\nimport { getConfigDir } from \"../config/paths.js\";\nimport {\n REFRESH_ARG,\n refreshUpdateCache,\n startUpdateCheck,\n} from \"../update/notifier.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../../package.json\") as { version: string };\n\n// The detached update check re-runs this entry point; it only refreshes the\n// cache, without loading plugins or parsing commands.\nif (process.argv[2] === REFRESH_ARG) {\n await refreshUpdateCache(getConfigDir());\n process.exit(0);\n}\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// Discovery: the command catalog and shell completion\nprogram.addCommand(commandsCommand);\nprogram.addCommand(completionCommand);\nprogram.addCommand(completeCommand, { hidden: true });\n\nstartUpdateCheck({\n currentVersion: version,\n configDir: getConfigDir(),\n env: process.env,\n stdoutIsTTY: Boolean(process.stdout.isTTY),\n stderrIsTTY: Boolean(process.stderr.isTTY),\n entryPath: realpathSync(fileURLToPath(import.meta.url)),\n versions: process.versions,\n});\n\n// Discover and load all plugins (auto-discovered from node_modules and workspace)\nawait loadPlugins(program, packageRoot);\n\nprogram.parse(process.argv, { from: \"node\" });\n"],"mappings":"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"}
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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","../src/walk/prompter.ts","../src/git-sync/commit-subject.ts","../src/git-sync/change-summary.ts","../src/git-sync/actor-cache.ts","../src/catalog/annotations.ts","../src/catalog/walk.ts"],"mappings":";;;;;;UAIiB,YAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAFA;EAAA,SAIA,SAAA;EAFA;;;;;;EAAA,SASA,YAAA;IAAA,SACE,IAAA;IAAA,SACA,QAAA,iBAMe;IAAA,SAJf,QAAA;EAAA;AAAA;AAAA,UAII,WAAA;EACf,aAAA;EACA,QAAA,EAAU,MAAA,SAAe,YAAA;EACzB,OAAA,EAAS,MAAA;EAFT;;;;;;;;;;EAaA,cAAA;EAOc;;;EAHd,2BAAA,GAA8B,MAAA;AAAA;AAAA,iBAGhB,mBAAA,CAAA,GAAuB,WAAA;;;;;;AA3CvC;;;;;;;iBCUgB,YAAA,CAAA;AAAA,iBAoBA,iBAAA,CAAA;;;iBCjBA,UAAA,CAAA,GAAc,WAAA;AAAA,iBAwBd,WAAA,CAAY,MAAA,EAAQ,WAAA;AAAA,iBA6FpB,YAAA,CACd,OAAA,GAAU,MAAA,EAAQ,WAAA,KAAgB,WAAA,GACjC,WAAA;;;;;;AFpIH;;;;;;;;UGWiB,eAAA;EAAA,SACN,OAAA;AAAA;;;;;;AHUX;;;;iBGOgB,iBAAA,CAAkB,QAAA,WAAmB,eAAA;;;;;;AH7BrD;UIAiB,QAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA;AAAA;;;;;;AJHX;;;
|
|
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","../src/walk/prompter.ts","../src/git-sync/commit-subject.ts","../src/git-sync/change-summary.ts","../src/git-sync/actor-cache.ts","../src/catalog/annotations.ts","../src/catalog/walk.ts"],"mappings":";;;;;;UAIiB,YAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAFA;EAAA,SAIA,SAAA;EAFA;;;;;;EAAA,SASA,YAAA;IAAA,SACE,IAAA;IAAA,SACA,QAAA,iBAMe;IAAA,SAJf,QAAA;EAAA;AAAA;AAAA,UAII,WAAA;EACf,aAAA;EACA,QAAA,EAAU,MAAA,SAAe,YAAA;EACzB,OAAA,EAAS,MAAA;EAFT;;;;;;;;;;EAaA,cAAA;EAOc;;;EAHd,2BAAA,GAA8B,MAAA;AAAA;AAAA,iBAGhB,mBAAA,CAAA,GAAuB,WAAA;;;;;;AA3CvC;;;;;;;iBCUgB,YAAA,CAAA;AAAA,iBAoBA,iBAAA,CAAA;;;iBCjBA,UAAA,CAAA,GAAc,WAAA;AAAA,iBAwBd,WAAA,CAAY,MAAA,EAAQ,WAAA;AAAA,iBA6FpB,YAAA,CACd,OAAA,GAAU,MAAA,EAAQ,WAAA,KAAgB,WAAA,GACjC,WAAA;;;;;;AFpIH;;;;;;;;UGWiB,eAAA;EAAA,SACN,OAAA;AAAA;;;;;;AHUX;;;;iBGOgB,iBAAA,CAAkB,QAAA,WAAmB,eAAA;;;;;;AH7BrD;UIAiB,QAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA;AAAA;;;;;;AJHX;;;UKKiB,OAAA;EAAA,SACN,OAAA;EAAA,SACA,KAAA,EAAO,CAAA;AAAA;AAAA,UAGD,OAAA;EAAA,SACN,OAAA;EAAA,SACA,KAAA,EAAO,CAAA;AAAA;AAAA,KAGN,MAAA,QAAc,KAAA,IAAS,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;AAAA,iBAIxC,OAAA,GAAA,CAAW,KAAA,EAAO,CAAA,GAAI,OAAA,CAAQ,CAAA;AAAA,iBAI9B,OAAA,GAAA,CAAW,KAAA,EAAO,CAAA,GAAI,OAAA,CAAQ,CAAA;AAAA,iBAM9B,SAAA,MAAA,CAAgB,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,IAAU,OAAA,CAAQ,CAAA;AAAA,iBAIzD,SAAA,MAAA,CAAgB,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,IAAU,OAAA,CAAQ,CAAA;AAAA,iBAMzD,QAAA,GAAA,CAAY,EAAA,QAAU,CAAA,GAAI,MAAA,CAAO,CAAA,EAAG,KAAA;AAAA,iBAQ9B,aAAA,GAAA,CACpB,EAAA,QAAU,OAAA,CAAQ,CAAA,IACjB,OAAA,CAAQ,MAAA,CAAO,CAAA,EAAG,KAAA;AAAA,iBAQL,MAAA,MAAA,CAAa,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,CAAA;AAAA,iBAYpC,QAAA,MAAA,CAAe,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,GAAI,YAAA,EAAc,CAAA,GAAI,CAAA;AAAA,iBAKvD,SAAA,SAAA,CACd,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,GAClB,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,CAAA,GACjB,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBAKG,QAAA,SAAA,CACd,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,GAClB,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,CAAA,GACjB,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBAOG,OAAA,CAAQ,KAAA,YAAiB,KAAA,IAAS,KAAA;AAAA,iBAIlC,WAAA,CAAY,KAAA,YAAiB,KAAA,IAAS,MAAA,CAAO,cAAA;AAAA,iBAI7C,eAAA,CAAgB,KAAA;;;iBC/FhB,eAAA,CAAA;AAAA,UAUC,aAAA,SAAsB,QAAA;EAAA,SAC5B,IAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAyCM,WAAA;EAAA,SACN,IAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,EAAA;EAAA,SACA,IAAA;AAAA;AAAA,UAGM,mBAAA;EAAA,SACN,SAAA;EAAA,SACA,WAAA;EAAA,SACA,SAAA;EAAA,SACA,GAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,IAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,aAAA;EAAA,SACN,EAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,GAAA;AAAA;AAAA,UAGM,kBAAA;EAAA,SACN,QAAA;EAAA,SACA,SAAA,EAAW,aAAA;AAAA;;;AJyCtB;iBInCsB,aAAA,CACpB,KAAA,WACC,OAAA,CAAQ,MAAA,CAAO,WAAA,EAAa,aAAA;;;;;iBA0DT,OAAA,CACpB,KAAA,WACC,OAAA,CAAQ,MAAA,CAAO,WAAA,EAAa,aAAA;;;;iBAoDT,UAAA,CACpB,IAAA,UACA,IAAA,WACC,OAAA,CAAQ,MAAA,CAAO,kBAAA,EAAoB,aAAA;;;;iBA6EhB,kBAAA,CACpB,KAAA,UACA,OAAA,YACC,OAAA,CAAQ,MAAA,CAAO,WAAA,IAAe,aAAA;;;;AHzRjC;;;iBGyUsB,aAAA,CACpB,KAAA,UACA,SAAA,UACA,OAAA,YACC,OAAA,CAAQ,MAAA,CAAO,mBAAA,EAAqB,aAAA;;;;;;;;;;;;;;;;iBCvUvB,YAAA,CAAa,WAAA;;APK7B;;iBOwBgB,gBAAA,CAAA,GAAoB,YAAA;;;;iBAUpB,gBAAA,CAAA;;;UCnDC,aAAA;ERHN;EAAA,SQKA,OAAA,EAAS,OAAA;ERHT;EAAA,SQKA,YAAA;ERFA;EAAA,SQIA,SAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;EACT,QAAA,CAAS,GAAA,EAAK,aAAA,UAAuB,OAAA;AAAA;;;UCyCtB,cAAA;EACf,MAAA;EACA,OAAA,GAAU,MAAA;EACV,MAAA,GAAS,MAAA;EACT,IAAA;EACA,MAAA,GAAS,WAAA;EACT,QAAA,GAAW,WAAA;AAAA;AAAA,UA8GI,mBAAA;EACf,OAAA,wBACE,QAAA,UACA,OAAA,GAAU,cAAA,KACP,OAAA,CAAQ,SAAA;EACb,mBAAA,wBACE,QAAA,UACA,QAAA,EAAU,QAAA,EACV,OAAA,GAAU,IAAA,CAAK,cAAA;IACb,MAAA;EAAA,MAEC,OAAA,CAAQ,SAAA;EACb,GAAA,wBACE,QAAA,UACA,MAAA,GAAS,MAAA,mBACT,OAAA,GAAU,IAAA,CAAK,cAAA,2BACZ,OAAA,CAAQ,SAAA;EACb,IAAA,wBACE,QAAA,UACA,IAAA,YACA,OAAA,GAAU,IAAA,CAAK,cAAA,yBACZ,OAAA,CAAQ,SAAA;EACb,GAAA,wBACE,QAAA,UACA,IAAA,YACA,OAAA,GAAU,IAAA,CAAK,cAAA,yBACZ,OAAA,CAAQ,SAAA;EACb,KAAA,wBACE,QAAA,UACA,IAAA,YACA,OAAA,GAAU,IAAA,CAAK,cAAA,yBACZ,OAAA,CAAQ,SAAA;EACb,MAAA,wBACE,QAAA,UACA,OAAA,GAAU,IAAA,CAAK,cAAA,gBACZ,OAAA,CAAQ,SAAA;AAAA;AAAA,KA0ZH,WAAA,GAAc,mBAAA;;;UC3mBT,aAAA;EACf,MAAA;EACA,OAAA;EACA,EAAA;AAAA;AAAA,UAGe,cAAA;EACf,SAAA,IAAa,OAAA,CAAQ,WAAA;EVJZ;;EUOT,YAAA,CAAa,OAAA,WAAkB,OAAA,CAAQ,WAAA;EACvC,MAAA,CAAO,IAAA;EACP,SAAA,CAAU,GAAA;EACV,OAAA;AAAA;;;KCZG,UAAA,IAAc,MAAA,EAAQ,OAAA,EAAS,GAAA,EAAK,cAAA;AAAA,iBAEzB,mBAAA,CACd,IAAA,UACA,WAAA,UACA,QAAA,EAAU,UAAA,GACT,OAAA;;;iBCHa,oBAAA,CAAqB,IAAA;EACnC,KAAA;EACA,OAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;EACA,EAAA;EACA,OAAA;AAAA,IACE,cAAA;;;iBCdY,YAAA,CAAa,IAAA,WAAe,OAAA,EAAS,aAAA;;;KCIzC,GAAA;;;KAIA,KAAA;EAAU,KAAA;AAAA;EAAoB,GAAA,EAAK,GAAA;AAAA;AAAA,UAE9B,MAAA;EACf,KAAA;EACA,KAAA;EACA,WAAA;AAAA;AAAA,UAGe,QAAA;EdCJ;EcCX,OAAA;EACA,OAAA;EdCmB;EcCnB,IAAA;EACA,OAAA,GAAU,MAAA;EdEgB;EcA1B,OAAA;EACA,IAAA;EACA,SAAA;AAAA;AAAA,UAGe,QAAA;EACf,KAAA;EACA,KAAA,EAAO,CAAA;EACP,WAAA;EACA,QAAA;AAAA;AAAA,UAGe,QAAA;EACf,GAAA,CAAI,QAAA,EAAU,QAAA,GAAW,OAAA,CAAQ,KAAA;EdVxB;EcYT,IAAA,mBACE,OAAA,UACA,KAAA,EAAO,aAAA,CAAc,QAAA,CAAS,CAAA,KAC7B,OAAA,CAAQ,CAAA;EACX,IAAA,CAAK,IAAA;AAAA;AAAA,cAGM,QAAA;;iBAMG,UAAA,CAAW,GAAA,WAAc,KAAA;;iBAwBzB,gBAAA,CAAA,GAAoB,QAAA;;;iBA+FpB,iBAAA,CACd,WAAA,UACA,KAAA;;;;;;Ad3KF;;;;;;;;;;;;;;;;AAsBA;;;;;;;;;;;;UeMiB,YAAA;EfHf;EAAA,SeKS,IAAA;EfMT;EAAA,SeJS,QAAA;AAAA;;cAIE,iBAAA;AfOb;;;;;AAAA,UeIiB,oBAAA;EAAA,SACN,SAAA;EAAA,SACA,MAAA;EdvCK;EAAA,ScyCL,SAAA;EAAA,SACA,QAAA;AAAA;;iBAcK,qBAAA,CAAsB,IAAA,EAAM,IAAA;;;;;;;;AbrD5C;;;;;AAwBA;;;;iBaiDgB,eAAA,CAAgB,KAAA;Ab4ChC;;;;;;;;;;AAAA,iBa1BgB,oBAAA,CACd,MAAA,UACA,KAAA,EAAO,YAAA,SACP,IAAA,GAAM,IAAA;;;;;;;iBAyBQ,mBAAA,CACd,OAAA,8BACC,oBAAA;AZ3HH;;;;;AAAA,iBYgJgB,oBAAA,CACd,OAAA;;UAQQ,MAAA;EAAA,SACC,EAAA;EAAA,SACA,SAAA;EAAA,SACA,SAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA;AAAA;;UAID,UAAA,SAAmB,MAAA;EAAA,SAClB,IAAA,GAAO,MAAA;AAAA;;;;;;;iBAcF,kBAAA,CAAmB,GAAA,EAAK,UAAA,GAAa,YAAA;;;;;;Af7LrD;;;;;;;;;;;;;;;;AAsBA;;;;;;KgBAY,YAAA;;;;;;;;;;;;;;AhBqBZ;;;;KgBFY,aAAA,cAEN,OAAA,WAAkB,WAAA,gBAA2B,OAAA;AAAA,UAElC,WAAA;EAAA,SACN,MAAA,EAAQ,YAAA;;WAER,IAAA;AAAA;;;;AflBX;;iBe0BgB,oBAAA,CAAqB,IAAA,WAAe,YAAA;;;;;;Ad3CpD;;;;;iBckEgB,gBAAA,CAAiB,MAAA,WAAiB,WAAA;;;;;AdmDlD;;;;;;;iBcAgB,gBAAA,CACd,OAAA,WAAkB,WAAA,IAClB,SAAA;;;AhB9GF;;;;;AAAA,ciBCa,kBAAA;;UAGI,kBAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EjBJT;EAAA,SiBMS,QAAA;AAAA;;iBAYK,eAAA,CAAgB,GAAA,YAAmB,YAAA;;iBAcnC,gBAAA,CAAiB,KAAA,EAAO,YAAA,EAAc,GAAA,GAAG,IAAA;;;;AjBbzD;;;;;;;iBiBmDsB,mBAAA,CACpB,UAAA,QAAkB,OAAA,CAAQ,YAAA,WACzB,OAAA,CAAQ,YAAA;;;UCzFM,eAAA;EAAA,SACN,MAAA;ElBHA;EAAA,SkBKA,IAAA;AAAA;AAAA,UAGM,iBAAA;ElBGJ;EAAA,SkBDF,SAAA,YAAqB,eAAA;ElBGX;EAAA,SkBDV,MAAA;AAAA;;;;;iBAWK,eAAA,WAA0B,OAAA,CAAA,CACxC,OAAA,EAAS,CAAA,EACT,UAAA,EAAY,iBAAA,GACX,CAAA;;;UCzBc,aAAA;EAAA,SACN,KAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;EAAA,SACA,WAAA;EnBIA;EAAA,SmBFA,UAAA;EnBIE;EAAA,SmBFF,aAAA;EnBIU;EAAA,SmBFV,QAAA;EAAA,SACA,YAAA;AAAA;AAAA,UAGM,eAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;AAAA;AAAA,UAGM,YAAA;EnBJf;EAAA,SmBMS,OAAA;EAAA,SACA,IAAA;EAAA,SACA,WAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA,WAAoB,eAAA;EAAA,SACpB,OAAA,WAAkB,aAAA;EnBM3B;EAAA,SmBJS,mBAAA;EAAA,SACA,WAAA;EnBG2B;EAAA,SmBD3B,QAAA;EnBIwB;EAAA,SmBFxB,MAAA;EnBE4B;EAAA,SmBA5B,SAAA,WAAoB,eAAA;AAAA;AAAA,iBAoDf,YAAA,CAAa,IAAA,EAAM,OAAA,GAAU,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-6SEVoGMw.mjs","names":[],"sources":["../src/catalog/annotations.ts","../src/catalog/walk.ts","../src/utils/result.ts","../src/auth/fluid-api.ts","../src/config/types.ts","../src/config/paths.ts","../src/config/config.ts","../src/config/project-config.ts","../src/auth/token.ts"],"sourcesContent":["/**\n * What a command does to the API, for the command catalog.\n *\n * Commander has nowhere to keep this, so it rides on the command object\n * under a registered symbol. A plugin installed with its own copy of\n * @fluid-app/fluid-cli still writes to the same key the running CLI reads,\n * which a module-scoped WeakMap would not.\n */\n\nimport type { Command } from \"commander\";\n\nexport interface CommandEndpoint {\n readonly method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n /** Path as the API documents it, e.g. `/api/domains/{id}`. */\n readonly path: string;\n}\n\nexport interface CommandAnnotation {\n /** The endpoints the command calls. */\n readonly endpoints?: readonly CommandEndpoint[];\n /** Whether the command changes data. */\n readonly writes?: boolean;\n}\n\nconst ANNOTATION = Symbol.for(\"@fluid-app/fluid-cli.command-annotation\");\n\ntype Annotated = Command & { [ANNOTATION]?: CommandAnnotation };\n\n/**\n * Record the endpoints a command calls and whether it writes, so\n * `fluid commands --json` can list them. Returns the command for chaining.\n */\nexport function describeCommand<T extends Command>(\n command: T,\n annotation: CommandAnnotation,\n): T {\n const target = command as T & Annotated;\n target[ANNOTATION] = { ...target[ANNOTATION], ...annotation };\n return command;\n}\n\nexport function readAnnotation(command: Command): CommandAnnotation | null {\n return (command as Annotated)[ANNOTATION] ?? null;\n}\n","/**\n * Walk the loaded command tree into a plain catalog: every command, its\n * arguments and flags, and what it does to the API. `fluid commands` prints\n * it and shell completion reads the same tree, so neither can drift from\n * the commands a user actually has installed.\n */\n\nimport type { Argument, Command, Option } from \"commander\";\nimport { readAnnotation, type CommandEndpoint } from \"./annotations.js\";\n\nexport interface CatalogOption {\n readonly flags: string;\n readonly long: string | null;\n readonly short: string | null;\n readonly description: string;\n /** `<value>` must follow the flag. */\n readonly takesValue: boolean;\n /** `[value]` may follow the flag. */\n readonly optionalValue: boolean;\n /** The command refuses to run without it. */\n readonly required: boolean;\n readonly defaultValue: unknown;\n}\n\nexport interface CatalogArgument {\n readonly name: string;\n readonly description: string;\n readonly required: boolean;\n readonly variadic: boolean;\n}\n\nexport interface CatalogEntry {\n /** Full invocation, e.g. `fluid domains add`. */\n readonly command: string;\n readonly path: readonly string[];\n readonly description: string;\n readonly aliases: readonly string[];\n readonly arguments: readonly CatalogArgument[];\n readonly options: readonly CatalogOption[];\n /** Ancestors whose flags this command also accepts, e.g. `fluid admin`'s `--token`. */\n readonly inheritsOptionsFrom: readonly string[];\n readonly subcommands: readonly string[];\n /** False for a group that only holds subcommands. */\n readonly runnable: boolean;\n /** True or false when the plugin says so or the command takes `--yes`; null when unknown. */\n readonly writes: boolean | null;\n /** The endpoints the plugin declared; null when it declared none, which means unknown, not none. */\n readonly endpoints: readonly CommandEndpoint[] | null;\n}\n\n// Commander keeps these two private; they have been stable since v7.\ntype CommandInternals = { _hidden?: boolean; _actionHandler?: unknown };\n\nexport function isHidden(command: Command): boolean {\n return (command as unknown as CommandInternals)._hidden === true;\n}\n\nfunction isRunnable(command: Command): boolean {\n return (command as unknown as CommandInternals)._actionHandler != null;\n}\n\nexport function visibleSubcommands(command: Command): Command[] {\n return command.commands.filter((sub) => !isHidden(sub));\n}\n\nexport function visibleOptions(command: Command): Option[] {\n return command.options.filter((option) => !option.hidden);\n}\n\nfunction describeOption(option: Option): CatalogOption {\n return {\n flags: option.flags,\n long: option.long ?? null,\n short: option.short ?? null,\n description: option.description,\n takesValue: option.required,\n optionalValue: option.optional,\n required: option.mandatory,\n defaultValue: option.defaultValue,\n };\n}\n\nfunction describeArgument(argument: Argument): CatalogArgument {\n return {\n name: argument.name(),\n description: argument.description,\n required: argument.required,\n variadic: argument.variadic,\n };\n}\n\nfunction inferWrites(command: Command, declared: boolean | undefined) {\n if (declared !== undefined) return declared;\n // Commands that change live data confirm with --yes across the plugins.\n return command.options.some((option) => option.long === \"--yes\")\n ? true\n : null;\n}\n\nexport function walkCommands(root: Command): CatalogEntry[] {\n const entries: CatalogEntry[] = [];\n\n function visit(command: Command, path: string[], ancestors: Command[]) {\n const annotation = readAnnotation(command);\n const subcommands = visibleSubcommands(command);\n entries.push({\n command: path.join(\" \"),\n path,\n description: command.description(),\n aliases: command.aliases(),\n arguments: command.registeredArguments.map(describeArgument),\n options: visibleOptions(command).map(describeOption),\n inheritsOptionsFrom: ancestors\n .filter((ancestor) =>\n // The program's --version is not a flag a subcommand takes.\n visibleOptions(ancestor).some(\n (option) => option.long !== \"--version\",\n ),\n )\n .map((ancestor) => commandPath(ancestor).join(\" \")),\n subcommands: subcommands.map((sub) => sub.name()),\n runnable: isRunnable(command),\n writes: inferWrites(command, annotation?.writes),\n endpoints: annotation?.endpoints ?? null,\n });\n for (const sub of subcommands) {\n visit(sub, [...path, sub.name()], [...ancestors, command]);\n }\n }\n\n visit(root, [root.name()], []);\n return entries;\n}\n\nexport function rootCommand(command: Command): Command {\n let root = command;\n while (root.parent) root = root.parent;\n return root;\n}\n\nexport function commandPath(command: Command): string[] {\n const path: string[] = [];\n for (let c: Command | null = command; c; c = c.parent) {\n path.unshift(c.name());\n }\n return path;\n}\n\n/** Keep the entries at or under `prefix`, matching names or aliases. */\nexport function filterCatalog(\n entries: readonly CatalogEntry[],\n root: Command,\n prefix: readonly string[],\n): CatalogEntry[] {\n let target = root;\n for (const word of prefix) {\n const next = visibleSubcommands(target).find(\n (sub) => sub.name() === word || sub.aliases().includes(word),\n );\n if (!next) return [];\n target = next;\n }\n const base = commandPath(target);\n return entries.filter((entry) =>\n base.every((word, index) => entry.path[index] === word),\n );\n}\n","/**\n * Result type utilities for type-safe error handling\n *\n * The Result<T, E> pattern provides a discriminated union for fallible operations,\n * enabling exhaustive handling without try/catch blocks.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Result type - discriminated union for success/failure\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Success<T> {\n readonly success: true;\n readonly value: T;\n}\n\nexport interface Failure<E> {\n readonly success: false;\n readonly error: E;\n}\n\nexport type Result<T, E = Error> = Success<T> | Failure<E>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constructor functions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function success<T>(value: T): Success<T> {\n return { success: true, value };\n}\n\nexport function failure<E>(error: E): Failure<E> {\n return { success: false, error };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Type guards\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function isSuccess<T, E>(result: Result<T, E>): result is Success<T> {\n return result.success === true;\n}\n\nexport function isFailure<T, E>(result: Result<T, E>): result is Failure<E> {\n return result.success === false;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utility functions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function tryCatch<T>(fn: () => T): Result<T, Error> {\n try {\n return success(fn());\n } catch (error) {\n return failure(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\nexport async function tryCatchAsync<T>(\n fn: () => Promise<T>,\n): Promise<Result<T, Error>> {\n try {\n return success(await fn());\n } catch (error) {\n return failure(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\nexport function unwrap<T, E>(result: Result<T, E>): T {\n if (isSuccess(result)) return result.value;\n // Always throw an Error instance so V8 attaches a stack trace and\n // `instanceof Error` checks in catch blocks work as expected.\n if (result.error instanceof Error) throw result.error;\n throw new Error(\n typeof result.error === \"object\" && result.error !== null\n ? JSON.stringify(result.error)\n : String(result.error),\n );\n}\n\nexport function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {\n if (isSuccess(result)) return result.value;\n return defaultValue;\n}\n\nexport function mapResult<T, U, E>(\n result: Result<T, E>,\n fn: (value: T) => U,\n): Result<U, E> {\n if (isSuccess(result)) return success(fn(result.value));\n return result;\n}\n\nexport function mapError<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => F,\n): Result<T, F> {\n if (isFailure(result)) return failure(fn(result.error));\n return result;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error narrowing utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function isError(value: unknown): value is Error {\n return value instanceof Error;\n}\n\nexport function isNodeError(value: unknown): value is NodeJS.ErrnoException {\n return value instanceof Error && \"code\" in value;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n return String(error);\n}\n","/**\n * Fluid API client for authentication operations\n */\n\nimport type { CliError } from \"../utils/errors.js\";\nimport { type Result, success, failure } from \"../utils/result.js\";\n\nconst API_TIMEOUT_MS = 10_000;\n\nexport function getFluidApiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction makeSignal(): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\n return { signal: controller.signal, cleanup: () => clearTimeout(timer) };\n}\n\nexport interface FluidApiError extends CliError {\n readonly code: string;\n readonly message: string;\n readonly details?: string;\n}\n\nexport const FLUID_API_ERROR = {\n INVALID_TOKEN: {\n code: \"INVALID_TOKEN\",\n message: \"Token is invalid or expired\",\n },\n API_UNREACHABLE: {\n code: \"API_UNREACHABLE\",\n message: \"Could not reach the Fluid API\",\n },\n UUID_NOT_FOUND: {\n code: \"UUID_NOT_FOUND\",\n message: \"Verification session not found\",\n },\n CODE_EXPIRED: {\n code: \"CODE_EXPIRED\",\n message: \"Verification code has expired — please request a new one\",\n },\n INVALID_CODE: {\n code: \"INVALID_CODE\",\n message: \"Invalid verification code\",\n },\n COMPANY_NOT_FOUND: {\n code: \"COMPANY_NOT_FOUND\",\n message: \"Company not found\",\n },\n SWITCH_FAILED: {\n code: \"SWITCH_FAILED\",\n message: \"Failed to switch company\",\n },\n} as const;\n\nfunction createApiError(\n template: { readonly code: string; readonly message: string },\n details?: string,\n): FluidApiError {\n return { code: template.code, message: template.message, details };\n}\n\nexport interface CompanyInfo {\n readonly name: string;\n}\n\nexport interface UserCompany {\n readonly id: number;\n readonly name: string;\n}\n\nexport interface SwitchCompanyResult {\n readonly companyId: number;\n readonly companyName: string;\n readonly fluidShop: string;\n readonly jwt: string;\n}\n\nexport interface MfaResponse {\n readonly uuid: string;\n readonly expiresAt: string;\n}\n\nexport interface CompanyChoice {\n readonly id: number;\n readonly name: string;\n readonly shopName: string;\n readonly jwt: string;\n}\n\nexport interface ConfirmMfaResponse {\n readonly authType: string;\n readonly companies: CompanyChoice[];\n}\n\n/**\n * Validate a token against the Fluid API and return company info\n */\nexport async function validateToken(\n token: string,\n): Promise<Result<CompanyInfo, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/company/v1/companies/me`,\n {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n data?: { company?: { name?: string } };\n };\n const name = data?.data?.company?.name;\n if (!name) {\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n \"Unexpected response shape from /companies/me\",\n ),\n );\n }\n return success({ name });\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}: Check that your token is valid and non-expired.`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Send a multi-factor authentication code to the given email address.\n * Always returns 201 from the API (anti-enumeration).\n */\nexport async function sendMfa(\n email: string,\n): Promise<Result<MfaResponse, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/authentication/send_mfa`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n signal,\n },\n );\n\n if (response.status === 201) {\n const data = (await response.json()) as {\n multi_factor_authentication?: {\n uuid?: string;\n verification_code_expires_at?: string;\n };\n };\n const uuid = data.multi_factor_authentication?.uuid;\n const expiresAt =\n data.multi_factor_authentication?.verification_code_expires_at;\n if (!uuid || !expiresAt) {\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n \"Unexpected response shape from /send_mfa\",\n ),\n );\n }\n return success({ uuid, expiresAt });\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Confirm a multi-factor authentication code and retrieve company JWTs.\n */\nexport async function confirmMfa(\n uuid: string,\n code: string,\n): Promise<Result<ConfirmMfaResponse, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/authentication/confirm_mfa`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n uuid,\n multi_factor_authentication: { verification_code: code },\n }),\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n authenticated?: boolean;\n auth_type?: string;\n companies?:\n | {\n id: number;\n name: string;\n shop_name: string;\n jwt: string;\n }[]\n | null;\n };\n if (!data.authenticated) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_CODE,\n \"Authentication was not confirmed by the server\",\n ),\n );\n }\n const companies = Array.isArray(data.companies) ? data.companies : [];\n return success({\n authType: data.auth_type ?? \"\",\n companies: companies.map((c) => ({\n id: c.id,\n name: c.name,\n shopName: c.shop_name,\n jwt: c.jwt,\n })),\n });\n }\n\n if (response.status === 404) {\n return failure(createApiError(FLUID_API_ERROR.UUID_NOT_FOUND));\n }\n if (response.status === 410) {\n return failure(createApiError(FLUID_API_ERROR.CODE_EXPIRED));\n }\n if (response.status === 422) {\n return failure(createApiError(FLUID_API_ERROR.INVALID_CODE));\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Fetch all companies the authenticated user has access to.\n */\nexport async function fetchUserCompanies(\n token: string,\n baseUrl?: string,\n): Promise<Result<UserCompany[], FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n const apiBase = baseUrl ?? getFluidApiBase();\n try {\n const response = await fetch(`${apiBase}/api/me`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${token}` },\n signal,\n });\n\n if (response.ok) {\n const data = (await response.json()) as {\n companies?: { id: number; name: string }[];\n };\n const companies = Array.isArray(data.companies) ? data.companies : [];\n return success(companies.map((c) => ({ id: c.id, name: c.name })));\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Switch to a different company and receive a new JWT.\n *\n * Response shape (from @fluid-app/auth SwitchCompanyResponse):\n * { company: { id, name, fluid_shop, jwt }, meta: { request_id, timestamp } }\n */\nexport async function switchCompany(\n token: string,\n companyId: number,\n baseUrl?: string,\n): Promise<Result<SwitchCompanyResult, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n const apiBase = baseUrl ?? getFluidApiBase();\n try {\n const response = await fetch(\n `${apiBase}/api/authentication/company/${companyId}/switch`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n company?: {\n id: number;\n name: string;\n fluid_shop: string;\n jwt: string;\n };\n };\n const company = data.company;\n if (!company?.jwt) {\n return failure(\n createApiError(\n FLUID_API_ERROR.SWITCH_FAILED,\n \"Unexpected response shape from /switch\",\n ),\n );\n }\n return success({\n companyId: company.id,\n companyName: company.name,\n fluidShop: company.fluid_shop,\n jwt: company.jwt,\n });\n }\n\n if (response.status === 404) {\n return failure(\n createApiError(\n FLUID_API_ERROR.COMPANY_NOT_FOUND,\n `Company ${companyId} not found`,\n ),\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.SWITCH_FAILED,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n","/**\n * Configuration types for the Fluid CLI\n */\n\nexport interface FluidProfile {\n readonly name: string;\n readonly token: string;\n readonly companyName: string;\n readonly storedAt: string; // ISO-8601\n readonly baseUrl?: string;\n /** Stable storefront identity written by Mist and newer auth endpoints. */\n readonly fluidShop?: string;\n /**\n * Cached commit identity for GitSync subjects, so a publish doesn't\n * pay for `/api/me` to learn a name that changes approximately never.\n * Scoped to the profile (token + company), and dropped whenever login\n * or a company switch rewrites it. See git-sync/actor-cache.ts.\n */\n readonly gitSyncActor?: {\n readonly name: string;\n readonly publicId: string | null;\n /** ISO-8601. */\n readonly cachedAt: string;\n };\n}\n\nexport interface FluidConfig {\n activeProfile: string | null;\n profiles: Record<string, FluidProfile>;\n plugins: Record<string, unknown>;\n /**\n * Allow-list of plugin package names.\n *\n * - `null` (default) — auto-discover and load all `@fluid-app/fluid-cli-*`\n * and `*-cli-commands` packages found in `node_modules` or the pnpm\n * workspace. Only `@fluid-app`-scoped packages matching the naming\n * convention are eligible; no third-party code is loaded.\n * - `string[]` — only load plugins whose names appear in the array.\n * Set to `[]` to disable all plugins.\n */\n enabledPlugins: string[] | null;\n /** Historical local workspace slugs, keyed by stable storefront slug.\n * Optional because older CLI versions and hand-written test configs do not\n * know about Mist's local workspace compatibility metadata. */\n workspaceAliasesByFluidShop?: Record<string, string[]>;\n}\n\nexport function createDefaultConfig(): FluidConfig {\n return {\n activeProfile: null,\n profiles: {},\n plugins: {},\n enabledPlugins: null, // auto-discover all plugins\n };\n}\n","/**\n * XDG-compliant config directory resolution\n *\n * Priority:\n * 1. FLUID_CONFIG_DIR env var (explicit override)\n * 2. ~/.fluid/ on macOS (convention for CLI tools)\n * 3. %APPDATA%/fluid/ on Windows\n * 4. $XDG_CONFIG_HOME/fluid/ on Linux (XDG spec)\n * 5. ~/.config/fluid/ fallback on Linux\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function getConfigDir(): string {\n const envOverride = process.env[\"FLUID_CONFIG_DIR\"];\n if (envOverride) return envOverride;\n\n if (process.platform === \"darwin\") {\n return join(homedir(), \".fluid\");\n }\n\n if (process.platform === \"win32\") {\n const appData =\n process.env[\"APPDATA\"] ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"fluid\");\n }\n\n const xdgConfig = process.env[\"XDG_CONFIG_HOME\"];\n if (xdgConfig) return join(xdgConfig, \"fluid\");\n\n return join(homedir(), \".config\", \"fluid\");\n}\n\nexport function getConfigFilePath(): string {\n return join(getConfigDir(), \"config.json\");\n}\n","/**\n * Read/write config.json with atomic writes and safe defaults\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { type FluidConfig, createDefaultConfig } from \"./types.js\";\nimport { getConfigFilePath } from \"./paths.js\";\n\nexport function readConfig(): FluidConfig {\n const configPath = getConfigFilePath();\n\n if (!existsSync(configPath)) {\n return createDefaultConfig();\n }\n\n const raw = readFileSync(configPath, \"utf8\");\n\n let parsed: Partial<FluidConfig>;\n try {\n parsed = JSON.parse(raw) as Partial<FluidConfig>;\n } catch {\n // Corrupted config — fall back to defaults rather than crashing\n return createDefaultConfig();\n }\n\n // Merge with defaults to handle schema evolution\n return withDurableWorkspaceAliases({\n ...createDefaultConfig(),\n ...parsed,\n });\n}\n\nexport function writeConfig(config: FluidConfig): void {\n const configPath = getConfigFilePath();\n const dir = dirname(configPath);\n\n // 0o700: owner-only access so other users cannot enumerate the config dir\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Atomic write: write to a temp file in the same directory, then rename.\n // rename() is a POSIX atomic operation — a crash mid-write will never leave\n // a partially-written config.json. The temp file must be on the same\n // filesystem as the destination for rename() to succeed.\n const suffix = randomBytes(6).toString(\"hex\");\n const tmpPath = join(dir, `.fluid-config-${suffix}.tmp`);\n\n try {\n writeFileSync(\n tmpPath,\n JSON.stringify(withDurableWorkspaceAliases(config), null, 2) + \"\\n\",\n {\n encoding: \"utf8\",\n // NOTE: mode: 0o600 restricts to owner read/write on POSIX (macOS, Linux).\n // On Windows, Node.js ignores this option — NTFS permissions are not\n // set via the mode parameter. Windows file protection would require\n // icacls or Windows security descriptor APIs.\n mode: 0o600,\n },\n );\n renameSync(tmpPath, configPath);\n } catch (err) {\n // Clean up the temp file if something went wrong before the rename\n try {\n unlinkSync(tmpPath);\n } catch {\n // ignore cleanup errors — the temp file may not exist yet\n }\n throw err;\n }\n}\n\nfunction workspaceSlug(value: string): string {\n return value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction storefrontSlug(value: string): string {\n const withoutScheme = value.trim().replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, \"\");\n const host = withoutScheme.split(/[/?#]/, 1)[0]?.replace(/:\\d+$/, \"\") ?? \"\";\n return workspaceSlug(host.replace(/\\.fluid\\.app$/i, \"\"));\n}\n\n/** Preserve rename history even when `fluid logout` clears the profiles that\n * originally carried it. Derivation on read migrates existing config files;\n * normalization on every write makes the migration durable before profiles\n * are removed. */\nfunction withDurableWorkspaceAliases(config: FluidConfig): FluidConfig {\n const aliases: Record<string, string[]> = {};\n const add = (storefront: string, candidates: readonly string[]): void => {\n const key = storefrontSlug(storefront);\n if (!key) return;\n const values = new Set(aliases[key] ?? []);\n for (const candidate of candidates) {\n const slug = workspaceSlug(candidate);\n if (slug) values.add(slug);\n }\n if (values.size > 0) aliases[key] = [...values];\n };\n\n for (const [storefront, candidates] of Object.entries(\n config.workspaceAliasesByFluidShop ?? {},\n )) {\n if (!Array.isArray(candidates)) continue;\n add(\n storefront,\n candidates.filter(\n (candidate): candidate is string => typeof candidate === \"string\",\n ),\n );\n }\n for (const profile of Object.values(config.profiles)) {\n if (profile.fluidShop) {\n add(profile.fluidShop, [profile.name, profile.companyName]);\n }\n }\n\n return Object.keys(aliases).length > 0 ||\n config.workspaceAliasesByFluidShop !== undefined\n ? { ...config, workspaceAliasesByFluidShop: aliases }\n : config;\n}\n\nexport function updateConfig(\n updater: (config: FluidConfig) => FluidConfig,\n): FluidConfig {\n const config = readConfig();\n const updated = updater(config);\n writeConfig(updated);\n return updated;\n}\n","/**\n * Project-level config (.fluidrc) reader.\n *\n * Walks upward from a starting directory to find a `.fluidrc` file.\n * The file is JSON with a `profile` field that maps to a profile name\n * in `~/.fluid/config.json`.\n *\n * This allows portal repos to pin themselves to a specific CLI profile\n * so that `fluid portal pull` / `push` always use the correct company\n * credentials, regardless of which profile is globally active.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface ProjectRcConfig {\n readonly profile: string;\n}\n\nconst RC_FILENAME = \".fluidrc\";\n\n/** Cached result keyed by the starting directory. */\nlet cachedResult: { dir: string; config: ProjectRcConfig | null } | undefined;\n\n/**\n * Walk upward from `startDir` looking for a `.fluidrc` file.\n *\n * Returns the parsed config when found, or `null` if no valid `.fluidrc`\n * exists in any ancestor directory.\n *\n * The result is cached per starting directory for the lifetime of the\n * process — CLI commands are short-lived so stale-cache is not a concern.\n */\nexport function findProjectConfig(startDir: string): ProjectRcConfig | null {\n if (cachedResult && cachedResult.dir === startDir) {\n return cachedResult.config;\n }\n\n let dir = startDir;\n while (true) {\n const rcPath = join(dir, RC_FILENAME);\n if (existsSync(rcPath)) {\n try {\n const raw = readFileSync(rcPath, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<ProjectRcConfig>;\n if (typeof parsed.profile === \"string\" && parsed.profile.length > 0) {\n const config: ProjectRcConfig = { profile: parsed.profile };\n cachedResult = { dir: startDir, config };\n return config;\n }\n } catch {\n // Malformed .fluidrc — treat as absent\n }\n\n // Found a .fluidrc but it was invalid or empty — stop searching\n // upward to avoid confusing inheritance from a parent directory.\n cachedResult = { dir: startDir, config: null };\n return null;\n }\n\n const parent = dirname(dir);\n if (parent === dir) break; // reached filesystem root\n dir = parent;\n }\n\n cachedResult = { dir: startDir, config: null };\n return null;\n}\n\n/** Reset the cache. Exposed for tests only. */\nexport function _resetProjectConfigCache(): void {\n cachedResult = undefined;\n}\n","/**\n * Token storage and retrieval from config\n */\n\nimport { readConfig } from \"../config/config.js\";\nimport { findProjectConfig } from \"../config/project-config.js\";\nimport type { FluidProfile } from \"../config/types.js\";\n\n/**\n * Get the auth token for a named profile, or the active profile when omitted.\n *\n * Resolution order (when `profileName` is omitted — mirrors `resolveToken`\n * in domain/context.ts):\n * 1. `FLUID_TOKEN` / `FLUID_API_TOKEN` environment variables\n * 2. Project-level `.fluidrc` profile (walks upward from cwd)\n * 3. Global active profile from `~/.fluid/config.json`\n *\n * When `profileName` is provided explicitly (e.g. via `--profile` flag),\n * it takes precedence over the environment variables, `.fluidrc`, and the\n * active profile.\n */\nexport function getAuthToken(profileName?: string): string | null {\n const config = readConfig();\n\n // Explicit profile name takes precedence\n if (profileName) {\n const profile = config.profiles[profileName];\n return profile?.token ?? null;\n }\n\n // Environment variable override (before any stored profile)\n const envToken = process.env[\"FLUID_TOKEN\"] ?? process.env[\"FLUID_API_TOKEN\"];\n if (envToken) return envToken;\n\n // Project-level .fluidrc profile\n const projectConfig = findProjectConfig(process.cwd());\n if (projectConfig?.profile) {\n const profile = config.profiles[projectConfig.profile];\n if (profile) return profile.token;\n }\n\n // Fall back to global active profile\n if (!config.activeProfile) return null;\n const profile = config.profiles[config.activeProfile];\n return profile?.token ?? null;\n}\n\n/**\n * Get the active profile, or null if not logged in\n */\nexport function getActiveProfile(): FluidProfile | null {\n const config = readConfig();\n if (!config.activeProfile) return null;\n\n return config.profiles[config.activeProfile] ?? null;\n}\n\n/**\n * List all stored profile names\n */\nexport function listProfileNames(): string[] {\n const config = readConfig();\n return Object.keys(config.profiles);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"token-6SEVoGMw.mjs","names":[],"sources":["../src/catalog/annotations.ts","../src/catalog/walk.ts","../src/utils/result.ts","../src/auth/fluid-api.ts","../src/config/types.ts","../src/config/paths.ts","../src/config/config.ts","../src/config/project-config.ts","../src/auth/token.ts"],"sourcesContent":["/**\n * What a command does to the API, for the command catalog.\n *\n * Commander has nowhere to keep this, so it rides on the command object\n * under a registered symbol. A plugin installed with its own copy of\n * @fluid-app/fluid-cli still writes to the same key the running CLI reads,\n * which a module-scoped WeakMap would not.\n */\n\nimport type { Command } from \"commander\";\n\nexport interface CommandEndpoint {\n readonly method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n /** Path as the API documents it, e.g. `/api/domains/{id}`. */\n readonly path: string;\n}\n\nexport interface CommandAnnotation {\n /** The endpoints the command calls. */\n readonly endpoints?: readonly CommandEndpoint[];\n /** Whether the command changes data. */\n readonly writes?: boolean;\n}\n\nconst ANNOTATION = Symbol.for(\"@fluid-app/fluid-cli.command-annotation\");\n\ntype Annotated = Command & { [ANNOTATION]?: CommandAnnotation };\n\n/**\n * Record the endpoints a command calls and whether it writes, so\n * `fluid commands --json` can list them. Returns the command for chaining.\n */\nexport function describeCommand<T extends Command>(\n command: T,\n annotation: CommandAnnotation,\n): T {\n const target = command as T & Annotated;\n target[ANNOTATION] = { ...target[ANNOTATION], ...annotation };\n return command;\n}\n\nexport function readAnnotation(command: Command): CommandAnnotation | null {\n return (command as Annotated)[ANNOTATION] ?? null;\n}\n","/**\n * Walk the loaded command tree into a plain catalog: every command, its\n * arguments and flags, and what it does to the API. `fluid commands` prints\n * it and shell completion reads the same tree, so neither can drift from\n * the commands a user actually has installed.\n */\n\nimport type { Argument, Command, Option } from \"commander\";\nimport { readAnnotation, type CommandEndpoint } from \"./annotations.js\";\n\nexport interface CatalogOption {\n readonly flags: string;\n readonly long: string | null;\n readonly short: string | null;\n readonly description: string;\n /** `<value>` must follow the flag. */\n readonly takesValue: boolean;\n /** `[value]` may follow the flag. */\n readonly optionalValue: boolean;\n /** The command refuses to run without it. */\n readonly required: boolean;\n readonly defaultValue: unknown;\n}\n\nexport interface CatalogArgument {\n readonly name: string;\n readonly description: string;\n readonly required: boolean;\n readonly variadic: boolean;\n}\n\nexport interface CatalogEntry {\n /** Full invocation, e.g. `fluid domains add`. */\n readonly command: string;\n readonly path: readonly string[];\n readonly description: string;\n readonly aliases: readonly string[];\n readonly arguments: readonly CatalogArgument[];\n readonly options: readonly CatalogOption[];\n /** Ancestors whose flags this command also accepts, e.g. `fluid admin`'s `--token`. */\n readonly inheritsOptionsFrom: readonly string[];\n readonly subcommands: readonly string[];\n /** False for a group that only holds subcommands. */\n readonly runnable: boolean;\n /** True or false when the plugin says so or the command takes `--yes`; null when unknown. */\n readonly writes: boolean | null;\n /** The endpoints the plugin declared; null when it declared none, which means unknown, not none. */\n readonly endpoints: readonly CommandEndpoint[] | null;\n}\n\n// Commander keeps these two private; they have been stable since v7.\ntype CommandInternals = { _hidden?: boolean; _actionHandler?: unknown };\n\nexport function isHidden(command: Command): boolean {\n return (command as unknown as CommandInternals)._hidden === true;\n}\n\nfunction isRunnable(command: Command): boolean {\n return (command as unknown as CommandInternals)._actionHandler != null;\n}\n\nexport function visibleSubcommands(command: Command): Command[] {\n return command.commands.filter((sub) => !isHidden(sub));\n}\n\nexport function visibleOptions(command: Command): Option[] {\n return command.options.filter((option) => !option.hidden);\n}\n\nfunction describeOption(option: Option): CatalogOption {\n return {\n flags: option.flags,\n long: option.long ?? null,\n short: option.short ?? null,\n description: option.description,\n takesValue: option.required,\n optionalValue: option.optional,\n required: option.mandatory,\n defaultValue: option.defaultValue,\n };\n}\n\nfunction describeArgument(argument: Argument): CatalogArgument {\n return {\n name: argument.name(),\n description: argument.description,\n required: argument.required,\n variadic: argument.variadic,\n };\n}\n\nfunction inferWrites(command: Command, declared: boolean | undefined) {\n if (declared !== undefined) return declared;\n // Commands that change live data confirm with --yes across the plugins.\n return command.options.some((option) => option.long === \"--yes\")\n ? true\n : null;\n}\n\nexport function walkCommands(root: Command): CatalogEntry[] {\n const entries: CatalogEntry[] = [];\n\n function visit(command: Command, path: string[], ancestors: Command[]) {\n const annotation = readAnnotation(command);\n const subcommands = visibleSubcommands(command);\n entries.push({\n command: path.join(\" \"),\n path,\n description: command.description(),\n aliases: command.aliases(),\n arguments: command.registeredArguments.map(describeArgument),\n options: visibleOptions(command).map(describeOption),\n inheritsOptionsFrom: ancestors\n .filter((ancestor) =>\n // The program's --version is not a flag a subcommand takes.\n visibleOptions(ancestor).some(\n (option) => option.long !== \"--version\",\n ),\n )\n .map((ancestor) => commandPath(ancestor).join(\" \")),\n subcommands: subcommands.map((sub) => sub.name()),\n runnable: isRunnable(command),\n writes: inferWrites(command, annotation?.writes),\n endpoints: annotation?.endpoints ?? null,\n });\n for (const sub of subcommands) {\n visit(sub, [...path, sub.name()], [...ancestors, command]);\n }\n }\n\n visit(root, [root.name()], []);\n return entries;\n}\n\nexport function rootCommand(command: Command): Command {\n let root = command;\n while (root.parent) root = root.parent;\n return root;\n}\n\nexport function commandPath(command: Command): string[] {\n const path: string[] = [];\n for (let c: Command | null = command; c; c = c.parent) {\n path.unshift(c.name());\n }\n return path;\n}\n\n/** Keep the entries at or under `prefix`, matching names or aliases. */\nexport function filterCatalog(\n entries: readonly CatalogEntry[],\n root: Command,\n prefix: readonly string[],\n): CatalogEntry[] {\n let target = root;\n for (const word of prefix) {\n const next = visibleSubcommands(target).find(\n (sub) => sub.name() === word || sub.aliases().includes(word),\n );\n if (!next) return [];\n target = next;\n }\n const base = commandPath(target);\n return entries.filter((entry) =>\n base.every((word, index) => entry.path[index] === word),\n );\n}\n","/**\n * Result type utilities for type-safe error handling\n *\n * The Result<T, E> pattern provides a discriminated union for fallible operations,\n * enabling exhaustive handling without try/catch blocks.\n */\n\n// Result type - discriminated union for success/failure\n\nexport interface Success<T> {\n readonly success: true;\n readonly value: T;\n}\n\nexport interface Failure<E> {\n readonly success: false;\n readonly error: E;\n}\n\nexport type Result<T, E = Error> = Success<T> | Failure<E>;\n\n// Constructor functions\n\nexport function success<T>(value: T): Success<T> {\n return { success: true, value };\n}\n\nexport function failure<E>(error: E): Failure<E> {\n return { success: false, error };\n}\n\n// Type guards\n\nexport function isSuccess<T, E>(result: Result<T, E>): result is Success<T> {\n return result.success === true;\n}\n\nexport function isFailure<T, E>(result: Result<T, E>): result is Failure<E> {\n return result.success === false;\n}\n\n// Utility functions\n\nexport function tryCatch<T>(fn: () => T): Result<T, Error> {\n try {\n return success(fn());\n } catch (error) {\n return failure(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\nexport async function tryCatchAsync<T>(\n fn: () => Promise<T>,\n): Promise<Result<T, Error>> {\n try {\n return success(await fn());\n } catch (error) {\n return failure(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\nexport function unwrap<T, E>(result: Result<T, E>): T {\n if (isSuccess(result)) return result.value;\n // Always throw an Error instance so V8 attaches a stack trace and\n // `instanceof Error` checks in catch blocks work as expected.\n if (result.error instanceof Error) throw result.error;\n throw new Error(\n typeof result.error === \"object\" && result.error !== null\n ? JSON.stringify(result.error)\n : String(result.error),\n );\n}\n\nexport function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {\n if (isSuccess(result)) return result.value;\n return defaultValue;\n}\n\nexport function mapResult<T, U, E>(\n result: Result<T, E>,\n fn: (value: T) => U,\n): Result<U, E> {\n if (isSuccess(result)) return success(fn(result.value));\n return result;\n}\n\nexport function mapError<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => F,\n): Result<T, F> {\n if (isFailure(result)) return failure(fn(result.error));\n return result;\n}\n\n// Error narrowing utilities\n\nexport function isError(value: unknown): value is Error {\n return value instanceof Error;\n}\n\nexport function isNodeError(value: unknown): value is NodeJS.ErrnoException {\n return value instanceof Error && \"code\" in value;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n return String(error);\n}\n","/**\n * Fluid API client for authentication operations\n */\n\nimport type { CliError } from \"../utils/errors.js\";\nimport { type Result, success, failure } from \"../utils/result.js\";\n\nconst API_TIMEOUT_MS = 10_000;\n\nexport function getFluidApiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction makeSignal(): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\n return { signal: controller.signal, cleanup: () => clearTimeout(timer) };\n}\n\nexport interface FluidApiError extends CliError {\n readonly code: string;\n readonly message: string;\n readonly details?: string;\n}\n\nexport const FLUID_API_ERROR = {\n INVALID_TOKEN: {\n code: \"INVALID_TOKEN\",\n message: \"Token is invalid or expired\",\n },\n API_UNREACHABLE: {\n code: \"API_UNREACHABLE\",\n message: \"Could not reach the Fluid API\",\n },\n UUID_NOT_FOUND: {\n code: \"UUID_NOT_FOUND\",\n message: \"Verification session not found\",\n },\n CODE_EXPIRED: {\n code: \"CODE_EXPIRED\",\n message: \"Verification code has expired — please request a new one\",\n },\n INVALID_CODE: {\n code: \"INVALID_CODE\",\n message: \"Invalid verification code\",\n },\n COMPANY_NOT_FOUND: {\n code: \"COMPANY_NOT_FOUND\",\n message: \"Company not found\",\n },\n SWITCH_FAILED: {\n code: \"SWITCH_FAILED\",\n message: \"Failed to switch company\",\n },\n} as const;\n\nfunction createApiError(\n template: { readonly code: string; readonly message: string },\n details?: string,\n): FluidApiError {\n return { code: template.code, message: template.message, details };\n}\n\nexport interface CompanyInfo {\n readonly name: string;\n}\n\nexport interface UserCompany {\n readonly id: number;\n readonly name: string;\n}\n\nexport interface SwitchCompanyResult {\n readonly companyId: number;\n readonly companyName: string;\n readonly fluidShop: string;\n readonly jwt: string;\n}\n\nexport interface MfaResponse {\n readonly uuid: string;\n readonly expiresAt: string;\n}\n\nexport interface CompanyChoice {\n readonly id: number;\n readonly name: string;\n readonly shopName: string;\n readonly jwt: string;\n}\n\nexport interface ConfirmMfaResponse {\n readonly authType: string;\n readonly companies: CompanyChoice[];\n}\n\n/**\n * Validate a token against the Fluid API and return company info\n */\nexport async function validateToken(\n token: string,\n): Promise<Result<CompanyInfo, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/company/v1/companies/me`,\n {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n data?: { company?: { name?: string } };\n };\n const name = data?.data?.company?.name;\n if (!name) {\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n \"Unexpected response shape from /companies/me\",\n ),\n );\n }\n return success({ name });\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}: Check that your token is valid and non-expired.`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Send a multi-factor authentication code to the given email address.\n * Always returns 201 from the API (anti-enumeration).\n */\nexport async function sendMfa(\n email: string,\n): Promise<Result<MfaResponse, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/authentication/send_mfa`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n signal,\n },\n );\n\n if (response.status === 201) {\n const data = (await response.json()) as {\n multi_factor_authentication?: {\n uuid?: string;\n verification_code_expires_at?: string;\n };\n };\n const uuid = data.multi_factor_authentication?.uuid;\n const expiresAt =\n data.multi_factor_authentication?.verification_code_expires_at;\n if (!uuid || !expiresAt) {\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n \"Unexpected response shape from /send_mfa\",\n ),\n );\n }\n return success({ uuid, expiresAt });\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Confirm a multi-factor authentication code and retrieve company JWTs.\n */\nexport async function confirmMfa(\n uuid: string,\n code: string,\n): Promise<Result<ConfirmMfaResponse, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n try {\n const response = await fetch(\n `${getFluidApiBase()}/api/authentication/confirm_mfa`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n uuid,\n multi_factor_authentication: { verification_code: code },\n }),\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n authenticated?: boolean;\n auth_type?: string;\n companies?:\n | {\n id: number;\n name: string;\n shop_name: string;\n jwt: string;\n }[]\n | null;\n };\n if (!data.authenticated) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_CODE,\n \"Authentication was not confirmed by the server\",\n ),\n );\n }\n const companies = Array.isArray(data.companies) ? data.companies : [];\n return success({\n authType: data.auth_type ?? \"\",\n companies: companies.map((c) => ({\n id: c.id,\n name: c.name,\n shopName: c.shop_name,\n jwt: c.jwt,\n })),\n });\n }\n\n if (response.status === 404) {\n return failure(createApiError(FLUID_API_ERROR.UUID_NOT_FOUND));\n }\n if (response.status === 410) {\n return failure(createApiError(FLUID_API_ERROR.CODE_EXPIRED));\n }\n if (response.status === 422) {\n return failure(createApiError(FLUID_API_ERROR.INVALID_CODE));\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Fetch all companies the authenticated user has access to.\n */\nexport async function fetchUserCompanies(\n token: string,\n baseUrl?: string,\n): Promise<Result<UserCompany[], FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n const apiBase = baseUrl ?? getFluidApiBase();\n try {\n const response = await fetch(`${apiBase}/api/me`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${token}` },\n signal,\n });\n\n if (response.ok) {\n const data = (await response.json()) as {\n companies?: { id: number; name: string }[];\n };\n const companies = Array.isArray(data.companies) ? data.companies : [];\n return success(companies.map((c) => ({ id: c.id, name: c.name })));\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.API_UNREACHABLE,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n\n/**\n * Switch to a different company and receive a new JWT.\n *\n * Response shape (from @fluid-app/auth SwitchCompanyResponse):\n * { company: { id, name, fluid_shop, jwt }, meta: { request_id, timestamp } }\n */\nexport async function switchCompany(\n token: string,\n companyId: number,\n baseUrl?: string,\n): Promise<Result<SwitchCompanyResult, FluidApiError>> {\n const { signal, cleanup } = makeSignal();\n const apiBase = baseUrl ?? getFluidApiBase();\n try {\n const response = await fetch(\n `${apiBase}/api/authentication/company/${companyId}/switch`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n signal,\n },\n );\n\n if (response.ok) {\n const data = (await response.json()) as {\n company?: {\n id: number;\n name: string;\n fluid_shop: string;\n jwt: string;\n };\n };\n const company = data.company;\n if (!company?.jwt) {\n return failure(\n createApiError(\n FLUID_API_ERROR.SWITCH_FAILED,\n \"Unexpected response shape from /switch\",\n ),\n );\n }\n return success({\n companyId: company.id,\n companyName: company.name,\n fluidShop: company.fluid_shop,\n jwt: company.jwt,\n });\n }\n\n if (response.status === 404) {\n return failure(\n createApiError(\n FLUID_API_ERROR.COMPANY_NOT_FOUND,\n `Company ${companyId} not found`,\n ),\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n return failure(\n createApiError(\n FLUID_API_ERROR.INVALID_TOKEN,\n `HTTP ${response.status}`,\n ),\n );\n }\n\n const body = await response.text();\n return failure(\n createApiError(\n FLUID_API_ERROR.SWITCH_FAILED,\n `HTTP ${response.status}: ${body}`,\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure(createApiError(FLUID_API_ERROR.API_UNREACHABLE, message));\n } finally {\n cleanup();\n }\n}\n","/**\n * Configuration types for the Fluid CLI\n */\n\nexport interface FluidProfile {\n readonly name: string;\n readonly token: string;\n readonly companyName: string;\n readonly storedAt: string; // ISO-8601\n readonly baseUrl?: string;\n /** Stable storefront identity written by Mist and newer auth endpoints. */\n readonly fluidShop?: string;\n /**\n * Cached commit identity for GitSync subjects, so a publish doesn't\n * pay for `/api/me` to learn a name that changes approximately never.\n * Scoped to the profile (token + company), and dropped whenever login\n * or a company switch rewrites it. See git-sync/actor-cache.ts.\n */\n readonly gitSyncActor?: {\n readonly name: string;\n readonly publicId: string | null;\n /** ISO-8601. */\n readonly cachedAt: string;\n };\n}\n\nexport interface FluidConfig {\n activeProfile: string | null;\n profiles: Record<string, FluidProfile>;\n plugins: Record<string, unknown>;\n /**\n * Allow-list of plugin package names.\n *\n * - `null` (default) — auto-discover and load all `@fluid-app/fluid-cli-*`\n * and `*-cli-commands` packages found in `node_modules` or the pnpm\n * workspace. Only `@fluid-app`-scoped packages matching the naming\n * convention are eligible; no third-party code is loaded.\n * - `string[]` — only load plugins whose names appear in the array.\n * Set to `[]` to disable all plugins.\n */\n enabledPlugins: string[] | null;\n /** Historical local workspace slugs, keyed by stable storefront slug.\n * Optional because older CLI versions and hand-written test configs do not\n * know about Mist's local workspace compatibility metadata. */\n workspaceAliasesByFluidShop?: Record<string, string[]>;\n}\n\nexport function createDefaultConfig(): FluidConfig {\n return {\n activeProfile: null,\n profiles: {},\n plugins: {},\n enabledPlugins: null, // auto-discover all plugins\n };\n}\n","/**\n * XDG-compliant config directory resolution\n *\n * Priority:\n * 1. FLUID_CONFIG_DIR env var (explicit override)\n * 2. ~/.fluid/ on macOS (convention for CLI tools)\n * 3. %APPDATA%/fluid/ on Windows\n * 4. $XDG_CONFIG_HOME/fluid/ on Linux (XDG spec)\n * 5. ~/.config/fluid/ fallback on Linux\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function getConfigDir(): string {\n const envOverride = process.env[\"FLUID_CONFIG_DIR\"];\n if (envOverride) return envOverride;\n\n if (process.platform === \"darwin\") {\n return join(homedir(), \".fluid\");\n }\n\n if (process.platform === \"win32\") {\n const appData =\n process.env[\"APPDATA\"] ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"fluid\");\n }\n\n const xdgConfig = process.env[\"XDG_CONFIG_HOME\"];\n if (xdgConfig) return join(xdgConfig, \"fluid\");\n\n return join(homedir(), \".config\", \"fluid\");\n}\n\nexport function getConfigFilePath(): string {\n return join(getConfigDir(), \"config.json\");\n}\n","/**\n * Read/write config.json with atomic writes and safe defaults\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { type FluidConfig, createDefaultConfig } from \"./types.js\";\nimport { getConfigFilePath } from \"./paths.js\";\n\nexport function readConfig(): FluidConfig {\n const configPath = getConfigFilePath();\n\n if (!existsSync(configPath)) {\n return createDefaultConfig();\n }\n\n const raw = readFileSync(configPath, \"utf8\");\n\n let parsed: Partial<FluidConfig>;\n try {\n parsed = JSON.parse(raw) as Partial<FluidConfig>;\n } catch {\n // Corrupted config — fall back to defaults rather than crashing\n return createDefaultConfig();\n }\n\n // Merge with defaults to handle schema evolution\n return withDurableWorkspaceAliases({\n ...createDefaultConfig(),\n ...parsed,\n });\n}\n\nexport function writeConfig(config: FluidConfig): void {\n const configPath = getConfigFilePath();\n const dir = dirname(configPath);\n\n // 0o700: owner-only access so other users cannot enumerate the config dir\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Atomic write: write to a temp file in the same directory, then rename.\n // rename() is a POSIX atomic operation — a crash mid-write will never leave\n // a partially-written config.json. The temp file must be on the same\n // filesystem as the destination for rename() to succeed.\n const suffix = randomBytes(6).toString(\"hex\");\n const tmpPath = join(dir, `.fluid-config-${suffix}.tmp`);\n\n try {\n writeFileSync(\n tmpPath,\n JSON.stringify(withDurableWorkspaceAliases(config), null, 2) + \"\\n\",\n {\n encoding: \"utf8\",\n // NOTE: mode: 0o600 restricts to owner read/write on POSIX (macOS, Linux).\n // On Windows, Node.js ignores this option — NTFS permissions are not\n // set via the mode parameter. Windows file protection would require\n // icacls or Windows security descriptor APIs.\n mode: 0o600,\n },\n );\n renameSync(tmpPath, configPath);\n } catch (err) {\n // Clean up the temp file if something went wrong before the rename\n try {\n unlinkSync(tmpPath);\n } catch {\n // ignore cleanup errors — the temp file may not exist yet\n }\n throw err;\n }\n}\n\nfunction workspaceSlug(value: string): string {\n return value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction storefrontSlug(value: string): string {\n const withoutScheme = value.trim().replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, \"\");\n const host = withoutScheme.split(/[/?#]/, 1)[0]?.replace(/:\\d+$/, \"\") ?? \"\";\n return workspaceSlug(host.replace(/\\.fluid\\.app$/i, \"\"));\n}\n\n/** Preserve rename history even when `fluid logout` clears the profiles that\n * originally carried it. Derivation on read migrates existing config files;\n * normalization on every write makes the migration durable before profiles\n * are removed. */\nfunction withDurableWorkspaceAliases(config: FluidConfig): FluidConfig {\n const aliases: Record<string, string[]> = {};\n const add = (storefront: string, candidates: readonly string[]): void => {\n const key = storefrontSlug(storefront);\n if (!key) return;\n const values = new Set(aliases[key] ?? []);\n for (const candidate of candidates) {\n const slug = workspaceSlug(candidate);\n if (slug) values.add(slug);\n }\n if (values.size > 0) aliases[key] = [...values];\n };\n\n for (const [storefront, candidates] of Object.entries(\n config.workspaceAliasesByFluidShop ?? {},\n )) {\n if (!Array.isArray(candidates)) continue;\n add(\n storefront,\n candidates.filter(\n (candidate): candidate is string => typeof candidate === \"string\",\n ),\n );\n }\n for (const profile of Object.values(config.profiles)) {\n if (profile.fluidShop) {\n add(profile.fluidShop, [profile.name, profile.companyName]);\n }\n }\n\n return Object.keys(aliases).length > 0 ||\n config.workspaceAliasesByFluidShop !== undefined\n ? { ...config, workspaceAliasesByFluidShop: aliases }\n : config;\n}\n\nexport function updateConfig(\n updater: (config: FluidConfig) => FluidConfig,\n): FluidConfig {\n const config = readConfig();\n const updated = updater(config);\n writeConfig(updated);\n return updated;\n}\n","/**\n * Project-level config (.fluidrc) reader.\n *\n * Walks upward from a starting directory to find a `.fluidrc` file.\n * The file is JSON with a `profile` field that maps to a profile name\n * in `~/.fluid/config.json`.\n *\n * This allows portal repos to pin themselves to a specific CLI profile\n * so that `fluid portal pull` / `push` always use the correct company\n * credentials, regardless of which profile is globally active.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface ProjectRcConfig {\n readonly profile: string;\n}\n\nconst RC_FILENAME = \".fluidrc\";\n\n/** Cached result keyed by the starting directory. */\nlet cachedResult: { dir: string; config: ProjectRcConfig | null } | undefined;\n\n/**\n * Walk upward from `startDir` looking for a `.fluidrc` file.\n *\n * Returns the parsed config when found, or `null` if no valid `.fluidrc`\n * exists in any ancestor directory.\n *\n * The result is cached per starting directory for the lifetime of the\n * process — CLI commands are short-lived so stale-cache is not a concern.\n */\nexport function findProjectConfig(startDir: string): ProjectRcConfig | null {\n if (cachedResult && cachedResult.dir === startDir) {\n return cachedResult.config;\n }\n\n let dir = startDir;\n while (true) {\n const rcPath = join(dir, RC_FILENAME);\n if (existsSync(rcPath)) {\n try {\n const raw = readFileSync(rcPath, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<ProjectRcConfig>;\n if (typeof parsed.profile === \"string\" && parsed.profile.length > 0) {\n const config: ProjectRcConfig = { profile: parsed.profile };\n cachedResult = { dir: startDir, config };\n return config;\n }\n } catch {\n // Malformed .fluidrc — treat as absent\n }\n\n // Found a .fluidrc but it was invalid or empty — stop searching\n // upward to avoid confusing inheritance from a parent directory.\n cachedResult = { dir: startDir, config: null };\n return null;\n }\n\n const parent = dirname(dir);\n if (parent === dir) break; // reached filesystem root\n dir = parent;\n }\n\n cachedResult = { dir: startDir, config: null };\n return null;\n}\n\n/** Reset the cache. Exposed for tests only. */\nexport function _resetProjectConfigCache(): void {\n cachedResult = undefined;\n}\n","/**\n * Token storage and retrieval from config\n */\n\nimport { readConfig } from \"../config/config.js\";\nimport { findProjectConfig } from \"../config/project-config.js\";\nimport type { FluidProfile } from \"../config/types.js\";\n\n/**\n * Get the auth token for a named profile, or the active profile when omitted.\n *\n * Resolution order (when `profileName` is omitted — mirrors `resolveToken`\n * in domain/context.ts):\n * 1. `FLUID_TOKEN` / `FLUID_API_TOKEN` environment variables\n * 2. Project-level `.fluidrc` profile (walks upward from cwd)\n * 3. Global active profile from `~/.fluid/config.json`\n *\n * When `profileName` is provided explicitly (e.g. via `--profile` flag),\n * it takes precedence over the environment variables, `.fluidrc`, and the\n * active profile.\n */\nexport function getAuthToken(profileName?: string): string | null {\n const config = readConfig();\n\n // Explicit profile name takes precedence\n if (profileName) {\n const profile = config.profiles[profileName];\n return profile?.token ?? null;\n }\n\n // Environment variable override (before any stored profile)\n const envToken = process.env[\"FLUID_TOKEN\"] ?? process.env[\"FLUID_API_TOKEN\"];\n if (envToken) return envToken;\n\n // Project-level .fluidrc profile\n const projectConfig = findProjectConfig(process.cwd());\n if (projectConfig?.profile) {\n const profile = config.profiles[projectConfig.profile];\n if (profile) return profile.token;\n }\n\n // Fall back to global active profile\n if (!config.activeProfile) return null;\n const profile = config.profiles[config.activeProfile];\n return profile?.token ?? null;\n}\n\n/**\n * Get the active profile, or null if not logged in\n */\nexport function getActiveProfile(): FluidProfile | null {\n const config = readConfig();\n if (!config.activeProfile) return null;\n\n return config.profiles[config.activeProfile] ?? null;\n}\n\n/**\n * List all stored profile names\n */\nexport function listProfileNames(): string[] {\n const config = readConfig();\n return Object.keys(config.profiles);\n}\n"],"mappings":"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"}
|