consortium
Version:
Remote control and session sharing CLI for AI coding agents
141 lines (125 loc) • 5.32 kB
JavaScript
/**
* consortium-local — deprecated alias for `consortium code`.
*
* Historically this was the primary entry point for launching Consortium Code
* locally. The main `consortium` binary now ships an interactive TUI as its
* default no-args action, and `consortium code` is the supported direct
* launcher, so this shim exists only to guide existing users across.
*
* Behavior:
* - Prints a visible, multi-line deprecation banner to stderr (ANSI-colored
* only when stderr is a TTY — same postinstall-safe pattern used in
* scripts/verify-platform.cjs, no chalk dependency).
* - Execs `consortium code` with the same argv, stdio inheritance, and
* propagates the child's exit code.
* - Honors CONSORTIUM_HIDE_DEPRECATION_WARNING=1 as a silent escape hatch
* for automation / scripts that can't tolerate the extra output.
*
* This alias will be removed in the next major release.
*/
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
// --- ANSI helpers (no chalk — this file runs without devDeps guaranteed) ----
const isTTY = process.stderr.isTTY === true;
const ansi = (code, text) => (isTTY ? `\u001b[${code}m${text}\u001b[0m` : text);
const bold = (t) => ansi('1', t);
const yellow = (t) => ansi('33', t);
const dim = (t) => ansi('2', t);
const MIGRATION_URL =
'https://github.com/ConsortiumAI/consortium-cli#migrating-from-consortium-local';
/**
* Strip ANSI SGR escape sequences for visible-width calculations. ANSI bytes
* are part of the string's .length but render as zero columns, so naive
* padding breaks box alignment whenever bold/yellow/dim wrap a segment.
*/
function visibleLength(s) {
// Matches CSI-style SGR sequences like "\u001b[1m" / "\u001b[33m" / "\u001b[0m".
// eslint-disable-next-line no-control-regex
return s.replace(/\u001b\[[0-9;]*m/g, '').length;
}
/**
* Print the multi-line deprecation banner to stderr. Rendered with box-drawing
* characters and ANSI color when the stream is a TTY; falls back to plain text
* otherwise so piped output / CI logs stay readable.
*/
function printDeprecationBanner() {
const W = 84; // inner width of the box — fits the migration URL on one line
const pad = (text, width) => {
const visible = visibleLength(text);
if (visible >= width) return text;
return text + ' '.repeat(width - visible);
};
const top = '\u250c' + '\u2500'.repeat(W) + '\u2510';
const bot = '\u2514' + '\u2500'.repeat(W) + '\u2518';
const sep = (content) => '\u2502' + pad(content, W) + '\u2502';
const lines = [
top,
sep(' ' + bold(yellow('DEPRECATED')) + ' `consortium-local` is deprecated.'),
sep(''),
sep(' Run `consortium code` to launch Consortium Code directly,'),
sep(' or `consortium` to open the interactive menu.'),
sep(''),
sep(' This alias will be removed in the next major release.'),
sep(' Migration guide:'),
sep(' ' + dim(MIGRATION_URL)),
bot,
'',
];
process.stderr.write(lines.join('\n') + '\n');
}
function main() {
const hideWarning = process.env.CONSORTIUM_HIDE_DEPRECATION_WARNING === '1';
if (!hideWarning) {
printDeprecationBanner();
}
// Resolve sibling `consortium.mjs` so we always hit the same CLI build,
// regardless of PATH ordering or global install location.
const here = dirname(fileURLToPath(import.meta.url));
const consortiumBin = join(here, 'consortium.mjs');
// If the first arg is a recognized top-level subcommand, forward it
// directly to `consortium` instead of prepending `code`. Without this,
// commands like `consortium-local auth login --force` were rewritten to
// `consortium code auth login --force` — the Code launcher silently
// ignored the unknown flags, skipped re-auth, and dropped users into the
// Code TUI. The default case (no subcommand, or args meant for Code)
// still routes through `consortium code` to preserve the historical
// alias behavior.
const argv = process.argv.slice(2);
const TOP_LEVEL_SUBCOMMANDS = new Set([
'auth', 'doctor', 'connect', 'orgs', 'switch-org', 'whoami',
'deployments', 'codex', 'gemini', 'code', 'opencode', 'logout',
'notify', 'daemon', 'register-machine', 'claude', 'help',
'--help', '-h', '--version', '-v',
]);
const first = argv[0];
const passthrough = first && TOP_LEVEL_SUBCOMMANDS.has(first);
const childArgs = passthrough
? [consortiumBin, ...argv]
: [consortiumBin, 'code', ...argv];
// Inherit stdio + env so the child is indistinguishable from a direct
// `consortium` invocation. spawnSync (vs execFileSync) lets us
// propagate the exact exit code including signal-kill cases.
const result = spawnSync(
process.execPath,
childArgs,
{ stdio: 'inherit', env: process.env },
);
if (result.error) {
process.stderr.write(
`consortium-local: failed to exec \`consortium code\`: ${result.error.message}\n`,
);
process.exit(1);
}
if (typeof result.status === 'number') {
process.exit(result.status);
}
if (result.signal) {
// Child died to a signal — surface that as a non-zero exit so callers
// can detect abnormal termination.
process.exit(128);
}
process.exit(0);
}
main();