138 lines
5.7 KiB
TypeScript
138 lines
5.7 KiB
TypeScript
import { Command } from "commander";
|
|
import { onboard } from "./commands/onboard.js";
|
|
import { doctor } from "./commands/doctor.js";
|
|
import { envCommand } from "./commands/env.js";
|
|
import { configure } from "./commands/configure.js";
|
|
import { addAllowedHostname } from "./commands/allowed-hostname.js";
|
|
import { heartbeatRun } from "./commands/heartbeat-run.js";
|
|
import { runCommand } from "./commands/run.js";
|
|
import { bootstrapCeoInvite } from "./commands/auth-bootstrap-ceo.js";
|
|
import { registerContextCommands } from "./commands/client/context.js";
|
|
import { registerCompanyCommands } from "./commands/client/company.js";
|
|
import { registerIssueCommands } from "./commands/client/issue.js";
|
|
import { registerAgentCommands } from "./commands/client/agent.js";
|
|
import { registerApprovalCommands } from "./commands/client/approval.js";
|
|
import { registerActivityCommands } from "./commands/client/activity.js";
|
|
import { registerDashboardCommands } from "./commands/client/dashboard.js";
|
|
import { applyDataDirOverride, type DataDirOptionLike } from "./config/data-dir.js";
|
|
|
|
const program = new Command();
|
|
const DATA_DIR_OPTION_HELP =
|
|
"Paperclip data directory root (isolates state from ~/.paperclip)";
|
|
|
|
program
|
|
.name("paperclipai")
|
|
.description("Paperclip CLI — setup, diagnose, and configure your instance")
|
|
.version("0.2.2");
|
|
|
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
const options = actionCommand.optsWithGlobals() as DataDirOptionLike;
|
|
const optionNames = new Set(actionCommand.options.map((option) => option.attributeName()));
|
|
applyDataDirOverride(options, {
|
|
hasConfigOption: optionNames.has("config"),
|
|
hasContextOption: optionNames.has("context"),
|
|
});
|
|
});
|
|
|
|
program
|
|
.command("onboard")
|
|
.description("Interactive first-run setup wizard")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("-y, --yes", "Accept defaults (quickstart + start immediately)", false)
|
|
.option("--run", "Start Paperclip immediately after saving config", false)
|
|
.action(onboard);
|
|
|
|
program
|
|
.command("doctor")
|
|
.description("Run diagnostic checks on your Paperclip setup")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("--repair", "Attempt to repair issues automatically")
|
|
.alias("--fix")
|
|
.option("-y, --yes", "Skip repair confirmation prompts")
|
|
.action(async (opts) => {
|
|
await doctor(opts);
|
|
});
|
|
|
|
program
|
|
.command("env")
|
|
.description("Print environment variables for deployment")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.action(envCommand);
|
|
|
|
program
|
|
.command("configure")
|
|
.description("Update configuration sections")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("-s, --section <section>", "Section to configure (llm, database, logging, server, storage, secrets)")
|
|
.action(configure);
|
|
|
|
program
|
|
.command("allowed-hostname")
|
|
.description("Allow a hostname for authenticated/private mode access")
|
|
.argument("<host>", "Hostname to allow (for example dotta-macbook-pro)")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.action(addAllowedHostname);
|
|
|
|
program
|
|
.command("run")
|
|
.description("Bootstrap local setup (onboard + doctor) and run Paperclip")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("-i, --instance <id>", "Local instance id (default: default)")
|
|
.option("--repair", "Attempt automatic repairs during doctor", true)
|
|
.option("--no-repair", "Disable automatic repairs during doctor")
|
|
.action(runCommand);
|
|
|
|
const heartbeat = program.command("heartbeat").description("Heartbeat utilities");
|
|
|
|
heartbeat
|
|
.command("run")
|
|
.description("Run one agent heartbeat and stream live logs")
|
|
.requiredOption("-a, --agent-id <agentId>", "Agent ID to invoke")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("--context <path>", "Path to CLI context file")
|
|
.option("--profile <name>", "CLI context profile name")
|
|
.option("--api-base <url>", "Base URL for the Paperclip server API")
|
|
.option("--api-key <token>", "Bearer token for agent-authenticated calls")
|
|
.option(
|
|
"--source <source>",
|
|
"Invocation source (timer | assignment | on_demand | automation)",
|
|
"on_demand",
|
|
)
|
|
.option("--trigger <trigger>", "Trigger detail (manual | ping | callback | system)", "manual")
|
|
.option("--timeout-ms <ms>", "Max time to wait before giving up", "0")
|
|
.option("--json", "Output raw JSON where applicable")
|
|
.option("--debug", "Show raw adapter stdout/stderr JSON chunks")
|
|
.action(heartbeatRun);
|
|
|
|
registerContextCommands(program);
|
|
registerCompanyCommands(program);
|
|
registerIssueCommands(program);
|
|
registerAgentCommands(program);
|
|
registerApprovalCommands(program);
|
|
registerActivityCommands(program);
|
|
registerDashboardCommands(program);
|
|
|
|
const auth = program.command("auth").description("Authentication and bootstrap utilities");
|
|
|
|
auth
|
|
.command("bootstrap-ceo")
|
|
.description("Create a one-time bootstrap invite URL for first instance admin")
|
|
.option("-c, --config <path>", "Path to config file")
|
|
.option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
|
|
.option("--force", "Create new invite even if admin already exists", false)
|
|
.option("--expires-hours <hours>", "Invite expiration window in hours", (value) => Number(value))
|
|
.option("--base-url <url>", "Public base URL used to print invite link")
|
|
.action(bootstrapCeoInvite);
|
|
|
|
program.parseAsync().catch((err) => {
|
|
console.error(err instanceof Error ? err.message : String(err));
|
|
process.exit(1);
|
|
});
|