ccremote
Version:
Claude Code Remote: approve prompts from Discord, auto-continue sessions after quota resets, and schedule quota windows around your workday.
1,458 lines (1,447 loc) • 175 kB
JavaScript
#!/usr/bin/env node
import { n as __toESM, t as __commonJS } from "./chunk-CVmoYqME.js";
import { i as DiscordBot, n as SessionManager, r as generateQuotaMessage, t as TmuxManager } from "./tmux-D_WriM83.js";
import { i as getSessionLogPath } from "./paths-CZvcxIsd.js";
import { n as validateConfig, t as loadConfig } from "./config-dUyvuHWL.js";
import { accessSync, constants, existsSync, promises, readFileSync, writeFileSync } from "node:fs";
import { basename, dirname, join } from "node:path";
import process$1, { stdin, stdout } from "node:process";
import consola, { consola as consola$1 } from "consola";
import { homedir } from "node:os";
import { exec, spawn } from "node:child_process";
import { promisify, stripVTControlCharacters } from "node:util";
import { fileURLToPath } from "node:url";
import updateNotifier from "update-notifier";
import * as g from "node:readline";
import O from "node:readline";
import { Writable } from "node:stream";
//#region node_modules/gunshi/lib/utils-D41C8Abf.js
/**
* 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(...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);
}
//#endregion
//#region node_modules/gunshi/lib/context-D_EmfRNA.js
var en_US_default = {
COMMAND: "COMMAND",
COMMANDS: "COMMANDS",
SUBCOMMAND: "SUBCOMMAND",
USAGE: "USAGE",
ARGUMENTS: "ARGUMENTS",
OPTIONS: "OPTIONS",
EXAMPLES: "EXAMPLES",
FORMORE: "For more info, run any command with the `--help` flag:",
NEGATABLE: "Negatable of",
DEFAULT: "default",
CHOICES: "choices",
help: "Display this help message",
version: "Display this version"
};
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];
}
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, (_$2, 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$1 = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, cliOptions);
const locale = resolveLocale(cliOptions.locale);
const localeStr = locale.toString();
const adapter = (cliOptions.translationAdapterFactory || createTranslationAdapter)({
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) return (localeResources.get(localeStr) || localeResources.get(DEFAULT_LOCALE))[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$1,
args: _args,
values,
positionals,
rest,
_: argv$1,
tokens,
toKebab: command.toKebab,
log: cliOptions.usageSilent ? NOOP : log,
loadCommands,
translate
}));
const defaultCommandResource = Object.entries(args).map(([key, arg]) => {
return [key, arg.description || ""];
}).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;
}
//#endregion
//#region node_modules/gunshi/lib/definition-wq1Kmbvq.js
/**
* 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;
}
//#endregion
//#region node_modules/args-tokens/lib/utils-N7UlhLbz.js
/**
* 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());
}
//#endregion
//#region node_modules/gunshi/lib/renderer-BzRfaLdJ.js
/**
* 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 = [];
if (!ctx.omitted) {
const description$1 = resolveDescription(ctx);
if (description$1) messages.push(description$1, "");
}
messages.push(...await renderUsageSection(ctx), "");
if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
if (hasPositionalArgs(ctx)) messages.push(...await renderPositionalArgsSection(ctx), "");
if (hasOptionalArgs(ctx)) messages.push(...await renderOptionalArgsSection(ctx), "");
const examples = await renderExamplesSection(ctx);
if (examples.length > 0) messages.push(...examples, "");
return messages.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 = [];
messages.push(`${ctx.translate(resolveBuiltInKey("ARGUMENTS"))}:`);
messages.push(await generatePositionalArgsUsage(ctx));
return messages;
}
/**
* Render the optional arguments section
* @param ctx A {@link CommandContext | command context}
* @returns A rendered options section
*/
async function renderOptionalArgsSection(ctx) {
const messages = [];
messages.push(`${ctx.translate(resolveBuiltInKey("OPTIONS"))}:`);
messages.push(await generateOptionalArgsUsage(ctx, getOptionalArgsPairs(ctx)));
return messages;
}
/**
* Render the examples section
* @param ctx A {@link CommandContext | command context}
* @returns A rendered examples section
*/
async function renderExamplesSection(ctx) {
const messages = [];
const resolvedExamples = await resolveExamples$1(ctx);
if (resolvedExamples) {
const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
messages.push(`${ctx.translate(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
}
return messages;
}
/**
* Render the usage section
* @param ctx A {@link CommandContext | command context}
* @returns A rendered usage section
*/
async function renderUsageSection(ctx) {
const messages = [`${ctx.translate(resolveBuiltInKey("USAGE"))}:`];
if (ctx.omitted) {
const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`;
messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
if (await hasCommands(ctx)) {
const commandsUsage = `${resolveEntry(ctx)} <${ctx.translate(resolveBuiltInKey("COMMANDS"))}>`;
messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
}
} else {
const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`;
messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
}
return messages;
}
/**
* Render the commands section
* @param ctx A {@link CommandContext | command context}
* @returns A rendered commands section
*/
async function renderCommandsSection(ctx) {
const messages = [`${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.push(...commandsStr, "", ctx.translate(resolveBuiltInKey("FORMORE")));
messages.push(...loadedCommands.map((cmd) => {
const commandHelp = `${ctx.env.name} ${cmd.name} --help`;
return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
}));
return messages;
}
/**
* 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) {
return (await ctx.loadCommands()).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) {
let key = `--${toKebab || schema.toKebab ? kebabnize(name$1) : name$1}`;
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(([_$2, value]) => value.length));
const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key]) => resolveNegatableType(key, ctx).length)) : 0;
return (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)}`;
}))).join("\n");
}
function getPositionalArgs(ctx) {
return Object.entries(ctx.args).filter(([_$2, schema]) => schema.type === "positional");
}
async function generatePositionalArgsUsage(ctx) {
const positionals = getPositionalArgs(ctx);
const argsMaxLength = Math.max(...positionals.map(([name$1]) => name$1.length));
return (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)}`;
}))).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 = [];
for (const err of error.errors) messages.push(err.message);
return Promise.resolve(messages.join("\n"));
}
//#endregion
//#region node_modules/args-tokens/lib/parser-Dr4iAGaX.js
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;
}
//#endregion
//#region node_modules/args-tokens/lib/resolver-Q4k2fgTW.js
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) {
if (schemas.find((schema) => schema.short === currentShortOption.name && schema.type === "boolean")) positionalTokens.push({ ...token });
} else if (currentLongOption) {
if (args[currentLongOption.name]?.type === "boolean") 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) {
if (!optionTokens.find((token) => {
return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
})) {
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) {
return new ArgResolveError(schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}is required`, 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);
}
//#endregion
//#region node_modules/gunshi/lib/cli-DVGNVw3h.js
/**
* 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 ctx = await createCommandContext({
args,
values,
positionals,
rest,
argv: argv$1,
tokens,
omitted: !subCommand,
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);
}
return Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands: subCommands$1 });
}
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);
}
//#endregion
//#region package.json
var name = "ccremote";
var version = "0.2.0";
var description = "Claude Code Remote: approve prompts from Discord, auto-continue sessions after quota resets, and schedule quota windows around your workday.";
//#endregion
//#region src/core/daemon-manager.ts
var DaemonManager = class {
daemons = /* @__PURE__ */ new Map();
globalConfigDir;
daemonPidsFile;
initPromise = null;
constructor() {
this.globalConfigDir = join(homedir(), ".ccremote");
this.daemonPidsFile = join(this.globalConfigDir, "daemon-pids.json");
this.initPromise = this.loadDaemonPids().catch((error) => {
console.warn("Failed to load existing daemon PIDs on startup:", error instanceof Error ? error.message : error);
});
}
/**
* Ensure the daemon manager is fully initialized
*/
async ensureInitialized() {
if (this.initPromise) {
try {
await Promise.race([this.initPromise, new Promise((_$2, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Daemon initialization timeout")), 1e4))]);
} catch (error) {
console.warn("Daemon initialization failed or timed out:", error instanceof Error ? error.message : error);
}
this.initPromise = null;
}
}
/**
* Get the PM2 binary path from the bundled package
*/
getPm2BinaryPath() {
const currentFileUrl = import.meta.url;
const currentDir = dirname(fileURLToPath(currentFileUrl));
const possiblePaths = [
join(currentDir, "../../node_modules/pm2/bin/pm2"),
join(currentDir, "../node_modules/pm2/bin/pm2"),
join(dirname(process.argv[1]), "../node_modules/pm2/bin/pm2"),
join(dirname(currentDir), "node_modules/pm2/bin/pm2")
];
for (const path of possiblePaths) try {
accessSync(path, constants.F_OK);
return path;
} catch {}
throw new Error("PM2 binary not found. Please ensure PM2 is properly installed.");
}
/**
* Prepare PM2 command arguments and binary
*/
preparePm2Command(args) {
return {
binary: this.getPm2BinaryPath(),
args
};
}
/**
* Get the daemon script path
*/
async getDaemonScriptPath() {
const currentFileUrl = import.meta.url;
const currentDir = dirname(fileURLToPath(currentFileUrl));
const possiblePaths = [
join(currentDir, "../../dist/daemon.js"),
join(currentDir, "../daemon.js"),
join(currentDir, "daemon.js")
];
for (const path of possiblePaths) try {
await promises.access(path);
return path;
} catch {}
throw new Error("Could not find daemon.js script");
}
/**
* Spawn a new daemon process for a session using PM2
*/
async spawnDaemon(config) {
const pm2Name = `${config.sessionId}-daemon`;
if (await this.checkPm2ProcessExists(pm2Name)) await this.forceStopPm2Process(pm2Name);
const daemonScript = await this.getDaemonScriptPath();
const pm2Command = this.preparePm2Command([
"start",
daemonScript,
"--name",
pm2Name,
"--no-autorestart",
"--output",
config.logFile,
"--error",
config.logFile,
"--merge-logs"
]);
return new Promise((resolve$1, reject) => {
const pm2Process = spawn(pm2Command.binary, pm2Command.args, {
stdio: [
"ignore",
"pipe",
"pipe"
],
cwd: process.cwd(),
env: {
...process.env,
NODE_ENV: "production",
CCREMOTE_SESSION_ID: config.sessionId,
CCREMOTE_LOG_FILE: config.logFile
}
});
let stdout$1 = "";
let stderr = "";
pm2Process.stdout?.on("data", (data) => {
stdout$1 += data.toString();
});
pm2Process.stderr?.on("data", (data) => {
stderr += data.toString();
});
pm2Process.on("close", (code) => {
if (code !== 0) {
reject(/* @__PURE__ */ new Error(`PM2 start failed: ${stderr || stdout$1}`));
return;
}
const listCommand$1 = this.preparePm2Command([
"list",
pm2Name,
"--format"
]);
const listProcess = spawn(listCommand$1.binary, listCommand$1.args, { stdio: [
"ignore",
"pipe",
"pipe"
] });
listProcess.stdout?.on("data", (_data) => {});
listProcess.on("close", (_listCode) => {
const daemon = {
sessionId: config.sessionId,
pm2Id: pm2Name,
logFile: config.logFile,
startTime: /* @__PURE__ */ new Date()
};
this.daemons.set(config.sessionId, daemon);
this.saveDaemonPids().then(() => resolve$1(daemon)).catch(reject);
});
});
});
}
/**
* Stop a daemon process using PM2
*/
async stopDaemon(sessionId) {
const daemon = this.daemons.get(sessionId);
if (!daemon) return false;
return new Promise((resolve$1, reject) => {
const stopCommand$1 = this.preparePm2Command(["stop", daemon.pm2Id]);
spawn(stopCommand$1.binary, stopCommand$1.args, { stdio: "ignore" }).on("close", (_code) => {
const deleteCommand = this.preparePm2Command(["delete", daemon.pm2Id]);
spawn(deleteCommand.binary, deleteCommand.args, { stdio: "ignore" }).on("close", () => {
this.daemons.delete(sessionId);
this.saveDaemonPids().then(() => resolve$1(true)).catch(reject);
});
});
});
}
/**
* Check if a daemon is running for a session
*/
isDaemonRunning(sessionId) {
if (!this.daemons.get(sessionId)) return false;
return true;
}
/**
* Get daemon info for a session
*/
getDaemon(sessionId) {
return this.daemons.get(sessionId);
}
/**
* Get all running daemons
*/
getAllDaemons() {
return Array.from(this.daemons.values());
}
/**
* Stop all daemons
*/
async stopAllDaemons() {
const sessionIds = Array.from(this.daemons.keys());
await Promise.all(sessionIds.map(async (id) => this.stopDaemon(id)));
}
/**
* Load daemon PIDs from file (for recovery after restart)
*/
async loadDaemonPids() {
try {
const data = await promises.readFile(this.daemonPidsFile, "utf-8");
const loadPromises = JSON.parse(data).map(async (pidInfo) => {
if (!pidInfo.pm2Id) return;
try {
if (await this.checkPm2ProcessExists(pidInfo.pm2Id)) this.daemons.set(pidInfo.sessionId, {
sessionId: pidInfo.sessionId,
pm2Id: pidInfo.pm2Id,
logFile: pidInfo.logFile,
startTime: new Date(pidInfo.startTime)
});
} catch {}
});
await Promise.all(loadPromises);
} catch {}
}
/**
* Check if a PM2 process exists
*/
async checkPm2ProcessExists(pm2Id) {
return new Promise((resolve$1) => {
try {
const describeCommand = this.preparePm2Command(["describe", pm2Id]);
const checkProcess = spawn(describeCommand.binary, describeCommand.args, { stdio: [
"ignore",
"pipe",
"pipe"
] });
const timeout = setTimeout(() => {
checkProcess.kill("SIGTERM");
resolve$1(false);
}, 5e3);
checkProcess.on("close", (code) => {
clearTimeout(timeout);
resolve$1(code === 0);
});
checkProcess.on("error", () => {
clearTimeout(timeout);
resolve$1(false);
});
} catch {
resolve$1(false);
}
});
}
/**
* Force stop and delete a PM2 process by name
*/
async forceStopPm2Process(pm2Name) {
return new Promise((resolve$1) => {
const stopCommand$1 = this.preparePm2Command(["stop", pm2Name]);
const stopProcess = spawn(stopCommand$1.binary, stopCommand$1.args, { stdio: [
"ignore",
"pipe",
"pipe"
] });
stopProcess.stderr?.on("data", () => {});
stopProcess.on("close", (_stopCode) => {
const deleteCommand = this.preparePm2Command(["delete", pm2Name]);
const deleteProcess = spawn(deleteCommand.binary, deleteCommand.args, { stdio: [
"ignore",
"pipe",
"pipe"
] });
let deleteError = "";
deleteProcess.stderr?.on("data", (data) => {
deleteError += data.toString();
});
deleteProcess.on("close", (deleteCode) => {
if (deleteCode === 0) resolve$1();
else {
console.warn(`Failed to force-delete PM2 process ${pm2Name}: ${deleteError}`);
resolve$1();
}
});
deleteProcess.on("error", (error) => {
console.warn(`Error deleting PM2 process ${pm2Name}:`, error.message);
resolve$1();
});
});
stopProcess.on("error", (error) => {
console.warn(`Error stopping PM2 process ${pm2Name}:`, error.message);
const deleteCommand = this.preparePm2Command(["delete", pm2Name]);
const deleteProcess = spawn(deleteCommand.binary, deleteCommand.args, { stdio: "ignore" });
deleteProcess.on("close", () => resolve$1());
deleteProcess.on("error", () => resolve$1());
});
});
}
/**
* Save daemon PIDs to file
*/
async saveDaemonPids() {
try {
await promises.mkdir(this.globalConfigDir, { recursive: true });
const pids = Array.from(this.daemons.values()).map((daemon) => ({
sessionId: daemon.sessionId,
pm2Id: daemon.pm2Id,
logFile: daemon.logFile,
startTime: daemon.startTime.toISOString()
}));
await promises.writeFile(this.daemonPidsFile, JSON.stringify(pids, null, 2));
} catch (error) {
console.info("Failed to save daemon PIDs:", error);
}
}
};
const daemonManager = new DaemonManager();
//#endregion
//#region src/commands/clean.ts
const cleanCommand = define({
name: "clean",
description: "Remove ended and dead sessions, archive log files",
args: {
"dry-run": {
type: "boolean",
description: "Show what would be cleaned without making changes"
},
"all": {
type: "boolean",
description: "Clean sessions from all projects (default: current project only)"
}
},
async run(ctx) {
const { "dry-run": dryRun, "all": cleanAll } = ctx.values;
if (dryRun) consola$1.info("🔍 Running in dry-run mode - no changes will be made");
if (cleanAll) consola$1.info("🌍 Cleaning sessions from all projects");
else consola$1.info("📁 Cleaning sessions from current project only (use --all to clean all projects)");
consola$1.start("Cleaning up sessions...");
try {
const sessionManager = new SessionManager();
const tmuxManager = new TmuxManager();
await sessionManager.initialize();
let discordBot = null;
const botToken = process.env.CCREMOTE_DISCORD_BOT_TOKEN;
const ownerId = process.env.CCREMOTE_DISCORD_OWNER_ID;
if (botToken && ownerId) try {
discordBot = new DiscordBot(sessionManager, tmuxManager);
const authorizedUsersConfig = process.env.CCREMOTE_DISCORD_AUTHORIZED_USERS;
const authorizedUsers = authorizedUsersConfig ? authorizedUsersConfig.split(",").map((u$1) => u$1.trim()) : [];
await discordBot.start(botToken, ownerId, authorizedUsers);
consola$1.info("🤖 Discord bot initialized for channel cleanup");
} catch (error) {
consola$1.warn(`Failed to initialize Discord bot: ${error instanceof Error ? error.message : String(error)}`);
discordBot = null;
}
await daemonManager.ensureInitialized();
const sessions = cleanAll ? await sessionManager.listSession