UNPKG

ccusage

Version:

Usage analysis tool for Claude Code

1,311 lines 205 kB
#!/usr/bin/env node import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-Dm8hcn_h.js"; import { CostModes, SortOrders, dateSchema } from "./_types-Cr2YEzKm.js"; import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-CoS7we68.js"; import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-BeaFK_sH.js"; import { description, log, logger, name, version } from "./logger-Cke8hliP.js"; import { detectMismatches, printMismatchReport } from "./debug-BmJuGBXC.js"; import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-DKqp_F9c.js"; import { readFile } from "node:fs/promises"; import path from "node:path"; import process$1 from "node:process"; import { createServer } from "node:http"; import { Http2ServerRequest } from "node:http2"; import { Readable } from "node:stream"; import crypto from "node:crypto"; /** * The default locale string, which format is BCP 47 language tag. */ const DEFAULT_LOCALE = "en-US"; const BUILT_IN_PREFIX = "_"; const ARG_PREFIX = "arg"; const BUILT_IN_KEY_SEPARATOR = ":"; const ANONYMOUS_COMMAND_NAME = "(anonymous)"; const NOOP = () => {}; const COMMON_ARGS = { help: { type: "boolean", short: "h", description: "Display this help message" }, version: { type: "boolean", short: "v", description: "Display this version" } }; const COMMAND_OPTIONS_DEFAULT = { name: void 0, description: void 0, version: void 0, cwd: void 0, usageSilent: false, subCommands: void 0, leftMargin: 2, middleMargin: 10, usageOptionType: false, usageOptionValue: true, renderHeader: void 0, renderUsage: void 0, renderValidationErrors: void 0, translationAdapterFactory: void 0 }; function isLazyCommand(cmd) { return typeof cmd === "function" && "commandName" in cmd && !!cmd.commandName; } async function resolveLazyCommand(cmd, name$1, needRunResolving = false) { let command; if (isLazyCommand(cmd)) { command = Object.assign(create(), { name: cmd.commandName, description: cmd.description, args: cmd.args, examples: cmd.examples, resource: cmd.resource }); if (needRunResolving) { const loaded = await cmd(); if (typeof loaded === "function") command.run = loaded; else if (typeof loaded === "object") { if (loaded.run == null) throw new TypeError(`'run' is required in command: ${cmd.name || name$1}`); command.run = loaded.run; command.name = loaded.name; command.description = loaded.description; command.args = loaded.args; command.examples = loaded.examples; command.resource = loaded.resource; } else throw new TypeError(`Cannot resolve command: ${cmd.name || name$1}`); } } else command = Object.assign(create(), cmd); if (command.name == null && name$1) command.name = name$1; return deepFreeze(command); } function resolveBuiltInKey(key) { return `${BUILT_IN_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`; } function resolveArgKey(key) { return `${ARG_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`; } async function resolveExamples(ctx, examples) { return typeof examples === "string" ? examples : typeof examples === "function" ? await examples(ctx) : ""; } function mapResourceWithBuiltinKey(resource) { return Object.entries(resource).reduce((acc, [key, value]) => { acc[resolveBuiltInKey(key)] = value; return acc; }, create()); } function create(obj = null) { return Object.create(obj); } function log$1(...args) { console.log(...args); } function deepFreeze(obj) { if (obj === null || typeof obj !== "object") return obj; for (const key of Object.keys(obj)) { const value = obj[key]; if (typeof value === "object" && value !== null) deepFreeze(value); } return Object.freeze(obj); } var COMMAND = "COMMAND"; var COMMANDS = "COMMANDS"; var SUBCOMMAND = "SUBCOMMAND"; var USAGE = "USAGE"; var ARGUMENTS = "ARGUMENTS"; var OPTIONS = "OPTIONS"; var EXAMPLES = "EXAMPLES"; var FORMORE = "For more info, run any command with the `--help` flag:"; var NEGATABLE = "Negatable of"; var DEFAULT = "default"; var CHOICES = "choices"; var help = "Display this help message"; var version$1 = "Display this version"; var en_US_default = { COMMAND, COMMANDS, SUBCOMMAND, USAGE, ARGUMENTS, OPTIONS, EXAMPLES, FORMORE, NEGATABLE, DEFAULT, CHOICES, help, version: version$1 }; function createTranslationAdapter(options) { return new DefaultTranslation(options); } var DefaultTranslation = class { #resources = /* @__PURE__ */ new Map(); #options; constructor(options) { this.#options = options; this.#resources.set(options.locale, create()); if (options.locale !== options.fallbackLocale) this.#resources.set(options.fallbackLocale, create()); } getResource(locale) { return this.#resources.get(locale); } setResource(locale, resource) { this.#resources.set(locale, resource); } getMessage(locale, key) { const resource = this.getResource(locale); if (resource) return resource[key]; return void 0; } translate(locale, key, values = create()) { let message = this.getMessage(locale, key); if (message === void 0 && locale !== this.#options.fallbackLocale) message = this.getMessage(this.#options.fallbackLocale, key); if (message === void 0) return; return message.replaceAll(/\{\{(\w+)\}\}/g, (_, name$1) => { return values[name$1] == null ? "" : values[name$1].toString(); }); } }; const BUILT_IN_PREFIX_CODE = BUILT_IN_PREFIX.codePointAt(0); /** * Create a {@link CommandContext | command context} * @param param A {@link CommandContextParams | parameters} to create a {@link CommandContext | command context} * @returns A {@link CommandContext | command context}, which is readonly */ async function createCommandContext({ args, values, positionals, rest, argv: argv$1, tokens, command, cliOptions, callMode = "entry", omitted = false }) { /** * normailize the options schema and values, to avoid prototype pollution */ const _args = Object.entries(args).reduce((acc, [key, value]) => { acc[key] = Object.assign(create(), value); return acc; }, create()); /** * setup the environment */ const env$2 = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, cliOptions); const locale = resolveLocale(cliOptions.locale); const localeStr = locale.toString(); const translationAdapterFactory = cliOptions.translationAdapterFactory || createTranslationAdapter; const adapter = translationAdapterFactory({ locale: localeStr, fallbackLocale: DEFAULT_LOCALE }); const localeResources = /* @__PURE__ */ new Map(); let builtInLoadedResources; /** * load the built-in locale resources */ localeResources.set(DEFAULT_LOCALE, mapResourceWithBuiltinKey(en_US_default)); if (DEFAULT_LOCALE !== localeStr) try { builtInLoadedResources = (await import(`./locales/${localeStr}.json`, { with: { type: "json" } })).default; localeResources.set(localeStr, mapResourceWithBuiltinKey(builtInLoadedResources)); } catch {} /** * define the translation function, which is used to {@link CommandContext.translate}. * */ function translate(key, values$1 = create()) { const strKey = key; if (strKey.codePointAt(0) === BUILT_IN_PREFIX_CODE) { const resource = localeResources.get(localeStr) || localeResources.get(DEFAULT_LOCALE); return resource[strKey] || strKey; } else return adapter.translate(locale.toString(), strKey, values$1) || ""; } /** * load the sub commands */ let cachedCommands; async function loadCommands() { if (cachedCommands) return cachedCommands; const subCommands$1 = [...cliOptions.subCommands || []]; return cachedCommands = await Promise.all(subCommands$1.map(async ([name$1, cmd]) => await resolveLazyCommand(cmd, name$1))); } /** * create the context */ const ctx = deepFreeze(Object.assign(create(), { name: getCommandName(command), description: command.description, omitted, callMode, locale, env: env$2, args: _args, values, positionals, rest, _: argv$1, tokens, toKebab: command.toKebab, log: cliOptions.usageSilent ? NOOP : log$1, loadCommands, translate })); /** * load the command resources */ const loadedOptionsResources = Object.entries(args).map(([key, arg]) => { const description$1 = arg.description || ""; return [key, description$1]; }); const defaultCommandResource = loadedOptionsResources.reduce((res, [key, value]) => { res[resolveArgKey(key)] = value; return res; }, create()); defaultCommandResource.description = command.description || ""; defaultCommandResource.examples = await resolveExamples(ctx, command.examples); adapter.setResource(DEFAULT_LOCALE, defaultCommandResource); const originalResource = await loadCommandResource(ctx, command); if (originalResource) { const resource = Object.assign(create(), originalResource, { examples: await resolveExamples(ctx, originalResource.examples) }); if (builtInLoadedResources) { resource.help = builtInLoadedResources.help; resource.version = builtInLoadedResources.version; } adapter.setResource(localeStr, resource); } return ctx; } function getCommandName(cmd) { if (isLazyCommand(cmd)) return cmd.commandName || cmd.name || ANONYMOUS_COMMAND_NAME; else if (typeof cmd === "object") return cmd.name || ANONYMOUS_COMMAND_NAME; else return ANONYMOUS_COMMAND_NAME; } function resolveLocale(locale) { return locale instanceof Intl.Locale ? locale : typeof locale === "string" ? new Intl.Locale(locale) : new Intl.Locale(DEFAULT_LOCALE); } async function loadCommandResource(ctx, command) { let resource; try { resource = await command.resource?.(ctx); } catch {} return resource; } /** * Define a {@link Command | command} with type inference * @param definition A {@link Command | command} definition * @returns A {@link Command | command} definition with type inference */ function define(definition) { return definition; } /** * Entry point of utils. * * Note that this entry point is used by gunshi to import utility functions. * * @module */ /** * @author kazuya kawaguchi (a.k.a. kazupon) * @license MIT */ function kebabnize(str) { return str.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase()); } /** * Render the header. * @param ctx A {@link CommandContext | command context} * @returns A rendered header. */ function renderHeader(ctx) { const title = ctx.env.description || ctx.env.name || ""; return Promise.resolve(title ? `${title} (${ctx.env.name || ""}${ctx.env.version ? ` v${ctx.env.version}` : ""})` : title); } const COMMON_ARGS_KEYS = Object.keys(COMMON_ARGS); /** * Render the usage. * @param ctx A {@link CommandContext | command context} * @returns A rendered usage. */ async function renderUsage(ctx) { const messages$1 = []; if (!ctx.omitted) { const description$1 = resolveDescription(ctx); if (description$1) messages$1.push(description$1, ""); } messages$1.push(...await renderUsageSection(ctx), ""); if (ctx.omitted && await hasCommands(ctx)) messages$1.push(...await renderCommandsSection(ctx), ""); if (hasPositionalArgs(ctx)) messages$1.push(...await renderPositionalArgsSection(ctx), ""); if (hasOptionalArgs(ctx)) messages$1.push(...await renderOptionalArgsSection(ctx), ""); const examples = await renderExamplesSection(ctx); if (examples.length > 0) messages$1.push(...examples, ""); return messages$1.join("\n"); } /** * Render the positional arguments section * @param ctx A {@link CommandContext | command context} * @returns A rendered arguments section */ async function renderPositionalArgsSection(ctx) { const messages$1 = []; messages$1.push(`${ctx.translate(resolveBuiltInKey("ARGUMENTS"))}:`); messages$1.push(await generatePositionalArgsUsage(ctx)); return messages$1; } /** * Render the optional arguments section * @param ctx A {@link CommandContext | command context} * @returns A rendered options section */ async function renderOptionalArgsSection(ctx) { const messages$1 = []; messages$1.push(`${ctx.translate(resolveBuiltInKey("OPTIONS"))}:`); messages$1.push(await generateOptionalArgsUsage(ctx, getOptionalArgsPairs(ctx))); return messages$1; } /** * Render the examples section * @param ctx A {@link CommandContext | command context} * @returns A rendered examples section */ async function renderExamplesSection(ctx) { const messages$1 = []; const resolvedExamples = await resolveExamples$1(ctx); if (resolvedExamples) { const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length)); messages$1.push(`${ctx.translate(resolveBuiltInKey("EXAMPLES"))}:`, ...examples); } return messages$1; } /** * Render the usage section * @param ctx A {@link CommandContext | command context} * @returns A rendered usage section */ async function renderUsageSection(ctx) { const messages$1 = [`${ctx.translate(resolveBuiltInKey("USAGE"))}:`]; if (ctx.omitted) { const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`; messages$1.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length)); if (await hasCommands(ctx)) { const commandsUsage = `${resolveEntry(ctx)} <${ctx.translate(resolveBuiltInKey("COMMANDS"))}>`; messages$1.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length)); } } else { const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`; messages$1.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length)); } return messages$1; } /** * Render the commands section * @param ctx A {@link CommandContext | command context} * @returns A rendered commands section */ async function renderCommandsSection(ctx) { const messages$1 = [`${ctx.translate(resolveBuiltInKey("COMMANDS"))}:`]; const loadedCommands = await ctx.loadCommands(); const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length)); const commandsStr = await Promise.all(loadedCommands.map((cmd) => { const key = cmd.name || ""; const desc = cmd.description || ""; const command = `${key.padEnd(commandMaxLength + ctx.env.middleMargin)}${desc} `; return `${command.padStart(ctx.env.leftMargin + command.length)} `; })); messages$1.push(...commandsStr, "", ctx.translate(resolveBuiltInKey("FORMORE"))); messages$1.push(...loadedCommands.map((cmd) => { const commandHelp = `${ctx.env.name} ${cmd.name} --help`; return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`; })); return messages$1; } /** * Resolve the entry command name * @param ctx A {@link CommandContext | command context} * @returns The entry command name */ function resolveEntry(ctx) { return ctx.env.name || ctx.translate(resolveBuiltInKey("COMMAND")); } /** * Resolve the sub command name * @param ctx A {@link CommandContext | command context} * @returns The sub command name */ function resolveSubCommand(ctx) { return ctx.name || ctx.translate(resolveBuiltInKey("SUBCOMMAND")); } /** * Resolve the command description * @param ctx A {@link CommandContext | command context} * @returns resolved command description */ function resolveDescription(ctx) { return ctx.translate("description") || ctx.description || ""; } /** * Resolve the command examples * @param ctx A {@link CommandContext | command context} * @returns resolved command examples, if not resolved, return empty string */ async function resolveExamples$1(ctx) { const ret = ctx.translate("examples"); if (ret) return ret; const command = ctx.env.subCommands?.get(ctx.name || ""); return await resolveExamples(ctx, command?.examples); } /** * Check if the command has sub commands * @param ctx A {@link CommandContext | command context} * @returns True if the command has sub commands */ async function hasCommands(ctx) { const loadedCommands = await ctx.loadCommands(); return loadedCommands.length > 1; } /** * Check if the command has optional arguments * @param ctx A {@link CommandContext | command context} * @returns True if the command has options */ function hasOptionalArgs(ctx) { return !!(ctx.args && Object.values(ctx.args).some((arg) => arg.type !== "positional")); } /** * Check if the command has positional arguments * @param ctx A {@link CommandContext | command context} * @returns True if the command has options */ function hasPositionalArgs(ctx) { return !!(ctx.args && Object.values(ctx.args).some((arg) => arg.type === "positional")); } /** * Check if all options have default values * @param ctx A {@link CommandContext | command context} * @returns True if all options have default values */ function hasAllDefaultOptions(ctx) { return !!(ctx.args && Object.values(ctx.args).every((arg) => arg.default)); } /** * Generate options symbols for usage * @param ctx A {@link CommandContext | command context} * @returns Options symbols for usage */ function generateOptionsSymbols(ctx) { return hasOptionalArgs(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translate(resolveBuiltInKey("OPTIONS"))}]` : `<${ctx.translate(resolveBuiltInKey("OPTIONS"))}>` : ""; } function makeShortLongOptionPair(schema, name$1, toKebab) { const displayName = toKebab || schema.toKebab ? kebabnize(name$1) : name$1; let key = `--${displayName}`; if (schema.short) key = `-${schema.short}, ${key}`; return key; } /** * Get optional arguments pairs for usage * @param ctx A {@link CommandContext | command context} * @returns Options pairs for usage */ function getOptionalArgsPairs(ctx) { return Object.entries(ctx.args).reduce((acc, [name$1, schema]) => { if (schema.type === "positional") return acc; let key = makeShortLongOptionPair(schema, name$1, ctx.toKebab); if (schema.type !== "boolean") { const displayName = ctx.toKebab || schema.toKebab ? kebabnize(name$1) : name$1; key = schema.default ? `${key} [${displayName}]` : `${key} <${displayName}>`; } acc[name$1] = key; if (schema.type === "boolean" && schema.negatable && !COMMON_ARGS_KEYS.includes(name$1)) { const displayName = ctx.toKebab || schema.toKebab ? kebabnize(name$1) : name$1; acc[`no-${name$1}`] = `--no-${displayName}`; } return acc; }, create()); } const resolveNegatableKey = (key) => key.split("no-")[1]; function resolveNegatableType(key, ctx) { return ctx.args[key.startsWith("no-") ? resolveNegatableKey(key) : key].type; } function generateDefaultDisplayValue(ctx, schema) { return `${ctx.translate(resolveBuiltInKey("DEFAULT"))}: ${schema.default}`; } function resolveDisplayValue(ctx, key) { if (COMMON_ARGS_KEYS.includes(key)) return ""; const schema = ctx.args[key]; if ((schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "custom") && schema.default !== void 0) return `(${generateDefaultDisplayValue(ctx, schema)})`; if (schema.type === "enum") { const _default = schema.default !== void 0 ? generateDefaultDisplayValue(ctx, schema) : ""; const choices = `${ctx.translate(resolveBuiltInKey("CHOICES"))}: ${schema.choices.join(" | ")}`; return `(${_default ? `${_default}, ${choices}` : choices})`; } return ""; } /** * Generate optional arguments usage * @param ctx A {@link CommandContext | command context} * @param optionsPairs Options pairs for usage * @returns Generated options usage */ async function generateOptionalArgsUsage(ctx, optionsPairs) { const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length)); const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key]) => resolveNegatableType(key, ctx).length)) : 0; const usages = await Promise.all(Object.entries(optionsPairs).map(([key, value]) => { let rawDesc = ctx.translate(resolveArgKey(key)); if (!rawDesc && key.startsWith("no-")) { const name$1 = resolveNegatableKey(key); const schema = ctx.args[name$1]; const optionKey = makeShortLongOptionPair(schema, name$1, ctx.toKebab); rawDesc = `${ctx.translate(resolveBuiltInKey("NEGATABLE"))} ${optionKey}`; } const optionsSchema = ctx.env.usageOptionType ? `[${resolveNegatableType(key, ctx)}] ` : ""; const valueDesc = key.startsWith("no-") ? "" : resolveDisplayValue(ctx, key); const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`; const option = `${value.padEnd(optionsMaxLength + ctx.env.middleMargin)}${desc}${valueDesc ? ` ${valueDesc}` : ""}`; return `${option.padStart(ctx.env.leftMargin + option.length)}`; })); return usages.join("\n"); } function getPositionalArgs(ctx) { return Object.entries(ctx.args).filter(([_, schema]) => schema.type === "positional"); } async function generatePositionalArgsUsage(ctx) { const positionals = getPositionalArgs(ctx); const argsMaxLength = Math.max(...positionals.map(([name$1]) => name$1.length)); const usages = await Promise.all(positionals.map(([name$1]) => { const desc = ctx.translate(resolveArgKey(name$1)) || ctx.args[name$1].description || ""; const arg = `${name$1.padEnd(argsMaxLength + ctx.env.middleMargin)} ${desc}`; return `${arg.padStart(ctx.env.leftMargin + arg.length)}`; })); return usages.join("\n"); } function generatePositionalSymbols(ctx) { return hasPositionalArgs(ctx) ? getPositionalArgs(ctx).map(([name$1]) => `<${name$1}>`).join(" ") : ""; } /** * Render the validation errors. * @param ctx A {@link CommandContext | command context} * @param error An {@link AggregateError} of option in `args-token` validation * @returns A rendered validation error. */ function renderValidationErrors(_ctx, error) { const messages$1 = []; for (const err of error.errors) messages$1.push(err.message); return Promise.resolve(messages$1.join("\n")); } const HYPHEN_CHAR = "-"; const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0); const EQUAL_CHAR = "="; const EQUAL_CODE = EQUAL_CHAR.codePointAt(0); const TERMINATOR = "--"; const SHORT_OPTION_PREFIX = HYPHEN_CHAR; const LONG_OPTION_PREFIX = "--"; /** * Parse command line arguments. * @example * ```js * import { parseArgs } from 'args-tokens' // for Node.js and Bun * // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno * * const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz']) * // do something with using tokens * // ... * console.log('tokens:', tokens) * ``` * @param args command line arguments * @param options parse options * @returns Argument tokens. */ function parseArgs(args, options = {}) { const { allowCompatible = false } = options; const tokens = []; const remainings = [...args]; let index = -1; let groupCount = 0; let hasShortValueSeparator = false; while (remainings.length > 0) { const arg = remainings.shift(); if (arg == void 0) break; const nextArg = remainings[0]; if (groupCount > 0) groupCount--; else index++; if (arg === TERMINATOR) { tokens.push({ kind: "option-terminator", index }); const mapped = remainings.map((arg$1) => { return { kind: "positional", index: ++index, value: arg$1 }; }); tokens.push(...mapped); break; } if (isShortOption(arg)) { const shortOption = arg.charAt(1); let value; let inlineValue; if (groupCount) { tokens.push({ kind: "option", name: shortOption, rawName: arg, index, value, inlineValue }); if (groupCount === 1 && hasOptionValue(nextArg)) { value = remainings.shift(); if (hasShortValueSeparator) { inlineValue = true; hasShortValueSeparator = false; } tokens.push({ kind: "option", index, value, inlineValue }); } } else tokens.push({ kind: "option", name: shortOption, rawName: arg, index, value, inlineValue }); if (value != null) ++index; continue; } if (isShortOptionGroup(arg)) { const expanded = []; let shortValue = ""; for (let i = 1; i < arg.length; i++) { const shortableOption = arg.charAt(i); if (hasShortValueSeparator) shortValue += shortableOption; else if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) hasShortValueSeparator = true; else expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`); } if (shortValue) expanded.push(shortValue); remainings.unshift(...expanded); groupCount = expanded.length; continue; } if (isLongOption(arg)) { const longOption = arg.slice(2); tokens.push({ kind: "option", name: longOption, rawName: arg, index, value: void 0, inlineValue: void 0 }); continue; } if (isLongOptionAndValue(arg)) { const equalIndex = arg.indexOf(EQUAL_CHAR); const longOption = arg.slice(2, equalIndex); const value = arg.slice(equalIndex + 1); tokens.push({ kind: "option", name: longOption, rawName: `${LONG_OPTION_PREFIX}${longOption}`, index, value, inlineValue: true }); continue; } tokens.push({ kind: "positional", index, value: arg }); } return tokens; } /** * Check if `arg` is a short option (e.g. `-f`). * @param arg the argument to check * @returns whether `arg` is a short option. */ function isShortOption(arg) { return arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE; } /** * Check if `arg` is a short option group (e.g. `-abc`). * @param arg the argument to check * @returns whether `arg` is a short option group. */ function isShortOptionGroup(arg) { if (arg.length <= 2) return false; if (arg.codePointAt(0) !== HYPHEN_CODE) return false; if (arg.codePointAt(1) === HYPHEN_CODE) return false; return true; } /** * Check if `arg` is a long option (e.g. `--foo`). * @param arg the argument to check * @returns whether `arg` is a long option. */ function isLongOption(arg) { return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3); } /** * Check if `arg` is a long option with value (e.g. `--foo=bar`). * @param arg the argument to check * @returns whether `arg` is a long option. */ function isLongOptionAndValue(arg) { return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3); } /** * Check if `arg` is a long option prefix (e.g. `--`). * @param arg the argument to check * @returns whether `arg` is a long option prefix. */ function hasLongOptionPrefix(arg) { return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX); } /** * Check if a `value` is an option value. * @param value a value to check * @returns whether a `value` is an option value. */ function hasOptionValue(value) { return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE; } const SKIP_POSITIONAL_DEFAULT = -1; /** * Resolve command line arguments. * @param args - An arguments that contains {@link ArgSchema | arguments schema}. * @param tokens - An array of {@link ArgToken | tokens}. * @param resolveArgs - An arguments that contains {@link ResolveArgs | resolve arguments}. * @returns An object that contains the values of the arguments, positional arguments, rest arguments, and {@link AggregateError | validation errors}. */ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT, toKebab = false } = {}) { const skipPositionalIndex = typeof skipPositional === "number" ? Math.max(skipPositional, SKIP_POSITIONAL_DEFAULT) : SKIP_POSITIONAL_DEFAULT; const rest = []; const optionTokens = []; const positionalTokens = []; let currentLongOption; let currentShortOption; const expandableShortOptions = []; function toShortValue() { if (expandableShortOptions.length === 0) return void 0; else { const value = expandableShortOptions.map((token) => token.name).join(""); expandableShortOptions.length = 0; return value; } } function applyLongOptionValue(value = void 0) { if (currentLongOption) { currentLongOption.value = value; optionTokens.push({ ...currentLongOption }); currentLongOption = void 0; } } function applyShortOptionValue(value = void 0) { if (currentShortOption) { currentShortOption.value = value || toShortValue(); optionTokens.push({ ...currentShortOption }); currentShortOption = void 0; } } /** * analyze phase to resolve value * separate tokens into positionals, long and short options, after that resolve values */ const schemas = Object.values(args); let terminated = false; for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if (token.kind === "positional") { if (terminated && token.value) { rest.push(token.value); continue; } if (currentShortOption) { const found = schemas.find((schema) => schema.short === currentShortOption.name && schema.type === "boolean"); if (found) positionalTokens.push({ ...token }); } else if (currentLongOption) { const found = args[currentLongOption.name]?.type === "boolean"; if (found) positionalTokens.push({ ...token }); } else positionalTokens.push({ ...token }); applyLongOptionValue(token.value); applyShortOptionValue(token.value); } else if (token.kind === "option") if (token.rawName) { if (hasLongOptionPrefix(token.rawName)) { applyLongOptionValue(); if (token.inlineValue) optionTokens.push({ ...token }); else currentLongOption = { ...token }; applyShortOptionValue(); } else if (isShortOption(token.rawName)) if (currentShortOption) { if (currentShortOption.index === token.index) if (shortGrouping) { currentShortOption.value = token.value; optionTokens.push({ ...currentShortOption }); currentShortOption = { ...token }; } else expandableShortOptions.push({ ...token }); else { currentShortOption.value = toShortValue(); optionTokens.push({ ...currentShortOption }); currentShortOption = { ...token }; } applyLongOptionValue(); } else { currentShortOption = { ...token }; applyLongOptionValue(); } } else { if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) { currentShortOption.value = token.value; optionTokens.push({ ...currentShortOption }); currentShortOption = void 0; } applyLongOptionValue(); } else { if (token.kind === "option-terminator") terminated = true; applyLongOptionValue(); applyShortOptionValue(); } } /** * check if the last long or short option is not resolved */ applyLongOptionValue(); applyShortOptionValue(); /** * resolve values */ const values = Object.create(null); const errors = []; function checkTokenName(option, schema, token) { return token.name === (schema.type === "boolean" ? schema.negatable && token.name?.startsWith("no-") ? `no-${option}` : option : option); } const positionalItemCount = tokens.filter((token) => token.kind === "positional").length; function getPositionalSkipIndex() { return Math.min(skipPositionalIndex, positionalItemCount); } let positionalsCount = 0; for (const [rawArg, schema] of Object.entries(args)) { const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg; if (schema.required) { const found = optionTokens.find((token) => { return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg; }); if (!found) { errors.push(createRequireError(arg, schema)); continue; } } if (schema.type === "positional") { if (skipPositionalIndex > SKIP_POSITIONAL_DEFAULT) while (positionalsCount <= getPositionalSkipIndex()) positionalsCount++; const positional = positionalTokens[positionalsCount]; if (positional != null) values[rawArg] = positional.value; else errors.push(createRequireError(arg, schema)); positionalsCount++; continue; } for (let i = 0; i < optionTokens.length; i++) { const token = optionTokens[i]; if (checkTokenName(arg, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName) || schema.short === token.name && token.rawName != void 0 && isShortOption(token.rawName)) { const invalid = validateRequire(token, arg, schema); if (invalid) { errors.push(invalid); continue; } if (schema.type === "boolean") token.value = void 0; const [parsedValue, error] = parse(token, arg, schema); if (error) errors.push(error); else if (schema.multiple) { values[rawArg] ||= []; values[rawArg].push(parsedValue); } else values[rawArg] = parsedValue; } } if (values[rawArg] == null && schema.default != null) values[rawArg] = schema.default; } return { values, positionals: positionalTokens.map((token) => token.value), rest, error: errors.length > 0 ? new AggregateError(errors) : void 0 }; } function parse(token, option, schema) { switch (schema.type) { case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)]; case "boolean": return token.value ? [token.value || schema.default, void 0] : [!(schema.negatable && token.name.startsWith("no-")), void 0]; case "number": { if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)]; return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0]; } case "enum": { if (schema.choices && !schema.choices.includes(token.value)) return [void 0, new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be chosen from '${schema.type}' [${schema.choices.map((c) => JSON.stringify(c)).join(", ")}] values`, option, "type", schema)]; return [token.value || schema.default, void 0]; } case "custom": { if (typeof schema.parse !== "function") throw new TypeError(`argument '${option}' should have a 'parse' function`); try { return [schema.parse(token.value || String(schema.default || "")), void 0]; } catch (error) { return [void 0, error]; } } default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`); } } function createRequireError(option, schema) { const message = schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}is required`; return new ArgResolveError(message, option, "required", schema); } /** * An error that occurs when resolving arguments. * This error is thrown when the argument is not valid. */ var ArgResolveError = class extends Error { name; schema; type; constructor(message, name$1, type, schema) { super(message); this.name = name$1; this.type = type; this.schema = schema; } }; function validateRequire(token, option, schema) { if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema); } function isNumeric(str) { return str.trim() !== "" && !isNaN(str); } function createTypeError(option, schema) { return new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be '${schema.type}'`, option, "type", schema); } /** * Run the command. * @param args Command line arguments * @param entry A {@link Command | entry command}, an {@link CommandRunner | inline command runner}, or a {@link LazyCommand | lazily-loaded command} * @param options A {@link CliOptions | CLI options} * @returns A rendered usage or undefined. if you will use {@link CliOptions.usageSilent} option, it will return rendered usage string. */ async function cli(argv$1, entry, options = {}) { const cliOptions = resolveCliOptions(options, entry); const tokens = parseArgs(argv$1); const subCommand = getSubCommand(tokens); const { commandName: name$1, command, callMode } = await resolveCommand(subCommand, entry, cliOptions); if (!command) throw new Error(`Command not found: ${name$1 || ""}`); const args = resolveArguments(getCommandArgs(command)); const { values, positionals, rest, error } = resolveArgs(args, tokens, { shortGrouping: true, toKebab: command.toKebab, skipPositional: cliOptions.subCommands.size > 0 ? 0 : -1 }); const omitted = !subCommand; const ctx = await createCommandContext({ args, values, positionals, rest, argv: argv$1, tokens, omitted, callMode, command, cliOptions }); if (values.version) { showVersion(ctx); return; } const usageBuffer = []; const header = await showHeader(ctx); if (header) usageBuffer.push(header); if (values.help) { const usage = await showUsage(ctx); if (usage) usageBuffer.push(usage); return usageBuffer.join("\n"); } if (error) { await showValidationErrors(ctx, error); return; } await executeCommand(command, ctx, name$1 || ""); } function getCommandArgs(cmd) { if (isLazyCommand(cmd)) return cmd.args || create(); else if (typeof cmd === "object") return cmd.args || create(); else return create(); } function resolveArguments(args) { return Object.assign(create(), args, COMMON_ARGS); } function resolveCliOptions(options, entry) { const subCommands$1 = new Map(options.subCommands); if (options.subCommands) { if (isLazyCommand(entry)) subCommands$1.set(entry.commandName, entry); else if (typeof entry === "object" && entry.name) subCommands$1.set(entry.name, entry); } const resolvedOptions = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands: subCommands$1 }); return resolvedOptions; } function getSubCommand(tokens) { const firstToken = tokens[0]; return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : ""; } async function showUsage(ctx) { if (ctx.env.renderUsage === null) return; const usage = await (ctx.env.renderUsage || renderUsage)(ctx); if (usage) { ctx.log(usage); return usage; } } function showVersion(ctx) { ctx.log(ctx.env.version); } async function showHeader(ctx) { if (ctx.env.renderHeader === null) return; const header = await (ctx.env.renderHeader || renderHeader)(ctx); if (header) { ctx.log(header); ctx.log(); return header; } } async function showValidationErrors(ctx, error) { if (ctx.env.renderValidationErrors === null) return; const render = ctx.env.renderValidationErrors || renderValidationErrors; ctx.log(await render(ctx, error)); } const CANNOT_RESOLVE_COMMAND = { callMode: "unexpected" }; async function resolveCommand(sub, entry, options) { const omitted = !sub; async function doResolveCommand() { if (typeof entry === "function") if ("commandName" in entry && entry.commandName) return { commandName: entry.commandName, command: entry, callMode: "entry" }; else return { command: { run: entry }, callMode: "entry" }; else if (typeof entry === "object") return { commandName: resolveEntryName(entry), command: entry, callMode: "entry" }; else return CANNOT_RESOLVE_COMMAND; } if (omitted || options.subCommands?.size === 0) return doResolveCommand(); const cmd = options.subCommands?.get(sub); if (cmd == null) return { commandName: sub, callMode: "unexpected" }; if (isLazyCommand(cmd) && cmd.commandName == null) cmd.commandName = sub; else if (typeof cmd === "object" && cmd.name == null) cmd.name = sub; return { commandName: sub, command: cmd, callMode: "subCommand" }; } function resolveEntryName(entry) { return entry.name || ANONYMOUS_COMMAND_NAME; } async function executeCommand(cmd, ctx, name$1) { const resolved = isLazyCommand(cmd) ? await resolveLazyCommand(cmd, name$1, true) : cmd; if (resolved.run == null) throw new Error(`'run' not found on Command \`${name$1}\``); await resolved.run(ctx); } var require_picocolors = __commonJSMin((exports, module) => { let p = process || {}, argv = p.argv || [], env$1 = p.env || {}; let isColorSupported = !(!!env$1.NO_COLOR || argv.includes("--no-color")) && (!!env$1.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env$1.TERM !== "dumb" || !!env$1.CI); let formatter = (open, close, replace = open) => (input) => { let string = "" + input, index = string.indexOf(close, open.length); return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; }; let replaceClose = (string, close, replace, index) => { let result = "", cursor = 0; do { result += string.substring(cursor, index) + replace; cursor = index + close.length; index = string.indexOf(close, cursor); } while (~index); return result + string.substring(cursor); }; let createColors = (enabled = isColorSupported) => { let f = enabled ? formatter : () => String; return { isColorSupported: enabled, reset: f("\x1B[0m", "\x1B[0m"), bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), italic: f("\x1B[3m", "\x1B[23m"), underline: f("\x1B[4m", "\x1B[24m"), inverse: f("\x1B[7m", "\x1B[27m"), hidden: f("\x1B[8m", "\x1B[28m"), strikethrough: f("\x1B[9m", "\x1B[29m"), black: f("\x1B[30m", "\x1B[39m"), red: f("\x1B[31m", "\x1B[39m"), green: f("\x1B[32m", "\x1B[39m"), yellow: f("\x1B[33m", "\x1B[39m"), blue: f("\x1B[34m", "\x1B[39m"), magenta: f("\x1B[35m", "\x1B[39m"), cyan: f("\x1B[36m", "\x1B[39m"), white: f("\x1B[37m", "\x1B[39m"), gray: f("\x1B[90m", "\x1B[39m"), bgBlack: f("\x1B[40m", "\x1B[49m"), bgRed: f("\x1B[41m", "\x1B[49m"), bgGreen: f("\x1B[42m", "\x1B[49m"), bgYellow: f("\x1B[43m", "\x1B[49m"), bgBlue: f("\x1B[44m", "\x1B[49m"), bgMagenta: f("\x1B[45m", "\x1B[49m"), bgCyan: f("\x1B[46m", "\x1B[49m"), bgWhite: f("\x1B[47m", "\x1B[49m"), blackBright: f("\x1B[90m", "\x1B[39m"), redBright: f("\x1B[91m", "\x1B[39m"), greenBright: f("\x1B[92m", "\x1B[39m"), yellowBright: f("\x1B[93m", "\x1B[39m"), blueBright: f("\x1B[94m", "\x1B[39m"), magentaBright: f("\x1B[95m", "\x1B[39m"), cyanBright: f("\x1B[96m", "\x1B[39m"), whiteBright: f("\x1B[97m", "\x1B[39m"), bgBlackBright: f("\x1B[100m", "\x1B[49m"), bgRedBright: f("\x1B[101m", "\x1B[49m"), bgGreenBright: f("\x1B[102m", "\x1B[49m"), bgYellowBright: f("\x1B[103m", "\x1B[49m"), bgBlueBright: f("\x1B[104m", "\x1B[49m"), bgMagentaBright: f("\x1B[105m", "\x1B[49m"), bgCyanBright: f("\x1B[106m", "\x1B[49m"), bgWhiteBright: f("\x1B[107m", "\x1B[49m") }; }; module.exports = createColors(); module.exports.createColors = createColors; }); /** * Parses and validates a date argument in YYYYMMDD format * @param value - Date string to parse * @returns Validated date string * @throws TypeError if date format is invalid */ function parseDateArg(value) { const result = dateSchema.safeParse(value); if (!result.success) throw new TypeError(result.error.issues[0]?.message ?? "Invalid date format"); return result.data; } /** * Shared command line arguments used across multiple CLI commands */ const sharedArgs = { since: { type: "custom", short: "s", description: "Filter from date (YYYYMMDD format)", parse: parseDateArg }, until: { type: "custom", short: "u", description: "Filter until date (YYYYMMDD format)", parse: parseDateArg }, json: { type: "boolean", short: "j", description: "Output in JSON format", default: false }, mode: { type: "enum", short: "m", description: "Cost calculation mode: auto (use costUSD if exists, otherwise calculate), calculate (always calculate), display (always use costUSD)", default: "auto", choices: CostModes }, debug: { type: "boolean", short: "d", description: "Show pricing mismatch information for debugging", default: false }, debugSamples: { type: "number", description: "Number of sample discrepancies to show in debug output (default: 5)", default: 5 }, order: { type: "enum", short: "o", description: "Sort order: desc (newest first) or asc (oldest first)", default: "asc", choices: SortOrders }, breakdown: { type: "boolean", short: "b", description: "Show per-model cost breakdown", default: false }, offline: { type: "boolean", negatable: true, short: "O", description: "Use cached pricing data for Claude models instead of fetching from API", default: false } }; /** * Shared command configuration for Gunshi CLI commands */ const sharedCommandConfig = { args: sharedArgs, toKebab: true }; var require_debug$1 = __commonJSMin((exports, module) => { let messages = []; let level = 0; const debug$3 = (msg, min) => { if (level >= min) messages.push(msg); }; debug$3.WARN = 1; debug$3.INFO = 2; debug$3.DEBUG = 3; debug$3.reset = () => { messages = []; }; debug$3.setDebugLevel = (v) => { level = v; }; debug$3.warn = (msg) => debug$3(msg, debug$3.WARN); debug$3.info = (msg) => debug$3(msg, debug$3.INFO); debug$3.debug = (msg) => debug$3(msg, debug$3.DEBUG); debug$3.debugMessages = () => messages; module.exports = debug$3; }); var require_ansi_regex = __commonJSMin((exports, module) => { module.exports = ({ onlyFirst = false } = {}) => { const pattern = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|"); return new RegExp(pattern, onlyFirst ? void 0 : "g"); }; }); var require_strip_ansi = __commonJSMin((exports, module) => { const ansiRegex$1 = require_ansi_regex(); module.exports = (string) => typeof string === "string" ? string.replace(ansiRegex$1(), "") : string; }); var require_is_fullwidth_code_point = __commonJSMin((exports, module) => { const isFullwidthCodePoint$1 = (codePoint) => { if (Number.isNaN(codePoint)) return false; if (codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || 11904 <= codePoint && codePoint <= 12871 && codePoint !== 12351 || 12880 <= codePoint && codePoint <= 19903 || 19968 <= codePoint && codePoint <= 42182 || 43360 <= codePoint && codePoint <= 43388 || 44032 <= codePoint && codePoint <= 55203 || 63744 <= codePoint && codePoint <= 64255 || 65040 <= codePoint && codePoint <= 65049 || 65072 <= codePoint && codePoint <= 65131 || 65281 <= codePoint && codePoint <= 65376 || 65504 <= codePoint && codePoint <= 65510 || 110592 <= codePoint && codePoint <= 110593 || 127488 <= codePoint && codePoint <= 127569 || 131072 <= codePoint && codePoint <= 262141)) return true; return false; }; module.exports = isFullwidthCodePoint$1; module.exports.default = isFullwidthCodePoint$1; }); var require_emoji_regex$1 = __commonJSMin((exports, module) => { module.exports = function() { return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\u