import { Command } from "@cliffy/command"; import { generateShellCompletions } from "@cliffy/command/completions"; import { UpgradeCommand } from "@cliffy/command/upgrade"; import * as log from "./core/log.ts"; import { realpathSync } from "node:fs"; import { fileURLToPath } from "node:url"; import flow from "./commands/flow/flow.ts"; import app from "./commands/app/app.ts"; import script from "./commands/script/script.ts"; import workspace, { getActiveWorkspace, } from "./commands/workspace/workspace.ts"; import resource from "./commands/resource/resource.ts"; import resourceType from "./commands/resource-type/resource-type.ts"; import user from "./commands/user/user.ts"; import variable from "./commands/variable/variable.ts"; import hub from "./commands/hub/hub.ts"; import folder from "./commands/folder/folder.ts"; import schedule from "./commands/schedule/schedule.ts"; import trigger from "./commands/trigger/trigger.ts"; import sync from "./commands/sync/sync.ts"; import gitsyncSettings from "./commands/gitsync-settings/gitsync-settings.ts"; import instance from "./commands/instance/instance.ts"; import workerGroups from "./commands/worker-groups/worker-groups.ts"; import lint from "./commands/lint/lint.ts"; import dev from "./commands/dev/dev.ts"; import { GlobalOptions } from "./types.ts"; import { OpenAPI } from "../gen/index.ts"; import { getHeaders } from "./utils/utils.ts"; import { setShowDiffs } from "./core/conf.ts"; import { NpmProvider } from "./utils/upgrade.ts"; import { pull as hubPull } from "./commands/hub/hub.ts"; import { pull, push } from "./commands/sync/sync.ts"; import { add as workspaceAdd } from "./commands/workspace/workspace.ts"; import workers from "./commands/workers/workers.ts"; import queues from "./commands/queues/queues.ts"; import dependencies from "./commands/dependencies/dependencies.ts"; import init from "./commands/init/init.ts"; import jobs from "./commands/jobs/jobs.ts"; import generateMetadata from "./commands/generate-metadata/generate-metadata.ts"; import docs from "./commands/docs/docs.ts"; import { fetchVersion } from "./core/context.ts"; export { flow, app, script, workspace, resource, resourceType, user, variable, hub, folder, schedule, trigger, sync, lint, gitsyncSettings, instance, dev, docs, hubPull, pull, push, workspaceAdd, }; export const VERSION = "1.655.0"; // Re-exported from constants.ts to maintain backwards compatibility export { WM_FORK_PREFIX } from "./core/constants.ts"; const command = new Command() .name("wmill") .action(() => log.info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`) ) .description("Windmill CLI") .globalOption( "--workspace ", "Specify the target workspace. This overrides the default workspace." ) .globalOption("--debug --verbose", "Show debug/verbose logs") .globalOption( "--show-diffs", "Show diff informations when syncing (may show sensitive informations)" ) .globalOption( "--token ", "Specify an API token. This will override any stored token." ) .globalOption( "--base-url ", "Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used." ) .globalOption( "--config-dir ", "Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location." ) .env( "HEADERS ", "Specify headers to use for all requests. e.g: \"HEADERS='h1: v1, h2: v2'\"" ) .version(VERSION) .versionOption(false) .command("init", init) .command("app", app) .command("flow", flow) .command("script", script) .command("workspace", workspace) .command("resource", resource) .command("resource-type", resourceType) .command("user", user) .command("variable", variable) .command("hub", hub) .command("folder", folder) .command("schedule", schedule) .command("trigger", trigger) .command("dev", dev) .command("sync", sync) .command("lint", lint) .command("gitsync-settings", gitsyncSettings) .command("instance", instance) .command("worker-groups", workerGroups) .command("workers", workers) .command("queues", queues) .command("dependencies", dependencies) .command("jobs", jobs) .command("generate-metadata", generateMetadata) .command("docs", docs) .command("version --version", "Show version information") .action(async (opts: any) => { console.log("CLI version: " + VERSION); try { const provider = new NpmProvider({ package: "windmill-cli" }); const versions = await provider.getVersions("windmill-cli"); if (versions.latest !== VERSION) { console.log( `CLI is outdated. Latest version ${versions.latest} is available. Run \`wmill upgrade\` to update.` ); } else { console.log("CLI is up to date"); } } catch (e) { console.warn( `Cannot fetch latest CLI version on npmjs to check if up-to-date: ${e}` ); } const workspace = await getActiveWorkspace(opts as GlobalOptions); if (workspace) { try { const backendVersion = await fetchVersion(workspace.remote); console.log("Backend Version: " + backendVersion); } catch (e) { console.warn( `Cannot fetch backend version from ${workspace.remote} (workspace: ${workspace.name}): ${e}` ); } } else { console.warn( "Cannot fetch backend version: no active workspace selected, choose one to pick a remote to fetch version of" ); } }) .command( "upgrade", new UpgradeCommand({ provider: new NpmProvider({ package: "windmill-cli" }), }).error((e: any) => { log.error(e); log.info( "Try running with sudo and otherwise check the result of the command: npm uninstall windmill-cli && npm install -g windmill-cli" ); }) ) .command( "completions", new Command() .description("Generate shell completions.") .command("bash", new Command().description("Generate bash completions.").action(() => { process.stdout.write(generateShellCompletions(command, "bash") + "\n"); })) .command("zsh", new Command().description("Generate zsh completions.").action(() => { process.stdout.write(generateShellCompletions(command, "zsh") + "\n"); })) .command("fish", new Command().description("Generate fish completions.").action(() => { process.stdout.write(generateShellCompletions(command, "fish") + "\n"); })) ); async function main() { try { const args = process.argv.slice(2); if (args.length === 0) { command.showHelp(); } const LOG_LEVEL = args.includes("--verbose") || args.includes("--debug") ? "DEBUG" : "INFO"; // const NO_COLORS = args.includes("--no-colors"); setShowDiffs(args.includes("--show-diffs")); log.setup(LOG_LEVEL); log.debug("Debug logging enabled. CLI build against " + VERSION); const extraHeaders = getHeaders(); if (extraHeaders) { OpenAPI.HEADERS = extraHeaders; } await command.parse(args); } catch (e) { if (e && typeof e === "object" && "name" in e && e.name === "ApiError") { console.log( "Server failed. " + (e as any).statusText + ": " + (e as any).body ); } throw e; } } function isMain() { // Handle symlinks properly: resolve symlinks when comparing process.argv[1] // with import.meta.url, so `wmill` symlink matches the real file path. try { const scriptPath = process.argv[1]; if (!scriptPath) return false; const realScriptPath = realpathSync(scriptPath); const modulePath = fileURLToPath(import.meta.url); return realScriptPath === modulePath; } catch { return false; } } if (isMain()) { main().then(() => { // Destroy stdin so interactive prompts (Cliffy) don't keep the event loop alive process.stdin.destroy(); }); } export default command;