@mutagent/cli 0.1.396 → 0.1.398
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -17
- package/dist/bin/cli.js +31120 -25907
- package/dist/index.js +559 -322
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -425,116 +425,357 @@ var init_config = __esm(() => {
|
|
|
425
425
|
CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
|
|
426
426
|
});
|
|
427
427
|
|
|
428
|
-
// src/lib/
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
428
|
+
// src/lib/command-path.ts
|
|
429
|
+
function commandPathOf(command) {
|
|
430
|
+
const names = [];
|
|
431
|
+
for (let current = command;current.parent; current = current.parent) {
|
|
432
|
+
names.unshift(current.name());
|
|
432
433
|
}
|
|
433
|
-
|
|
434
|
-
return new MutagentError("UNKNOWN_ERROR", error.message, undefined, 1);
|
|
435
|
-
}
|
|
436
|
-
return new MutagentError("UNKNOWN_ERROR", typeof error === "string" ? error : "An unknown error occurred", undefined, 1);
|
|
434
|
+
return names.join(" ");
|
|
437
435
|
}
|
|
438
|
-
function
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
436
|
+
function setRunningCommandPath(path) {
|
|
437
|
+
runningPath = path;
|
|
438
|
+
}
|
|
439
|
+
function runningHelpCommand() {
|
|
440
|
+
return runningPath ? `mutagent ${runningPath} --help` : "mutagent --help";
|
|
441
|
+
}
|
|
442
|
+
var runningPath;
|
|
443
|
+
|
|
444
|
+
// src/lib/output.ts
|
|
445
|
+
import chalk from "chalk";
|
|
446
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
447
|
+
import { join as join2 } from "path";
|
|
448
|
+
function setCliVersion(version) {
|
|
449
|
+
_globalCliVersion = version;
|
|
450
|
+
_compatCache = null;
|
|
451
|
+
}
|
|
452
|
+
function getCliVersion() {
|
|
453
|
+
return _globalCliVersion;
|
|
454
|
+
}
|
|
455
|
+
function getCompatMetadata(cliVersion = _globalCliVersion) {
|
|
456
|
+
if (_compatCache !== null)
|
|
457
|
+
return _compatCache;
|
|
458
|
+
let skillVersion = "unknown";
|
|
459
|
+
let skillMinCliVersion = "unknown";
|
|
460
|
+
try {
|
|
461
|
+
const candidatePaths = [
|
|
462
|
+
join2(process.cwd(), ".claude", "skills", "mutagent-cli", "SKILL.md"),
|
|
463
|
+
join2(process.cwd(), "mutagent-cli", ".claude", "skills", "mutagent-cli", "SKILL.md")
|
|
464
|
+
];
|
|
465
|
+
for (const p of candidatePaths) {
|
|
466
|
+
if (existsSync2(p)) {
|
|
467
|
+
const content = readFileSync2(p, "utf-8");
|
|
468
|
+
const verMatch = /^\s+skill_version:\s+"?([^"\n]+)"?\s*$/m.exec(content);
|
|
469
|
+
const minMatch = /^\s+skill_min_cli_version:\s+"?([^"\n]+)"?\s*$/m.exec(content);
|
|
470
|
+
if (/^metadata:/m.test(content) && !verMatch && !minMatch) {
|
|
471
|
+
throw new Error("Bundled SKILL.md is missing metadata.skill_version / skill_min_cli_version" + " — re-run `bun run sync-skill` to regenerate.");
|
|
472
|
+
}
|
|
473
|
+
if (verMatch?.[1])
|
|
474
|
+
skillVersion = verMatch[1].trim();
|
|
475
|
+
if (minMatch?.[1])
|
|
476
|
+
skillMinCliVersion = minMatch[1].trim();
|
|
477
|
+
break;
|
|
461
478
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
if (err instanceof Error && err.message.startsWith("Bundled SKILL.md is missing")) {
|
|
482
|
+
throw err;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
_compatCache = { cliVersion, skillVersion, skillMinCliVersion };
|
|
486
|
+
return _compatCache;
|
|
487
|
+
}
|
|
488
|
+
function getJsonFlag(command) {
|
|
489
|
+
if (!command)
|
|
490
|
+
return false;
|
|
491
|
+
const parentOpts = command.parent ? command.parent.opts() : null;
|
|
492
|
+
const ownOpts = command.opts();
|
|
493
|
+
return Boolean(parentOpts?.json ?? ownOpts.json);
|
|
494
|
+
}
|
|
495
|
+
function startJsonLines() {
|
|
496
|
+
jsonLines = true;
|
|
497
|
+
}
|
|
498
|
+
function jsonText(value) {
|
|
499
|
+
return jsonLines ? JSON.stringify(value) : JSON.stringify(value, null, 2);
|
|
500
|
+
}
|
|
501
|
+
function withSuccess(value) {
|
|
502
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
503
|
+
return value;
|
|
504
|
+
if ("success" in value)
|
|
505
|
+
return value;
|
|
506
|
+
return { success: true, ...value };
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
class OutputFormatter {
|
|
510
|
+
format;
|
|
511
|
+
constructor(format) {
|
|
512
|
+
this.format = format;
|
|
513
|
+
}
|
|
514
|
+
output(data) {
|
|
515
|
+
const clean = scrubKnownSuccessDeep(data);
|
|
516
|
+
if (this.format === "json") {
|
|
517
|
+
let enriched = withSuccess(clean);
|
|
518
|
+
if (_globalCliVersion !== "0.0.0" && clean !== null && typeof clean === "object" && !Array.isArray(clean) && "_directive" in clean) {
|
|
519
|
+
enriched = {
|
|
520
|
+
...enriched,
|
|
521
|
+
_compat: getCompatMetadata(_globalCliVersion)
|
|
468
522
|
};
|
|
469
523
|
}
|
|
470
|
-
console.log(
|
|
524
|
+
console.log(jsonText(enriched));
|
|
471
525
|
} else {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
console.error("Run `mutagent <command> --help` for usage information.");
|
|
496
|
-
}
|
|
526
|
+
this.formatTable(clean);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
formatTable(data) {
|
|
530
|
+
if (Array.isArray(data)) {
|
|
531
|
+
this.formatArray(data);
|
|
532
|
+
} else if (data && typeof data === "object") {
|
|
533
|
+
this.formatObject(data);
|
|
534
|
+
} else {
|
|
535
|
+
console.log(data);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
formatArray(data) {
|
|
539
|
+
if (data.length === 0) {
|
|
540
|
+
console.log(chalk.gray("No results"));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const firstItem = data[0];
|
|
544
|
+
if (!firstItem || typeof firstItem !== "object") {
|
|
545
|
+
data.forEach((item) => {
|
|
546
|
+
console.log(` ${String(item)}`);
|
|
547
|
+
});
|
|
548
|
+
return;
|
|
497
549
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
550
|
+
const keys = Object.keys(firstItem);
|
|
551
|
+
const toDisplayString = (val) => {
|
|
552
|
+
if (val === null || val === undefined)
|
|
553
|
+
return "";
|
|
554
|
+
if (typeof val === "object")
|
|
555
|
+
return JSON.stringify(val);
|
|
556
|
+
if (typeof val === "string")
|
|
557
|
+
return val;
|
|
558
|
+
if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") {
|
|
559
|
+
return val.toString();
|
|
560
|
+
}
|
|
561
|
+
return JSON.stringify(val);
|
|
562
|
+
};
|
|
563
|
+
const widths = {};
|
|
564
|
+
keys.forEach((key) => {
|
|
565
|
+
widths[key] = Math.max(key.length, ...data.map((item) => toDisplayString(item[key]).length));
|
|
566
|
+
});
|
|
567
|
+
const header = keys.map((key) => chalk.bold(key.toUpperCase().padEnd(widths[key] ?? 10))).join(" ");
|
|
568
|
+
console.log(header);
|
|
569
|
+
console.log(keys.map((key) => "-".repeat(widths[key] ?? 10)).join(" "));
|
|
570
|
+
data.forEach((item) => {
|
|
571
|
+
const row = keys.map((key) => {
|
|
572
|
+
const value = toDisplayString(item[key]);
|
|
573
|
+
const width = widths[key] ?? 10;
|
|
574
|
+
return value.padEnd(width).slice(0, width);
|
|
575
|
+
}).join(" ");
|
|
576
|
+
console.log(row);
|
|
577
|
+
});
|
|
578
|
+
console.log(chalk.gray(`
|
|
579
|
+
${String(data.length)} result(s)`));
|
|
580
|
+
}
|
|
581
|
+
formatObject(data) {
|
|
582
|
+
const maxKeyLength = Math.max(...Object.keys(data).map((k) => k.length));
|
|
583
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
584
|
+
const formattedKey = chalk.bold(key.padEnd(maxKeyLength));
|
|
585
|
+
let formattedValue;
|
|
586
|
+
if (value === null || value === undefined) {
|
|
587
|
+
formattedValue = "";
|
|
588
|
+
} else if (typeof value === "object") {
|
|
589
|
+
formattedValue = JSON.stringify(value, null, 2);
|
|
590
|
+
} else if (typeof value === "string") {
|
|
591
|
+
formattedValue = value;
|
|
592
|
+
} else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
593
|
+
formattedValue = value.toString();
|
|
507
594
|
} else {
|
|
508
|
-
|
|
509
|
-
if (wrapped.suggestion) {
|
|
510
|
-
console.error(`Suggestion: ${wrapped.suggestion}`);
|
|
511
|
-
}
|
|
595
|
+
formattedValue = JSON.stringify(value);
|
|
512
596
|
}
|
|
513
|
-
|
|
597
|
+
console.log(`${formattedKey} ${formattedValue}`);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
success(message) {
|
|
601
|
+
const text = scrubKnownSuccess(message);
|
|
602
|
+
if (this.format === "json") {
|
|
603
|
+
console.log(JSON.stringify({ success: true, message: text }, null, 2));
|
|
604
|
+
} else {
|
|
605
|
+
console.log(chalk.green(`✓ ${text}`));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
error(message, code, suggestedAction) {
|
|
609
|
+
const text = scrubKnownSuccess(message);
|
|
610
|
+
const action = suggestedAction === undefined ? undefined : scrubKnownSuccess(suggestedAction);
|
|
611
|
+
if (this.format === "json") {
|
|
612
|
+
const result = { success: false, error: text };
|
|
613
|
+
if (code)
|
|
614
|
+
result.code = code;
|
|
615
|
+
if (action)
|
|
616
|
+
result.suggestedAction = action;
|
|
617
|
+
console.log(JSON.stringify(result, null, 2));
|
|
618
|
+
} else {
|
|
619
|
+
console.log(chalk.red(`✗ ${text}`));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
info(message) {
|
|
623
|
+
if (this.format !== "json") {
|
|
624
|
+
console.log(chalk.blue(`ℹ ${scrubKnownSuccess(message)}`));
|
|
514
625
|
}
|
|
515
626
|
}
|
|
516
|
-
|
|
627
|
+
warn(message) {
|
|
628
|
+
const text = scrubKnownSuccess(message);
|
|
629
|
+
if (this.format === "json") {
|
|
630
|
+
console.log(JSON.stringify({ warning: text }, null, 2));
|
|
631
|
+
} else {
|
|
632
|
+
console.log(chalk.yellow(`⚠ ${text}`));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function createSpinner(text, isJson) {
|
|
637
|
+
if (isJson || !process.stderr.isTTY || process.env.CI === "true") {
|
|
638
|
+
const noop = () => {
|
|
639
|
+
return;
|
|
640
|
+
};
|
|
641
|
+
return {
|
|
642
|
+
start: noop,
|
|
643
|
+
stop: noop,
|
|
644
|
+
succeed: noop,
|
|
645
|
+
fail: noop
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const start = async () => {
|
|
649
|
+
const { default: ora } = await import("ora");
|
|
650
|
+
return ora(text).start();
|
|
651
|
+
};
|
|
652
|
+
return { start };
|
|
653
|
+
}
|
|
654
|
+
var _compatCache = null, _globalCliVersion = "0.0.0", jsonLines = false;
|
|
655
|
+
var init_output = __esm(() => {
|
|
656
|
+
init_known_secrets();
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// src/lib/errors.ts
|
|
660
|
+
function exitCodeFor(code) {
|
|
661
|
+
if (KEY_REJECTED_CODES.has(code))
|
|
662
|
+
return EXIT_CODES.keyExpiredOrInvalid;
|
|
663
|
+
if (NOT_SIGNED_IN_CODES.has(code))
|
|
664
|
+
return EXIT_CODES.notSignedInOrNoWorkspace;
|
|
665
|
+
return EXIT_CODES.failure;
|
|
666
|
+
}
|
|
667
|
+
function createErrorFromUnknown(error) {
|
|
668
|
+
if (error instanceof MutagentError) {
|
|
669
|
+
return error;
|
|
670
|
+
}
|
|
671
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "An unknown error occurred";
|
|
672
|
+
return new MutagentError("UNKNOWN_ERROR", message);
|
|
673
|
+
}
|
|
674
|
+
function runnableCommand(suggestion) {
|
|
675
|
+
const line = suggestion?.replace(/^(Run|See|To switch):\s+/, "").trim();
|
|
676
|
+
return line !== undefined && /^mutagent [^\s]/.test(line) && !/[\n:]|\s\(/.test(line) ? line : undefined;
|
|
677
|
+
}
|
|
678
|
+
function agentGuidance(error, own) {
|
|
679
|
+
const command = runnableCommand(error.suggestion);
|
|
680
|
+
const helpMatch = error.suggestion === undefined ? null : /^Run:\s+(mutagent\s+[^\n]+--help)$/.exec(error.suggestion);
|
|
681
|
+
const signIn = error.exitCode !== EXIT_CODES.failure && error.code !== "WORKSPACE_REQUIRED";
|
|
682
|
+
return {
|
|
683
|
+
helpCommand: helpMatch?.[1] ?? runningHelpCommand(),
|
|
684
|
+
suggestion: error.suggestion,
|
|
685
|
+
fix: command === undefined ? [] : [command],
|
|
686
|
+
notes: command === undefined && error.suggestion !== undefined ? [error.suggestion] : [],
|
|
687
|
+
...signIn ? { escalate: SIGN_IN_ESCALATION } : {},
|
|
688
|
+
...own !== null && typeof own === "object" ? own : {}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function errorBody(error) {
|
|
692
|
+
const body = { ...error.toJSON() };
|
|
693
|
+
if (error instanceof AuthenticationError)
|
|
694
|
+
body.remediation = { ...AUTH_REMEDIATION };
|
|
695
|
+
if (error instanceof WorkspaceContextError) {
|
|
696
|
+
body.remediation = { setWorkspace: "mutagent workspaces use <name>", listWorkspaces: "mutagent workspaces list" };
|
|
697
|
+
}
|
|
698
|
+
if (error instanceof ApiError && error.statusCode === 403) {
|
|
699
|
+
body.remediation = {
|
|
700
|
+
interactive: SIGN_IN_COMMAND,
|
|
701
|
+
nonInteractive: "export MUTAGENT_API_KEY=<your-key>",
|
|
702
|
+
checkPermissions: "Verify your API key has the required permissions"
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
const guidance = agentGuidance(error, body._agentGuidance);
|
|
706
|
+
body._agentGuidance = guidance;
|
|
707
|
+
if (typeof body.suggestedAction !== "string" || body.suggestedAction === "") {
|
|
708
|
+
body.suggestedAction = `Run: ${String(guidance.helpCommand)}`;
|
|
709
|
+
}
|
|
710
|
+
return body;
|
|
711
|
+
}
|
|
712
|
+
function printHuman(error) {
|
|
713
|
+
console.error(`Error: ${scrubKnown(error.message)}`);
|
|
714
|
+
if (error.name === "TenancyError") {} else if (error instanceof AuthenticationError && error.code === "AUTH_REQUIRED") {
|
|
715
|
+
console.error("");
|
|
716
|
+
console.error(AUTH_REMEDIATION_MESSAGE);
|
|
717
|
+
} else if (error instanceof WorkspaceContextError) {
|
|
718
|
+
console.error("");
|
|
719
|
+
console.error(WORKSPACE_REMEDIATION_MESSAGE);
|
|
720
|
+
} else if (error instanceof ApiError && error.statusCode === 403) {
|
|
721
|
+
console.error("");
|
|
722
|
+
console.error("Access denied. To fix:");
|
|
723
|
+
console.error(` ${SIGN_IN_COMMAND} # Re-authenticate`);
|
|
724
|
+
console.error(" export MUTAGENT_API_KEY=mg_live_... # Or use a different API key");
|
|
725
|
+
} else if (error.suggestion) {
|
|
726
|
+
console.error(`Suggestion: ${scrubKnown(error.suggestion)}`);
|
|
727
|
+
}
|
|
728
|
+
const usage = ["MISSING_ARGUMENTS", "INVALID_ARGUMENTS", "INVALID_INPUT", "VALIDATION_ERROR", "UNKNOWN_OPTION", "UNKNOWN_COMMAND"];
|
|
729
|
+
if (usage.includes(error.code) && !(error.suggestion ?? "").includes(runningHelpCommand())) {
|
|
730
|
+
console.error("");
|
|
731
|
+
console.error(`Run \`${runningHelpCommand()}\` for usage information.`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function asSdkError(error) {
|
|
735
|
+
if (!error || typeof error !== "object")
|
|
736
|
+
return;
|
|
737
|
+
const sdkErr = error;
|
|
738
|
+
if (sdkErr.name !== "SDKValidationError" && sdkErr.name !== "SDKError" && sdkErr.rawValue === undefined && sdkErr.rawMessage === undefined) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
return new MutagentError("REQUEST_FAILED", scrubKnown(sdkErr.rawMessage ?? sdkErr.message ?? "Request failed"), "Check your input and try again. Run the command with --help for usage details.");
|
|
742
|
+
}
|
|
743
|
+
function toFailure(error) {
|
|
744
|
+
return error instanceof MutagentError ? error : asSdkError(error) ?? new MutagentError("UNKNOWN_ERROR", error instanceof Error ? scrubKnown(error.message) : "Unknown error", 'If it keeps happening, report it: mutagent feedback send "<what happened>" --category cli');
|
|
745
|
+
}
|
|
746
|
+
function handleError(error, isJson) {
|
|
747
|
+
const failure = toFailure(error);
|
|
517
748
|
if (isJson) {
|
|
518
|
-
console.log(
|
|
749
|
+
console.log(jsonText(scrubKnownDeep(errorBody(failure))));
|
|
519
750
|
} else {
|
|
520
|
-
|
|
751
|
+
printHuman(failure);
|
|
521
752
|
}
|
|
522
|
-
process.exit(
|
|
753
|
+
process.exit(failure.exitCode);
|
|
523
754
|
}
|
|
524
|
-
var MutagentError, AUTH_REMEDIATION_MESSAGE, AuthenticationError, ApiError, WORKSPACE_REMEDIATION_MESSAGE, WorkspaceContextError, ValidationError, NetworkError, RateLimitError, NotFoundError;
|
|
755
|
+
var EXIT_CODES, KEY_REJECTED_CODES, NOT_SIGNED_IN_CODES, EXIT_CODES_LINE = "Exit codes: 0 success · 1 failure (usage errors included) · 2 key expired or invalid · 3 not signed in or no workspace", MutagentError, SIGN_IN_COMMAND = "mutagent login --browser", SIGN_IN_WITH_KEY = "export MUTAGENT_API_KEY=<key>, then: mutagent login --json", AUTH_REMEDIATION_MESSAGE, AUTH_REMEDIATION, AuthenticationError, ApiError, WORKSPACE_REMEDIATION_MESSAGE, WorkspaceContextError, ValidationError, NetworkError, RateLimitError, NotFoundError, SIGN_IN_ESCALATION = "Signing in needs a person at a browser, or a MUTAGENT_API_KEY from them.";
|
|
525
756
|
var init_errors = __esm(() => {
|
|
526
757
|
init_known_secrets();
|
|
758
|
+
init_output();
|
|
759
|
+
EXIT_CODES = {
|
|
760
|
+
success: 0,
|
|
761
|
+
failure: 1,
|
|
762
|
+
keyExpiredOrInvalid: 2,
|
|
763
|
+
notSignedInOrNoWorkspace: 3
|
|
764
|
+
};
|
|
765
|
+
KEY_REJECTED_CODES = new Set(["AUTH_EXPIRED", "INVALID_API_KEY", "FOREIGN_API_KEY", "API_401", "SANDBOX_TOKEN_REFUSED"]);
|
|
766
|
+
NOT_SIGNED_IN_CODES = new Set(["AUTH_REQUIRED", "WORKSPACE_REQUIRED"]);
|
|
527
767
|
MutagentError = class MutagentError extends Error {
|
|
528
768
|
code;
|
|
529
769
|
suggestion;
|
|
530
|
-
|
|
531
|
-
constructor(code, message, suggestion, exitCode = 1) {
|
|
770
|
+
constructor(code, message, suggestion) {
|
|
532
771
|
super(message);
|
|
533
772
|
this.code = code;
|
|
534
773
|
this.suggestion = suggestion;
|
|
535
|
-
this.exitCode = exitCode;
|
|
536
774
|
this.name = "MutagentError";
|
|
537
775
|
}
|
|
776
|
+
get exitCode() {
|
|
777
|
+
return exitCodeFor(this.code);
|
|
778
|
+
}
|
|
538
779
|
toJSON() {
|
|
539
780
|
return {
|
|
540
781
|
success: false,
|
|
@@ -546,29 +787,34 @@ var init_errors = __esm(() => {
|
|
|
546
787
|
};
|
|
547
788
|
AUTH_REMEDIATION_MESSAGE = [
|
|
548
789
|
"Authentication required. Options:",
|
|
549
|
-
|
|
790
|
+
` Interactive: ${SIGN_IN_COMMAND}`,
|
|
550
791
|
" Non-interactive: export MUTAGENT_API_KEY=<your-key>",
|
|
551
|
-
|
|
792
|
+
` CI/CD: ${SIGN_IN_WITH_KEY}`
|
|
552
793
|
].join(`
|
|
553
794
|
`);
|
|
795
|
+
AUTH_REMEDIATION = {
|
|
796
|
+
interactive: SIGN_IN_COMMAND,
|
|
797
|
+
nonInteractive: "export MUTAGENT_API_KEY=<your-key>",
|
|
798
|
+
ciCd: SIGN_IN_WITH_KEY
|
|
799
|
+
};
|
|
554
800
|
AuthenticationError = class AuthenticationError extends MutagentError {
|
|
555
801
|
suggestions;
|
|
556
802
|
cause;
|
|
557
803
|
constructor(message, options = {}) {
|
|
558
|
-
|
|
804
|
+
const suggestion = options.suggestions ? options.suggestions[0] : `Run: ${SIGN_IN_COMMAND}`;
|
|
805
|
+
super("AUTH_REQUIRED", message ?? "Not signed in. Run: mutagent login", suggestion);
|
|
559
806
|
this.suggestions = options.suggestions ?? [
|
|
560
|
-
|
|
807
|
+
SIGN_IN_COMMAND,
|
|
561
808
|
"export MUTAGENT_API_KEY=<your-key>",
|
|
562
|
-
|
|
809
|
+
SIGN_IN_WITH_KEY
|
|
563
810
|
];
|
|
564
|
-
this.suggestion = options.suggestions ? options.suggestions[0] : "Run: mutagent auth login --browser";
|
|
565
811
|
this.cause = options.cause;
|
|
566
812
|
}
|
|
567
813
|
};
|
|
568
814
|
ApiError = class ApiError extends MutagentError {
|
|
569
815
|
statusCode;
|
|
570
816
|
constructor(status, message) {
|
|
571
|
-
super(`API_${String(status)}`, scrubKnown(message), status === 401 ? "Check your API key with: mutagent auth status" : undefined
|
|
817
|
+
super(`API_${String(status)}`, scrubKnown(message), status === 401 ? "Check your API key with: mutagent auth status" : undefined);
|
|
572
818
|
this.statusCode = status;
|
|
573
819
|
}
|
|
574
820
|
};
|
|
@@ -580,19 +826,19 @@ var init_errors = __esm(() => {
|
|
|
580
826
|
`);
|
|
581
827
|
WorkspaceContextError = class WorkspaceContextError extends MutagentError {
|
|
582
828
|
constructor(message) {
|
|
583
|
-
super("WORKSPACE_REQUIRED", message ?? "Workspace context is required but not configured", "Run: mutagent workspaces use <name>"
|
|
829
|
+
super("WORKSPACE_REQUIRED", message ?? "Workspace context is required but not configured", "Run: mutagent workspaces use <name>");
|
|
584
830
|
}
|
|
585
831
|
};
|
|
586
832
|
ValidationError = class ValidationError extends MutagentError {
|
|
587
833
|
constructor(message) {
|
|
588
|
-
super("VALIDATION_ERROR", message, "Check the command syntax with: mutagent <command> --help"
|
|
834
|
+
super("VALIDATION_ERROR", message, "Check the command syntax with: mutagent <command> --help");
|
|
589
835
|
}
|
|
590
836
|
};
|
|
591
837
|
NetworkError = class NetworkError extends MutagentError {
|
|
592
838
|
url;
|
|
593
839
|
statusCode;
|
|
594
840
|
constructor(message = "Network error", url, statusCode) {
|
|
595
|
-
super("NETWORK_ERROR", message, "Check your internet connection and verify the server URL"
|
|
841
|
+
super("NETWORK_ERROR", message, "Check your internet connection and verify the server URL");
|
|
596
842
|
this.url = url;
|
|
597
843
|
this.statusCode = statusCode;
|
|
598
844
|
}
|
|
@@ -604,12 +850,208 @@ var init_errors = __esm(() => {
|
|
|
604
850
|
this.code = "RATE_LIMIT_ERROR";
|
|
605
851
|
this.suggestion = retryAfter ? `Wait ${String(retryAfter)} seconds before retrying` : "Wait a few seconds before retrying";
|
|
606
852
|
this.retryAfter = retryAfter;
|
|
607
|
-
this.exitCode = 6;
|
|
608
853
|
}
|
|
609
854
|
};
|
|
610
855
|
NotFoundError = class NotFoundError extends MutagentError {
|
|
611
|
-
constructor(resource, id) {
|
|
612
|
-
super("NOT_FOUND", `${resource} not found: ${id}`, `Check the ID and try again. List available ${resource.toLowerCase()}s with the appropriate list command
|
|
856
|
+
constructor(resource, id, listCommand) {
|
|
857
|
+
super("NOT_FOUND", `${resource} not found: ${id}`, listCommand === undefined ? `Check the ID and try again. List available ${resource.toLowerCase()}s with the appropriate list command.` : `Run: ${listCommand}`);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// src/lib/secret-scan.ts
|
|
863
|
+
function foreignCredential(key) {
|
|
864
|
+
const hit = FOREIGN_KEY_PREFIXES.find((family) => family.prefix.test(key));
|
|
865
|
+
if (hit === undefined)
|
|
866
|
+
return;
|
|
867
|
+
const label = PATTERNS.find((pattern) => pattern.kind === hit.kind)?.label ?? "credential from another service";
|
|
868
|
+
return { kind: hit.kind, label };
|
|
869
|
+
}
|
|
870
|
+
function hasCredentialEntropy(candidate) {
|
|
871
|
+
if (candidate.length < 16)
|
|
872
|
+
return false;
|
|
873
|
+
const distinct = new Set(candidate).size;
|
|
874
|
+
if (distinct < 8)
|
|
875
|
+
return false;
|
|
876
|
+
return !/(.)\1{7,}/.test(candidate);
|
|
877
|
+
}
|
|
878
|
+
function maskSecret(secret) {
|
|
879
|
+
const head = secret.slice(0, 6);
|
|
880
|
+
return `${head}${"*".repeat(Math.min(12, Math.max(4, secret.length - head.length)))}`;
|
|
881
|
+
}
|
|
882
|
+
function collectHits(content) {
|
|
883
|
+
const accepted = [];
|
|
884
|
+
const overlaps = (start, end) => accepted.some((hit) => start < hit.end && end > hit.start);
|
|
885
|
+
for (const pattern of PATTERNS) {
|
|
886
|
+
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
887
|
+
let match;
|
|
888
|
+
while ((match = regex.exec(content)) !== null) {
|
|
889
|
+
if (match[0].length === 0) {
|
|
890
|
+
regex.lastIndex += 1;
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
const captured = match[1];
|
|
894
|
+
const secret = captured ?? match[0];
|
|
895
|
+
const start = captured === undefined ? match.index : match.index + match[0].indexOf(captured);
|
|
896
|
+
const end = start + secret.length;
|
|
897
|
+
const exemptFromEntropy = pattern.kind === "private-key";
|
|
898
|
+
if (!exemptFromEntropy && !hasCredentialEntropy(secret))
|
|
899
|
+
continue;
|
|
900
|
+
if (overlaps(start, end))
|
|
901
|
+
continue;
|
|
902
|
+
accepted.push({ kind: pattern.kind, label: pattern.label, start, end, secret });
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
return accepted.sort((a, b) => a.start - b.start);
|
|
906
|
+
}
|
|
907
|
+
function lineOf(content, offset) {
|
|
908
|
+
let line = 1;
|
|
909
|
+
for (let i = 0;i < offset && i < content.length; i++) {
|
|
910
|
+
if (content[i] === `
|
|
911
|
+
`)
|
|
912
|
+
line++;
|
|
913
|
+
}
|
|
914
|
+
return line;
|
|
915
|
+
}
|
|
916
|
+
function redactSecrets(content) {
|
|
917
|
+
if (!content)
|
|
918
|
+
return { content, matches: [] };
|
|
919
|
+
const hits = collectHits(content);
|
|
920
|
+
if (hits.length === 0)
|
|
921
|
+
return { content, matches: [] };
|
|
922
|
+
let out = "";
|
|
923
|
+
let cursor = 0;
|
|
924
|
+
for (const hit of hits) {
|
|
925
|
+
out += content.slice(cursor, hit.start);
|
|
926
|
+
out += `[REDACTED:${hit.kind}]`;
|
|
927
|
+
cursor = hit.end;
|
|
928
|
+
}
|
|
929
|
+
out += content.slice(cursor);
|
|
930
|
+
return {
|
|
931
|
+
content: out,
|
|
932
|
+
matches: hits.map((hit) => ({
|
|
933
|
+
kind: hit.kind,
|
|
934
|
+
label: hit.label,
|
|
935
|
+
line: lineOf(content, hit.start),
|
|
936
|
+
preview: maskSecret(hit.secret)
|
|
937
|
+
}))
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
var PATTERNS, FOREIGN_KEY_PREFIXES;
|
|
941
|
+
var init_secret_scan = __esm(() => {
|
|
942
|
+
PATTERNS = [
|
|
943
|
+
{
|
|
944
|
+
kind: "private-key",
|
|
945
|
+
label: "Private key block",
|
|
946
|
+
regex: /-----BEGIN(?: [A-Z]+)* PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z]+)* PRIVATE KEY-----/g
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
kind: "anthropic-api-key",
|
|
950
|
+
label: "Anthropic API key",
|
|
951
|
+
regex: /sk-ant-[A-Za-z0-9_-]{16,}/g
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
kind: "openai-api-key",
|
|
955
|
+
label: "OpenAI API key",
|
|
956
|
+
regex: /sk-(?!ant-)(?:proj-)?[A-Za-z0-9_-]{20,}/g
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
kind: "aws-access-key-id",
|
|
960
|
+
label: "AWS access key ID",
|
|
961
|
+
regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
kind: "github-token",
|
|
965
|
+
label: "GitHub token",
|
|
966
|
+
regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b|\bgithub_pat_[A-Za-z0-9_]{22,}\b/g
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
kind: "google-api-key",
|
|
970
|
+
label: "Google API key",
|
|
971
|
+
regex: /\bAIza[0-9A-Za-z_-]{35}\b/g
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
kind: "slack-token",
|
|
975
|
+
label: "Slack token",
|
|
976
|
+
regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
kind: "mutagent-api-key",
|
|
980
|
+
label: "Mutagent API key",
|
|
981
|
+
regex: /\bmt_[A-Za-z0-9]{16,}\b/g
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
kind: "bearer-token",
|
|
985
|
+
label: "Bearer token",
|
|
986
|
+
regex: /\bBearer\s+([A-Za-z0-9._-]{20,})/g
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
kind: "generic-assignment",
|
|
990
|
+
label: "Credential assignment",
|
|
991
|
+
regex: /(?:api[_-]?key|apikey|secret|password|passwd|token|access[_-]?key)["']?\s*[:=]\s*["']?([A-Za-z0-9._/+-]{16,})/gi
|
|
992
|
+
}
|
|
993
|
+
];
|
|
994
|
+
FOREIGN_KEY_PREFIXES = [
|
|
995
|
+
{ kind: "anthropic-api-key", prefix: /^sk-ant-/ },
|
|
996
|
+
{ kind: "openai-api-key", prefix: /^sk-/ },
|
|
997
|
+
{ kind: "aws-access-key-id", prefix: /^(?:AKIA|ASIA)[0-9A-Z]/ },
|
|
998
|
+
{ kind: "github-token", prefix: /^(?:gh[pousr]_|github_pat_)/ },
|
|
999
|
+
{ kind: "google-api-key", prefix: /^AIza/ },
|
|
1000
|
+
{ kind: "slack-token", prefix: /^xox[baprs]-/ }
|
|
1001
|
+
];
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// src/lib/key-check.ts
|
|
1005
|
+
async function checkApiKey(apiKey, endpoint) {
|
|
1006
|
+
let response;
|
|
1007
|
+
try {
|
|
1008
|
+
response = await fetch(`${endpoint}/api/providers`, { headers: { "x-api-key": apiKey } });
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
return { ok: false, reason: "unreachable", cause: error instanceof Error ? error.message : String(error) };
|
|
1011
|
+
}
|
|
1012
|
+
if (response.ok)
|
|
1013
|
+
return { ok: true };
|
|
1014
|
+
if (response.status === 401 || response.status === 403)
|
|
1015
|
+
return { ok: false, reason: "rejected", status: response.status };
|
|
1016
|
+
return { ok: false, reason: "failed", status: response.status };
|
|
1017
|
+
}
|
|
1018
|
+
function endpointUnreachableError(endpoint, cause) {
|
|
1019
|
+
const why = cause ? ` (${cause})` : "";
|
|
1020
|
+
return new MutagentError("ENDPOINT_UNREACHABLE", `Could not reach the Mutagent server at ${endpoint}${why}. The API key was not checked.`, "Check the endpoint (MUTAGENT_ENDPOINT or --endpoint) and the network, then run the same command again.");
|
|
1021
|
+
}
|
|
1022
|
+
function keyCheckError(check, endpoint) {
|
|
1023
|
+
if (check.reason === "unreachable")
|
|
1024
|
+
return endpointUnreachableError(endpoint, check.cause);
|
|
1025
|
+
if (check.reason === "rejected") {
|
|
1026
|
+
return new MutagentError("INVALID_API_KEY", `The API key was rejected by ${endpoint} (HTTP ${String(check.status)}).`, "Run: mutagent login --browser");
|
|
1027
|
+
}
|
|
1028
|
+
return new MutagentError("KEY_CHECK_FAILED", `The Mutagent server at ${endpoint} answered HTTP ${String(check.status)} while checking the API key.`, "Run the same command again; if it keeps failing, check the endpoint with: mutagent config get endpoint");
|
|
1029
|
+
}
|
|
1030
|
+
function assertMutagentKey(apiKey, source) {
|
|
1031
|
+
const foreign = foreignCredential(apiKey.trim());
|
|
1032
|
+
if (foreign !== undefined)
|
|
1033
|
+
throw new ForeignApiKeyError(source, foreign.label);
|
|
1034
|
+
}
|
|
1035
|
+
var ForeignApiKeyError;
|
|
1036
|
+
var init_key_check = __esm(() => {
|
|
1037
|
+
init_errors();
|
|
1038
|
+
init_secret_scan();
|
|
1039
|
+
ForeignApiKeyError = class ForeignApiKeyError extends MutagentError {
|
|
1040
|
+
constructor(source, label) {
|
|
1041
|
+
super("FOREIGN_API_KEY", `${source} looks like ${/^[AEIOU]/.test(label) ? "an" : "a"} ${label}, not a Mutagent API key. Nothing was sent.`, "Run: mutagent login --browser");
|
|
1042
|
+
this.name = "ForeignApiKeyError";
|
|
1043
|
+
}
|
|
1044
|
+
toJSON() {
|
|
1045
|
+
return {
|
|
1046
|
+
...super.toJSON(),
|
|
1047
|
+
_agentGuidance: {
|
|
1048
|
+
fix: ["mutagent login --browser"],
|
|
1049
|
+
notes: [
|
|
1050
|
+
"Mutagent API keys come from signing in (mutagent login), or from the dashboard; they start with mg_live_.",
|
|
1051
|
+
"An LLM provider key belongs in the workspace: mutagent providers add --provider <type> --name <name> --api-key <key>"
|
|
1052
|
+
]
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
613
1055
|
}
|
|
614
1056
|
};
|
|
615
1057
|
});
|
|
@@ -617,6 +1059,7 @@ var init_errors = __esm(() => {
|
|
|
617
1059
|
// src/lib/organization-scope.ts
|
|
618
1060
|
var exports_organization_scope = {};
|
|
619
1061
|
__export(exports_organization_scope, {
|
|
1062
|
+
workspaceDenied: () => workspaceDenied,
|
|
620
1063
|
unreachableServerError: () => unreachableServerError,
|
|
621
1064
|
tenancyHeaders: () => tenancyHeaders,
|
|
622
1065
|
tenancyFailure: () => tenancyFailure,
|
|
@@ -674,20 +1117,22 @@ function parseBody(body) {
|
|
|
674
1117
|
return {};
|
|
675
1118
|
}
|
|
676
1119
|
function deadKeyError() {
|
|
677
|
-
return new TenancyError("AUTH_EXPIRED", "Your CLI key expired or was revoked.", "Run: mutagent login"
|
|
1120
|
+
return new TenancyError("AUTH_EXPIRED", "Your CLI key expired or was revoked.", "Run: mutagent login");
|
|
1121
|
+
}
|
|
1122
|
+
function workspaceDenied(ctx) {
|
|
1123
|
+
const shown = ctx.workspaceName ?? ctx.workspaceId ?? "workspace";
|
|
1124
|
+
if (ctx.workspaceOrigin === "selection") {
|
|
1125
|
+
return new TenancyError(TENANCY_DENIED, `Your saved workspace ${shown} is no longer available to you.`, "Run: mutagent workspaces use <name>");
|
|
1126
|
+
}
|
|
1127
|
+
const where = ctx.organizationName ? ` in ${ctx.organizationName}` : "";
|
|
1128
|
+
return new TenancyError(TENANCY_DENIED, `No workspace "${shown}" among your workspaces${where}.`, "See: mutagent workspaces list");
|
|
678
1129
|
}
|
|
679
1130
|
function tenancyFailure(status, body, ctx) {
|
|
680
1131
|
const parsed = parseBody(body);
|
|
681
1132
|
if (status === 401)
|
|
682
1133
|
return deadKeyError();
|
|
683
|
-
if (status === 403 && parsed.code === TENANCY_DENIED)
|
|
684
|
-
|
|
685
|
-
if (ctx.workspaceOrigin === "selection") {
|
|
686
|
-
return new TenancyError(TENANCY_DENIED, `Your saved workspace ${shown} is no longer available to you.`, "Run: mutagent workspaces use <name>");
|
|
687
|
-
}
|
|
688
|
-
const where = ctx.organizationName ? ` in ${ctx.organizationName}` : "";
|
|
689
|
-
return new TenancyError(TENANCY_DENIED, `No workspace "${shown}" among your workspaces${where}.`, "See: mutagent workspaces list");
|
|
690
|
-
}
|
|
1134
|
+
if (status === 403 && parsed.code === TENANCY_DENIED)
|
|
1135
|
+
return workspaceDenied(ctx);
|
|
691
1136
|
if (status === 403 && ctx.keyScope === "workspace" && ctx.workspaceOrigin === "override") {
|
|
692
1137
|
return new TenancyError("WORKSPACE_KEY_MISMATCH", "This API key only works in its own workspace.", "Run: mutagent login (for a key that can switch workspaces)");
|
|
693
1138
|
}
|
|
@@ -698,7 +1143,7 @@ function tenancyFailure(status, body, ctx) {
|
|
|
698
1143
|
}
|
|
699
1144
|
function noWorkspaceSelected() {
|
|
700
1145
|
const fix = envKeyReplacesFile() ? ENV_KEY_WORKSPACE_FIX : "Run: mutagent workspaces use <name>";
|
|
701
|
-
return new TenancyError("WORKSPACE_REQUIRED", "No workspace selected.", fix
|
|
1146
|
+
return new TenancyError("WORKSPACE_REQUIRED", "No workspace selected.", fix);
|
|
702
1147
|
}
|
|
703
1148
|
function envKeyHasNoSelection() {
|
|
704
1149
|
return new TenancyError("WORKSPACE_FROM_ENV", "With MUTAGENT_API_KEY set, the workspace comes from --workspace or MUTAGENT_WORKSPACE_ID.", ENV_KEY_WORKSPACE_FIX);
|
|
@@ -889,8 +1334,8 @@ var init_organization_scope = __esm(() => {
|
|
|
889
1334
|
init_config();
|
|
890
1335
|
UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
891
1336
|
TenancyError = class TenancyError extends MutagentError {
|
|
892
|
-
constructor(code, what, fix
|
|
893
|
-
super(code, `${what} ${fix}`, fix.replace(/^(Run|See|To switch): /, "")
|
|
1337
|
+
constructor(code, what, fix) {
|
|
1338
|
+
super(code, `${what} ${fix}`, fix.replace(/^(Run|See|To switch): /, ""));
|
|
894
1339
|
this.name = "TenancyError";
|
|
895
1340
|
}
|
|
896
1341
|
};
|
|
@@ -1485,19 +1930,13 @@ function resetSDKClient() {
|
|
|
1485
1930
|
sdkClient = null;
|
|
1486
1931
|
}
|
|
1487
1932
|
async function validateApiKey(apiKey, endpoint) {
|
|
1488
|
-
|
|
1489
|
-
const response = await fetch(`${endpoint}/api/providers`, {
|
|
1490
|
-
headers: { "x-api-key": apiKey }
|
|
1491
|
-
});
|
|
1492
|
-
return response.ok;
|
|
1493
|
-
} catch {
|
|
1494
|
-
return false;
|
|
1495
|
-
}
|
|
1933
|
+
return (await checkApiKey(apiKey, endpoint)).ok;
|
|
1496
1934
|
}
|
|
1497
1935
|
var SDK_TRANSPORT_ERRORS, sdkClient = null;
|
|
1498
1936
|
var init_sdk_client = __esm(() => {
|
|
1499
1937
|
init_errors();
|
|
1500
1938
|
init_config();
|
|
1939
|
+
init_key_check();
|
|
1501
1940
|
init_organization_scope();
|
|
1502
1941
|
init_sdk_debug();
|
|
1503
1942
|
init_known_secrets();
|
|
@@ -1509,209 +1948,7 @@ var init_sdk_client = __esm(() => {
|
|
|
1509
1948
|
// src/index.ts
|
|
1510
1949
|
init_config();
|
|
1511
1950
|
init_sdk_client();
|
|
1512
|
-
|
|
1513
|
-
// src/lib/output.ts
|
|
1514
|
-
init_known_secrets();
|
|
1515
|
-
import chalk from "chalk";
|
|
1516
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
1517
|
-
import { join as join2 } from "path";
|
|
1518
|
-
var _compatCache = null;
|
|
1519
|
-
var _globalCliVersion = "0.0.0";
|
|
1520
|
-
function setCliVersion(version) {
|
|
1521
|
-
_globalCliVersion = version;
|
|
1522
|
-
_compatCache = null;
|
|
1523
|
-
}
|
|
1524
|
-
function getCliVersion() {
|
|
1525
|
-
return _globalCliVersion;
|
|
1526
|
-
}
|
|
1527
|
-
function getCompatMetadata(cliVersion = _globalCliVersion) {
|
|
1528
|
-
if (_compatCache !== null)
|
|
1529
|
-
return _compatCache;
|
|
1530
|
-
let skillVersion = "unknown";
|
|
1531
|
-
let skillMinCliVersion = "unknown";
|
|
1532
|
-
try {
|
|
1533
|
-
const candidatePaths = [
|
|
1534
|
-
join2(process.cwd(), ".claude", "skills", "mutagent-cli", "SKILL.md"),
|
|
1535
|
-
join2(process.cwd(), "mutagent-cli", ".claude", "skills", "mutagent-cli", "SKILL.md")
|
|
1536
|
-
];
|
|
1537
|
-
for (const p of candidatePaths) {
|
|
1538
|
-
if (existsSync2(p)) {
|
|
1539
|
-
const content = readFileSync2(p, "utf-8");
|
|
1540
|
-
const verMatch = /^\s+skill_version:\s+"?([^"\n]+)"?\s*$/m.exec(content);
|
|
1541
|
-
const minMatch = /^\s+skill_min_cli_version:\s+"?([^"\n]+)"?\s*$/m.exec(content);
|
|
1542
|
-
if (/^metadata:/m.test(content) && !verMatch && !minMatch) {
|
|
1543
|
-
throw new Error("Bundled SKILL.md is missing metadata.skill_version / skill_min_cli_version" + " — re-run `bun run sync-skill` to regenerate.");
|
|
1544
|
-
}
|
|
1545
|
-
if (verMatch?.[1])
|
|
1546
|
-
skillVersion = verMatch[1].trim();
|
|
1547
|
-
if (minMatch?.[1])
|
|
1548
|
-
skillMinCliVersion = minMatch[1].trim();
|
|
1549
|
-
break;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
} catch (err) {
|
|
1553
|
-
if (err instanceof Error && err.message.startsWith("Bundled SKILL.md is missing")) {
|
|
1554
|
-
throw err;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
_compatCache = { cliVersion, skillVersion, skillMinCliVersion };
|
|
1558
|
-
return _compatCache;
|
|
1559
|
-
}
|
|
1560
|
-
function getJsonFlag(command) {
|
|
1561
|
-
if (!command)
|
|
1562
|
-
return false;
|
|
1563
|
-
const parentOpts = command.parent ? command.parent.opts() : null;
|
|
1564
|
-
const ownOpts = command.opts();
|
|
1565
|
-
return Boolean(parentOpts?.json ?? ownOpts.json);
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
class OutputFormatter {
|
|
1569
|
-
format;
|
|
1570
|
-
constructor(format2) {
|
|
1571
|
-
this.format = format2;
|
|
1572
|
-
}
|
|
1573
|
-
output(data) {
|
|
1574
|
-
const clean = scrubKnownSuccessDeep(data);
|
|
1575
|
-
if (this.format === "json") {
|
|
1576
|
-
let enriched = clean;
|
|
1577
|
-
if (_globalCliVersion !== "0.0.0" && clean !== null && typeof clean === "object" && !Array.isArray(clean) && "_directive" in clean) {
|
|
1578
|
-
enriched = {
|
|
1579
|
-
...clean,
|
|
1580
|
-
_compat: getCompatMetadata(_globalCliVersion)
|
|
1581
|
-
};
|
|
1582
|
-
}
|
|
1583
|
-
console.log(JSON.stringify(enriched, null, 2));
|
|
1584
|
-
} else {
|
|
1585
|
-
this.formatTable(clean);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
formatTable(data) {
|
|
1589
|
-
if (Array.isArray(data)) {
|
|
1590
|
-
this.formatArray(data);
|
|
1591
|
-
} else if (data && typeof data === "object") {
|
|
1592
|
-
this.formatObject(data);
|
|
1593
|
-
} else {
|
|
1594
|
-
console.log(data);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
formatArray(data) {
|
|
1598
|
-
if (data.length === 0) {
|
|
1599
|
-
console.log(chalk.gray("No results"));
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
const firstItem = data[0];
|
|
1603
|
-
if (!firstItem || typeof firstItem !== "object") {
|
|
1604
|
-
data.forEach((item) => {
|
|
1605
|
-
console.log(` ${String(item)}`);
|
|
1606
|
-
});
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
const keys = Object.keys(firstItem);
|
|
1610
|
-
const toDisplayString = (val) => {
|
|
1611
|
-
if (val === null || val === undefined)
|
|
1612
|
-
return "";
|
|
1613
|
-
if (typeof val === "object")
|
|
1614
|
-
return JSON.stringify(val);
|
|
1615
|
-
if (typeof val === "string")
|
|
1616
|
-
return val;
|
|
1617
|
-
if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") {
|
|
1618
|
-
return val.toString();
|
|
1619
|
-
}
|
|
1620
|
-
return JSON.stringify(val);
|
|
1621
|
-
};
|
|
1622
|
-
const widths = {};
|
|
1623
|
-
keys.forEach((key) => {
|
|
1624
|
-
widths[key] = Math.max(key.length, ...data.map((item) => toDisplayString(item[key]).length));
|
|
1625
|
-
});
|
|
1626
|
-
const header = keys.map((key) => chalk.bold(key.toUpperCase().padEnd(widths[key] ?? 10))).join(" ");
|
|
1627
|
-
console.log(header);
|
|
1628
|
-
console.log(keys.map((key) => "-".repeat(widths[key] ?? 10)).join(" "));
|
|
1629
|
-
data.forEach((item) => {
|
|
1630
|
-
const row = keys.map((key) => {
|
|
1631
|
-
const value = toDisplayString(item[key]);
|
|
1632
|
-
const width = widths[key] ?? 10;
|
|
1633
|
-
return value.padEnd(width).slice(0, width);
|
|
1634
|
-
}).join(" ");
|
|
1635
|
-
console.log(row);
|
|
1636
|
-
});
|
|
1637
|
-
console.log(chalk.gray(`
|
|
1638
|
-
${String(data.length)} result(s)`));
|
|
1639
|
-
}
|
|
1640
|
-
formatObject(data) {
|
|
1641
|
-
const maxKeyLength = Math.max(...Object.keys(data).map((k) => k.length));
|
|
1642
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
1643
|
-
const formattedKey = chalk.bold(key.padEnd(maxKeyLength));
|
|
1644
|
-
let formattedValue;
|
|
1645
|
-
if (value === null || value === undefined) {
|
|
1646
|
-
formattedValue = "";
|
|
1647
|
-
} else if (typeof value === "object") {
|
|
1648
|
-
formattedValue = JSON.stringify(value, null, 2);
|
|
1649
|
-
} else if (typeof value === "string") {
|
|
1650
|
-
formattedValue = value;
|
|
1651
|
-
} else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1652
|
-
formattedValue = value.toString();
|
|
1653
|
-
} else {
|
|
1654
|
-
formattedValue = JSON.stringify(value);
|
|
1655
|
-
}
|
|
1656
|
-
console.log(`${formattedKey} ${formattedValue}`);
|
|
1657
|
-
});
|
|
1658
|
-
}
|
|
1659
|
-
success(message) {
|
|
1660
|
-
const text = scrubKnownSuccess(message);
|
|
1661
|
-
if (this.format === "json") {
|
|
1662
|
-
console.log(JSON.stringify({ success: true, message: text }, null, 2));
|
|
1663
|
-
} else {
|
|
1664
|
-
console.log(chalk.green(`✓ ${text}`));
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
error(message, code, suggestedAction) {
|
|
1668
|
-
const text = scrubKnownSuccess(message);
|
|
1669
|
-
const action = suggestedAction === undefined ? undefined : scrubKnownSuccess(suggestedAction);
|
|
1670
|
-
if (this.format === "json") {
|
|
1671
|
-
const result = { success: false, error: text };
|
|
1672
|
-
if (code)
|
|
1673
|
-
result.code = code;
|
|
1674
|
-
if (action)
|
|
1675
|
-
result.suggestedAction = action;
|
|
1676
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1677
|
-
} else {
|
|
1678
|
-
console.log(chalk.red(`✗ ${text}`));
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
info(message) {
|
|
1682
|
-
if (this.format !== "json") {
|
|
1683
|
-
console.log(chalk.blue(`ℹ ${scrubKnownSuccess(message)}`));
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
warn(message) {
|
|
1687
|
-
const text = scrubKnownSuccess(message);
|
|
1688
|
-
if (this.format === "json") {
|
|
1689
|
-
console.log(JSON.stringify({ warning: text }, null, 2));
|
|
1690
|
-
} else {
|
|
1691
|
-
console.log(chalk.yellow(`⚠ ${text}`));
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
function createSpinner(text, isJson) {
|
|
1696
|
-
if (isJson) {
|
|
1697
|
-
const noop = () => {
|
|
1698
|
-
return;
|
|
1699
|
-
};
|
|
1700
|
-
return {
|
|
1701
|
-
start: noop,
|
|
1702
|
-
stop: noop,
|
|
1703
|
-
succeed: noop,
|
|
1704
|
-
fail: noop
|
|
1705
|
-
};
|
|
1706
|
-
}
|
|
1707
|
-
const start = async () => {
|
|
1708
|
-
const { default: ora } = await import("ora");
|
|
1709
|
-
return ora(text).start();
|
|
1710
|
-
};
|
|
1711
|
-
return { start };
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
// src/index.ts
|
|
1951
|
+
init_output();
|
|
1715
1952
|
init_errors();
|
|
1716
1953
|
var version = "0.1.0";
|
|
1717
1954
|
export {
|
|
@@ -1727,4 +1964,4 @@ export {
|
|
|
1727
1964
|
ApiError
|
|
1728
1965
|
};
|
|
1729
1966
|
|
|
1730
|
-
//# debugId=
|
|
1967
|
+
//# debugId=0397F8E1C872BF7364756E2164756E21
|