@fractary/codex-cli 0.2.2 → 0.3.1
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 +10 -0
- package/dist/cli.cjs +932 -1187
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +927 -1183
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as
|
|
2
|
+
import * as path4 from 'path';
|
|
3
3
|
import 'url';
|
|
4
4
|
import * as fs from 'fs/promises';
|
|
5
5
|
import * as yaml from 'js-yaml';
|
|
6
6
|
import { ValidationError, PermissionDeniedError, ConfigurationError, CodexError } from '@fractary/codex';
|
|
7
7
|
import { Command } from 'commander';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
8
|
+
import chalk8 from 'chalk';
|
|
9
|
+
import * as crypto from 'crypto';
|
|
10
10
|
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
12
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -157,7 +157,7 @@ async function migrateConfig(legacyConfigPath, options) {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
async function writeYamlConfig(config, outputPath) {
|
|
160
|
-
const dir =
|
|
160
|
+
const dir = path4.dirname(outputPath);
|
|
161
161
|
await fs.mkdir(dir, { recursive: true });
|
|
162
162
|
const yamlContent = yaml.dump(config, {
|
|
163
163
|
indent: 2,
|
|
@@ -381,7 +381,7 @@ var init_codex_client = __esm({
|
|
|
381
381
|
const { readYamlConfig: readYamlConfig2 } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
|
|
382
382
|
const { resolveEnvVarsInConfig: resolveEnvVarsInConfig2 } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
|
|
383
383
|
try {
|
|
384
|
-
const configPath =
|
|
384
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
385
385
|
let config;
|
|
386
386
|
try {
|
|
387
387
|
config = await readYamlConfig2(configPath);
|
|
@@ -593,13 +593,110 @@ var init_codex_client = __esm({
|
|
|
593
593
|
// src/cli.ts
|
|
594
594
|
init_esm_shims();
|
|
595
595
|
|
|
596
|
-
// src/commands/
|
|
596
|
+
// src/commands/document/index.ts
|
|
597
|
+
init_esm_shims();
|
|
598
|
+
|
|
599
|
+
// src/commands/document/fetch.ts
|
|
600
|
+
init_esm_shims();
|
|
601
|
+
|
|
602
|
+
// src/client/get-client.ts
|
|
603
|
+
init_esm_shims();
|
|
604
|
+
var clientInstance = null;
|
|
605
|
+
async function getClient(options) {
|
|
606
|
+
if (!clientInstance) {
|
|
607
|
+
const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
|
|
608
|
+
clientInstance = await CodexClient2.create(options);
|
|
609
|
+
}
|
|
610
|
+
return clientInstance;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/commands/document/fetch.ts
|
|
614
|
+
function hashContent(content) {
|
|
615
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
616
|
+
}
|
|
617
|
+
function fetchCommand() {
|
|
618
|
+
const cmd = new Command("fetch");
|
|
619
|
+
cmd.description("Fetch a document by codex:// URI reference").argument("<uri>", "Codex URI (e.g., codex://org/project/docs/file.md)").option("--bypass-cache", "Skip cache and fetch directly from source").option("--ttl <seconds>", "Override default TTL (in seconds)", parseInt).option("--json", "Output as JSON with metadata").option("--output <file>", "Write content to file instead of stdout").action(async (uri, options) => {
|
|
620
|
+
try {
|
|
621
|
+
const { validateUri } = await import('@fractary/codex');
|
|
622
|
+
if (!validateUri(uri)) {
|
|
623
|
+
console.error(chalk8.red("Error: Invalid URI format"));
|
|
624
|
+
console.log(chalk8.dim("Expected: codex://org/project/path/to/file.md"));
|
|
625
|
+
console.log(chalk8.dim("Example: codex://fractary/codex/docs/api.md"));
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
const client = await getClient();
|
|
629
|
+
if (!options.json && !options.bypassCache) {
|
|
630
|
+
console.error(chalk8.dim(`Fetching ${uri}...`));
|
|
631
|
+
}
|
|
632
|
+
const result = await client.fetch(uri, {
|
|
633
|
+
bypassCache: options.bypassCache,
|
|
634
|
+
ttl: options.ttl
|
|
635
|
+
});
|
|
636
|
+
if (options.json) {
|
|
637
|
+
const output = {
|
|
638
|
+
uri,
|
|
639
|
+
content: result.content.toString("utf-8"),
|
|
640
|
+
metadata: {
|
|
641
|
+
fromCache: result.fromCache,
|
|
642
|
+
fetchedAt: result.metadata?.fetchedAt,
|
|
643
|
+
expiresAt: result.metadata?.expiresAt,
|
|
644
|
+
contentLength: result.metadata?.contentLength || result.content.length,
|
|
645
|
+
contentHash: hashContent(result.content)
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
console.log(JSON.stringify(output, null, 2));
|
|
649
|
+
} else if (options.output) {
|
|
650
|
+
await fs.writeFile(options.output, result.content);
|
|
651
|
+
console.log(chalk8.green("\u2713"), `Written to ${options.output}`);
|
|
652
|
+
console.log(chalk8.dim(` Size: ${result.content.length} bytes`));
|
|
653
|
+
if (result.fromCache) {
|
|
654
|
+
console.log(chalk8.dim(" Source: cache"));
|
|
655
|
+
} else {
|
|
656
|
+
console.log(chalk8.dim(" Source: storage"));
|
|
657
|
+
}
|
|
658
|
+
} else {
|
|
659
|
+
if (result.fromCache && !options.bypassCache) {
|
|
660
|
+
console.error(chalk8.green("\u2713"), chalk8.dim("from cache\n"));
|
|
661
|
+
} else {
|
|
662
|
+
console.error(chalk8.green("\u2713"), chalk8.dim("fetched\n"));
|
|
663
|
+
}
|
|
664
|
+
console.log(result.content.toString("utf-8"));
|
|
665
|
+
}
|
|
666
|
+
} catch (error) {
|
|
667
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
668
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
669
|
+
console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
670
|
+
} else if (error.message.includes("GITHUB_TOKEN")) {
|
|
671
|
+
console.log(chalk8.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
672
|
+
} else if (error.message.includes("not found") || error.message.includes("404")) {
|
|
673
|
+
console.log(chalk8.dim("\nThe document may not exist or you may not have access."));
|
|
674
|
+
console.log(chalk8.dim("Check the URI and ensure your storage providers are configured correctly."));
|
|
675
|
+
}
|
|
676
|
+
process.exit(1);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
return cmd;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/commands/document/index.ts
|
|
683
|
+
function documentCommand() {
|
|
684
|
+
const cmd = new Command("document");
|
|
685
|
+
cmd.description("Manage document operations");
|
|
686
|
+
cmd.addCommand(fetchCommand());
|
|
687
|
+
return cmd;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/commands/config/index.ts
|
|
691
|
+
init_esm_shims();
|
|
692
|
+
|
|
693
|
+
// src/commands/config/init.ts
|
|
597
694
|
init_esm_shims();
|
|
598
695
|
init_migrate_config();
|
|
599
696
|
async function getOrgFromGitRemote() {
|
|
600
697
|
try {
|
|
601
|
-
const { execSync
|
|
602
|
-
const remote =
|
|
698
|
+
const { execSync } = __require("child_process");
|
|
699
|
+
const remote = execSync("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
603
700
|
const sshMatch = remote.match(/git@github\.com:([^/]+)\//);
|
|
604
701
|
const httpsMatch = remote.match(/github\.com\/([^/]+)\//);
|
|
605
702
|
return sshMatch?.[1] || httpsMatch?.[1] || null;
|
|
@@ -619,7 +716,7 @@ function initCommand() {
|
|
|
619
716
|
const cmd = new Command("init");
|
|
620
717
|
cmd.description("Initialize Codex v3.0 with YAML configuration").option("--org <slug>", 'Organization slug (e.g., "fractary")').option("--mcp", "Enable MCP server registration").option("--force", "Overwrite existing configuration").action(async (options) => {
|
|
621
718
|
try {
|
|
622
|
-
console.log(
|
|
719
|
+
console.log(chalk8.blue("Initializing Codex v3.0 (YAML format)...\n"));
|
|
623
720
|
let org = options.org;
|
|
624
721
|
if (!org) {
|
|
625
722
|
org = await getOrgFromGitRemote();
|
|
@@ -628,32 +725,32 @@ function initCommand() {
|
|
|
628
725
|
try {
|
|
629
726
|
const { resolveOrganization } = await import('@fractary/codex');
|
|
630
727
|
org = resolveOrganization({
|
|
631
|
-
repoName:
|
|
728
|
+
repoName: path4.basename(process.cwd())
|
|
632
729
|
});
|
|
633
730
|
} catch {
|
|
634
731
|
}
|
|
635
732
|
}
|
|
636
733
|
if (!org) {
|
|
637
|
-
org =
|
|
638
|
-
console.log(
|
|
639
|
-
console.log(
|
|
734
|
+
org = path4.basename(process.cwd()).split("-")[0] || "default";
|
|
735
|
+
console.log(chalk8.yellow(`\u26A0 Could not detect organization, using: ${org}`));
|
|
736
|
+
console.log(chalk8.dim(" Use --org <slug> to specify explicitly\n"));
|
|
640
737
|
} else {
|
|
641
|
-
console.log(
|
|
738
|
+
console.log(chalk8.dim(`Organization: ${chalk8.cyan(org)}
|
|
642
739
|
`));
|
|
643
740
|
}
|
|
644
|
-
const configDir =
|
|
645
|
-
const configPath =
|
|
741
|
+
const configDir = path4.join(process.cwd(), ".fractary");
|
|
742
|
+
const configPath = path4.join(configDir, "codex.yaml");
|
|
646
743
|
const configExists = await fileExists(configPath);
|
|
647
|
-
const legacyConfigPath =
|
|
744
|
+
const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
648
745
|
const legacyExists = await fileExists(legacyConfigPath);
|
|
649
746
|
if (configExists && !options.force) {
|
|
650
|
-
console.log(
|
|
651
|
-
console.log(
|
|
747
|
+
console.log(chalk8.yellow("\u26A0 Configuration already exists at .fractary/codex.yaml"));
|
|
748
|
+
console.log(chalk8.dim("Use --force to overwrite"));
|
|
652
749
|
process.exit(1);
|
|
653
750
|
}
|
|
654
751
|
if (legacyExists && !configExists) {
|
|
655
|
-
console.log(
|
|
656
|
-
console.log(
|
|
752
|
+
console.log(chalk8.yellow("\u26A0 Legacy configuration detected at .fractary/plugins/codex/config.json"));
|
|
753
|
+
console.log(chalk8.dim('Run "fractary codex migrate" to upgrade to YAML format\n'));
|
|
657
754
|
}
|
|
658
755
|
console.log("Creating directory structure...");
|
|
659
756
|
const dirs = [
|
|
@@ -661,8 +758,8 @@ function initCommand() {
|
|
|
661
758
|
".codex-cache"
|
|
662
759
|
];
|
|
663
760
|
for (const dir of dirs) {
|
|
664
|
-
await fs.mkdir(
|
|
665
|
-
console.log(
|
|
761
|
+
await fs.mkdir(path4.join(process.cwd(), dir), { recursive: true });
|
|
762
|
+
console.log(chalk8.green("\u2713"), chalk8.dim(dir + "/"));
|
|
666
763
|
}
|
|
667
764
|
console.log("\nCreating YAML configuration...");
|
|
668
765
|
const config = getDefaultYamlConfig(org);
|
|
@@ -670,113 +767,183 @@ function initCommand() {
|
|
|
670
767
|
config.mcp.enabled = true;
|
|
671
768
|
}
|
|
672
769
|
await writeYamlConfig(config, configPath);
|
|
673
|
-
console.log(
|
|
674
|
-
console.log(
|
|
675
|
-
console.log(
|
|
676
|
-
console.log(
|
|
677
|
-
console.log(
|
|
678
|
-
console.log(
|
|
770
|
+
console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/codex.yaml"));
|
|
771
|
+
console.log(chalk8.green("\n\u2713 Codex v3.0 initialized successfully!\n"));
|
|
772
|
+
console.log(chalk8.bold("Configuration:"));
|
|
773
|
+
console.log(chalk8.dim(` Organization: ${org}`));
|
|
774
|
+
console.log(chalk8.dim(` Cache: .codex-cache/`));
|
|
775
|
+
console.log(chalk8.dim(` Config: .fractary/codex.yaml`));
|
|
679
776
|
if (options.mcp) {
|
|
680
|
-
console.log(
|
|
681
|
-
}
|
|
682
|
-
console.log(
|
|
683
|
-
console.log(
|
|
684
|
-
console.log(
|
|
685
|
-
console.log(
|
|
686
|
-
console.log(
|
|
687
|
-
console.log(
|
|
688
|
-
console.log(
|
|
689
|
-
console.log(
|
|
690
|
-
console.log(
|
|
777
|
+
console.log(chalk8.dim(` MCP Server: Enabled (port 3000)`));
|
|
778
|
+
}
|
|
779
|
+
console.log(chalk8.bold("\nStorage providers configured:"));
|
|
780
|
+
console.log(chalk8.dim(" - Local filesystem (./knowledge)"));
|
|
781
|
+
console.log(chalk8.dim(" - GitHub (requires GITHUB_TOKEN)"));
|
|
782
|
+
console.log(chalk8.dim(" - HTTP endpoint"));
|
|
783
|
+
console.log(chalk8.bold("\nNext steps:"));
|
|
784
|
+
console.log(chalk8.dim(' 1. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
785
|
+
console.log(chalk8.dim(" 2. Edit .fractary/codex.yaml to configure storage providers"));
|
|
786
|
+
console.log(chalk8.dim(" 3. Fetch a document: fractary codex fetch codex://org/project/path"));
|
|
787
|
+
console.log(chalk8.dim(" 4. Check cache: fractary codex cache list"));
|
|
691
788
|
if (legacyExists) {
|
|
692
|
-
console.log(
|
|
693
|
-
console.log(
|
|
789
|
+
console.log(chalk8.yellow("\n\u26A0 Legacy config detected:"));
|
|
790
|
+
console.log(chalk8.dim(' Run "fractary codex migrate" to convert your existing config'));
|
|
694
791
|
}
|
|
695
792
|
} catch (error) {
|
|
696
|
-
console.error(
|
|
793
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
697
794
|
process.exit(1);
|
|
698
795
|
}
|
|
699
796
|
});
|
|
700
797
|
return cmd;
|
|
701
798
|
}
|
|
702
799
|
|
|
703
|
-
// src/commands/
|
|
704
|
-
init_esm_shims();
|
|
705
|
-
|
|
706
|
-
// src/client/get-client.ts
|
|
800
|
+
// src/commands/config/migrate.ts
|
|
707
801
|
init_esm_shims();
|
|
708
|
-
|
|
709
|
-
async function
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
802
|
+
init_migrate_config();
|
|
803
|
+
async function fileExists2(filePath) {
|
|
804
|
+
try {
|
|
805
|
+
await fs.access(filePath);
|
|
806
|
+
return true;
|
|
807
|
+
} catch {
|
|
808
|
+
return false;
|
|
713
809
|
}
|
|
714
|
-
return clientInstance;
|
|
715
810
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
function hashContent(content) {
|
|
719
|
-
const crypto = __require("crypto");
|
|
720
|
-
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
811
|
+
async function readFileContent(filePath) {
|
|
812
|
+
return fs.readFile(filePath, "utf-8");
|
|
721
813
|
}
|
|
722
|
-
function
|
|
723
|
-
const cmd = new Command("
|
|
724
|
-
cmd.description("
|
|
814
|
+
function migrateCommand() {
|
|
815
|
+
const cmd = new Command("migrate");
|
|
816
|
+
cmd.description("Migrate legacy JSON configuration to v3.0 YAML format").option("--dry-run", "Show migration plan without executing").option("--no-backup", "Skip creating backup of old config").option("--json", "Output as JSON").action(async (options) => {
|
|
725
817
|
try {
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
818
|
+
const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
819
|
+
const newConfigPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
820
|
+
if (!await fileExists2(legacyConfigPath)) {
|
|
821
|
+
if (options.json) {
|
|
822
|
+
console.log(JSON.stringify({
|
|
823
|
+
status: "no_config",
|
|
824
|
+
message: "No legacy configuration file found",
|
|
825
|
+
path: legacyConfigPath
|
|
826
|
+
}));
|
|
827
|
+
} else {
|
|
828
|
+
console.log(chalk8.yellow("\u26A0 No legacy configuration file found."));
|
|
829
|
+
console.log(chalk8.dim(` Expected: ${legacyConfigPath}`));
|
|
830
|
+
console.log(chalk8.dim('\nRun "fractary codex init" to create a new v3.0 YAML configuration.'));
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (await fileExists2(newConfigPath) && !options.dryRun) {
|
|
835
|
+
if (options.json) {
|
|
836
|
+
console.log(JSON.stringify({
|
|
837
|
+
status: "already_migrated",
|
|
838
|
+
message: "YAML configuration already exists",
|
|
839
|
+
path: newConfigPath
|
|
840
|
+
}));
|
|
841
|
+
} else {
|
|
842
|
+
console.log(chalk8.yellow("\u26A0 YAML configuration already exists."));
|
|
843
|
+
console.log(chalk8.dim(` Path: ${newConfigPath}`));
|
|
844
|
+
console.log(chalk8.dim('\nUse "fractary codex init --force" to recreate.'));
|
|
845
|
+
}
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const legacyContent = await readFileContent(legacyConfigPath);
|
|
849
|
+
let legacyConfig;
|
|
850
|
+
try {
|
|
851
|
+
legacyConfig = JSON.parse(legacyContent);
|
|
852
|
+
} catch {
|
|
853
|
+
console.error(chalk8.red("Error:"), "Invalid JSON in legacy config file.");
|
|
731
854
|
process.exit(1);
|
|
732
855
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
856
|
+
if (!options.json && !options.dryRun) {
|
|
857
|
+
console.log(chalk8.blue("Migrating Codex configuration to v3.0 YAML format...\n"));
|
|
858
|
+
}
|
|
859
|
+
const migrationResult = await migrateConfig(
|
|
860
|
+
legacyConfigPath,
|
|
861
|
+
{
|
|
862
|
+
createBackup: options.backup !== false,
|
|
863
|
+
backupSuffix: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
|
|
864
|
+
}
|
|
865
|
+
);
|
|
866
|
+
if (!options.json) {
|
|
867
|
+
console.log(chalk8.bold("Legacy Configuration:"));
|
|
868
|
+
console.log(chalk8.dim(` Path: ${legacyConfigPath}`));
|
|
869
|
+
console.log(chalk8.dim(` Organization: ${legacyConfig.organization || legacyConfig.organizationSlug || "unknown"}`));
|
|
870
|
+
console.log("");
|
|
871
|
+
console.log(chalk8.bold("Migration Changes:"));
|
|
872
|
+
console.log(chalk8.green(" + Format: JSON \u2192 YAML"));
|
|
873
|
+
console.log(chalk8.green(" + Location: .fractary/plugins/codex/ \u2192 .fractary/"));
|
|
874
|
+
console.log(chalk8.green(" + File: config.json \u2192 codex.yaml"));
|
|
875
|
+
console.log(chalk8.green(" + Storage: Multi-provider configuration"));
|
|
876
|
+
console.log(chalk8.green(" + Cache: Modern cache management"));
|
|
877
|
+
console.log(chalk8.green(" + Types: Custom type registry"));
|
|
878
|
+
if (migrationResult.warnings.length > 0) {
|
|
879
|
+
console.log("");
|
|
880
|
+
console.log(chalk8.yellow("Warnings:"));
|
|
881
|
+
for (const warning of migrationResult.warnings) {
|
|
882
|
+
console.log(chalk8.yellow(" \u26A0"), chalk8.dim(warning));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
console.log("");
|
|
886
|
+
if (options.dryRun) {
|
|
887
|
+
console.log(chalk8.blue("Dry run - no changes made."));
|
|
888
|
+
console.log(chalk8.dim("Run without --dry-run to execute migration."));
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
736
891
|
}
|
|
737
|
-
const result = await client.fetch(uri, {
|
|
738
|
-
bypassCache: options.bypassCache,
|
|
739
|
-
ttl: options.ttl
|
|
740
|
-
});
|
|
741
892
|
if (options.json) {
|
|
742
893
|
const output = {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
894
|
+
status: options.dryRun ? "migration_ready" : "migrated",
|
|
895
|
+
dryRun: options.dryRun || false,
|
|
896
|
+
legacyConfig: {
|
|
897
|
+
path: legacyConfigPath,
|
|
898
|
+
organization: legacyConfig.organization || legacyConfig.organizationSlug
|
|
899
|
+
},
|
|
900
|
+
newConfig: {
|
|
901
|
+
path: newConfigPath,
|
|
902
|
+
organization: migrationResult.yamlConfig.organization
|
|
903
|
+
},
|
|
904
|
+
warnings: migrationResult.warnings,
|
|
905
|
+
backupPath: migrationResult.backupPath
|
|
752
906
|
};
|
|
753
907
|
console.log(JSON.stringify(output, null, 2));
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
console.log(chalk6.green("\u2713"), `Written to ${options.output}`);
|
|
757
|
-
console.log(chalk6.dim(` Size: ${result.content.length} bytes`));
|
|
758
|
-
if (result.fromCache) {
|
|
759
|
-
console.log(chalk6.dim(" Source: cache"));
|
|
760
|
-
} else {
|
|
761
|
-
console.log(chalk6.dim(" Source: storage"));
|
|
908
|
+
if (options.dryRun) {
|
|
909
|
+
return;
|
|
762
910
|
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
911
|
+
}
|
|
912
|
+
if (!options.dryRun) {
|
|
913
|
+
await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
|
|
914
|
+
const cacheDir = path4.join(process.cwd(), ".codex-cache");
|
|
915
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
916
|
+
if (!options.json) {
|
|
917
|
+
console.log(chalk8.green("\u2713"), "YAML configuration created");
|
|
918
|
+
console.log(chalk8.green("\u2713"), "Cache directory initialized");
|
|
919
|
+
if (migrationResult.backupPath) {
|
|
920
|
+
console.log(chalk8.green("\u2713"), "Legacy config backed up");
|
|
921
|
+
}
|
|
922
|
+
console.log("");
|
|
923
|
+
console.log(chalk8.bold("New Configuration:"));
|
|
924
|
+
console.log(chalk8.dim(` Path: ${newConfigPath}`));
|
|
925
|
+
console.log(chalk8.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
|
|
926
|
+
console.log(chalk8.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
|
|
927
|
+
console.log(chalk8.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
|
|
928
|
+
console.log("");
|
|
929
|
+
console.log(chalk8.bold("Next Steps:"));
|
|
930
|
+
console.log(chalk8.dim(" 1. Review the new configuration: .fractary/codex.yaml"));
|
|
931
|
+
console.log(chalk8.dim(' 2. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
932
|
+
console.log(chalk8.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
|
|
933
|
+
if (migrationResult.backupPath) {
|
|
934
|
+
console.log("");
|
|
935
|
+
console.log(chalk8.dim(`Backup saved: ${path4.basename(migrationResult.backupPath)}`));
|
|
936
|
+
}
|
|
768
937
|
}
|
|
769
|
-
console.log(result.content.toString("utf-8"));
|
|
770
938
|
}
|
|
771
939
|
} catch (error) {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
} else
|
|
778
|
-
console.
|
|
779
|
-
console.log(chalk6.dim("Check the URI and ensure your storage providers are configured correctly."));
|
|
940
|
+
if (options.json) {
|
|
941
|
+
console.log(JSON.stringify({
|
|
942
|
+
status: "error",
|
|
943
|
+
message: error.message
|
|
944
|
+
}));
|
|
945
|
+
} else {
|
|
946
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
780
947
|
}
|
|
781
948
|
process.exit(1);
|
|
782
949
|
}
|
|
@@ -784,6 +951,15 @@ function fetchCommand() {
|
|
|
784
951
|
return cmd;
|
|
785
952
|
}
|
|
786
953
|
|
|
954
|
+
// src/commands/config/index.ts
|
|
955
|
+
function configCommand() {
|
|
956
|
+
const cmd = new Command("config");
|
|
957
|
+
cmd.description("Manage configuration");
|
|
958
|
+
cmd.addCommand(initCommand());
|
|
959
|
+
cmd.addCommand(migrateCommand());
|
|
960
|
+
return cmd;
|
|
961
|
+
}
|
|
962
|
+
|
|
787
963
|
// src/commands/cache/index.ts
|
|
788
964
|
init_esm_shims();
|
|
789
965
|
|
|
@@ -804,8 +980,8 @@ function cacheListCommand() {
|
|
|
804
980
|
if (options.json) {
|
|
805
981
|
console.log(JSON.stringify({ entries: 0, message: "Cache is empty" }));
|
|
806
982
|
} else {
|
|
807
|
-
console.log(
|
|
808
|
-
console.log(
|
|
983
|
+
console.log(chalk8.yellow("Cache is empty."));
|
|
984
|
+
console.log(chalk8.dim("Fetch some documents to populate the cache."));
|
|
809
985
|
}
|
|
810
986
|
return;
|
|
811
987
|
}
|
|
@@ -819,25 +995,25 @@ function cacheListCommand() {
|
|
|
819
995
|
}, null, 2));
|
|
820
996
|
return;
|
|
821
997
|
}
|
|
822
|
-
console.log(
|
|
823
|
-
console.log(
|
|
824
|
-
console.log(` Total: ${
|
|
825
|
-
console.log(` Fresh: ${
|
|
826
|
-
console.log(` Stale: ${stats.staleCount > 0 ?
|
|
827
|
-
console.log(` Expired: ${stats.expiredCount > 0 ?
|
|
998
|
+
console.log(chalk8.bold("Cache Overview\n"));
|
|
999
|
+
console.log(chalk8.bold("Entries:"));
|
|
1000
|
+
console.log(` Total: ${chalk8.cyan(stats.entryCount.toString())} entries`);
|
|
1001
|
+
console.log(` Fresh: ${chalk8.green(stats.freshCount.toString())} entries`);
|
|
1002
|
+
console.log(` Stale: ${stats.staleCount > 0 ? chalk8.yellow(stats.staleCount.toString()) : chalk8.dim("0")} entries`);
|
|
1003
|
+
console.log(` Expired: ${stats.expiredCount > 0 ? chalk8.red(stats.expiredCount.toString()) : chalk8.dim("0")} entries`);
|
|
828
1004
|
console.log("");
|
|
829
|
-
console.log(
|
|
830
|
-
console.log(` Total size: ${
|
|
1005
|
+
console.log(chalk8.bold("Storage:"));
|
|
1006
|
+
console.log(` Total size: ${chalk8.cyan(formatSize(stats.totalSize))}`);
|
|
831
1007
|
console.log("");
|
|
832
1008
|
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
833
|
-
const healthColor = healthPercent > 80 ?
|
|
1009
|
+
const healthColor = healthPercent > 80 ? chalk8.green : healthPercent > 50 ? chalk8.yellow : chalk8.red;
|
|
834
1010
|
console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
|
|
835
1011
|
console.log("");
|
|
836
|
-
console.log(
|
|
837
|
-
console.log(
|
|
838
|
-
console.log(
|
|
1012
|
+
console.log(chalk8.dim("Note: Individual cache entries are managed by the SDK."));
|
|
1013
|
+
console.log(chalk8.dim('Use "fractary codex cache stats" for detailed statistics.'));
|
|
1014
|
+
console.log(chalk8.dim('Use "fractary codex cache clear" to clear cache entries.'));
|
|
839
1015
|
} catch (error) {
|
|
840
|
-
console.error(
|
|
1016
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
841
1017
|
process.exit(1);
|
|
842
1018
|
}
|
|
843
1019
|
});
|
|
@@ -853,7 +1029,7 @@ function cacheClearCommand() {
|
|
|
853
1029
|
const client = await getClient();
|
|
854
1030
|
const statsBefore = await client.getCacheStats();
|
|
855
1031
|
if (statsBefore.entryCount === 0) {
|
|
856
|
-
console.log(
|
|
1032
|
+
console.log(chalk8.yellow("Cache is already empty. Nothing to clear."));
|
|
857
1033
|
return;
|
|
858
1034
|
}
|
|
859
1035
|
let pattern;
|
|
@@ -862,45 +1038,45 @@ function cacheClearCommand() {
|
|
|
862
1038
|
} else if (options.pattern) {
|
|
863
1039
|
pattern = options.pattern;
|
|
864
1040
|
} else {
|
|
865
|
-
console.log(
|
|
866
|
-
console.log(
|
|
867
|
-
console.log(
|
|
1041
|
+
console.log(chalk8.yellow("Please specify what to clear:"));
|
|
1042
|
+
console.log(chalk8.dim(" --all Clear entire cache"));
|
|
1043
|
+
console.log(chalk8.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
|
|
868
1044
|
console.log("");
|
|
869
|
-
console.log(
|
|
870
|
-
console.log(
|
|
871
|
-
console.log(
|
|
1045
|
+
console.log(chalk8.dim("Examples:"));
|
|
1046
|
+
console.log(chalk8.dim(" fractary codex cache clear --all"));
|
|
1047
|
+
console.log(chalk8.dim(' fractary codex cache clear --pattern "codex://fractary/cli/*"'));
|
|
872
1048
|
return;
|
|
873
1049
|
}
|
|
874
1050
|
if (options.dryRun) {
|
|
875
|
-
console.log(
|
|
1051
|
+
console.log(chalk8.blue("Dry run - would clear:\n"));
|
|
876
1052
|
if (pattern) {
|
|
877
|
-
console.log(
|
|
878
|
-
console.log(
|
|
1053
|
+
console.log(chalk8.dim(` Pattern: ${pattern}`));
|
|
1054
|
+
console.log(chalk8.dim(` This would invalidate matching cache entries`));
|
|
879
1055
|
} else {
|
|
880
|
-
console.log(
|
|
1056
|
+
console.log(chalk8.dim(` All cache entries (${statsBefore.entryCount} entries)`));
|
|
881
1057
|
}
|
|
882
|
-
console.log(
|
|
1058
|
+
console.log(chalk8.dim(`
|
|
883
1059
|
Total size: ${formatSize2(statsBefore.totalSize)}`));
|
|
884
1060
|
return;
|
|
885
1061
|
}
|
|
886
1062
|
if (pattern) {
|
|
887
|
-
console.log(
|
|
1063
|
+
console.log(chalk8.blue(`Clearing cache entries matching pattern: ${pattern}
|
|
888
1064
|
`));
|
|
889
1065
|
await client.invalidateCache(pattern);
|
|
890
1066
|
} else {
|
|
891
|
-
console.log(
|
|
1067
|
+
console.log(chalk8.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
|
|
892
1068
|
`));
|
|
893
1069
|
await client.invalidateCache();
|
|
894
1070
|
}
|
|
895
1071
|
const statsAfter = await client.getCacheStats();
|
|
896
1072
|
const entriesCleared = statsBefore.entryCount - statsAfter.entryCount;
|
|
897
1073
|
const sizeFreed = statsBefore.totalSize - statsAfter.totalSize;
|
|
898
|
-
console.log(
|
|
1074
|
+
console.log(chalk8.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
|
|
899
1075
|
if (statsAfter.entryCount > 0) {
|
|
900
|
-
console.log(
|
|
1076
|
+
console.log(chalk8.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
|
|
901
1077
|
}
|
|
902
1078
|
} catch (error) {
|
|
903
|
-
console.error(
|
|
1079
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
904
1080
|
process.exit(1);
|
|
905
1081
|
}
|
|
906
1082
|
});
|
|
@@ -929,93 +1105,333 @@ function cacheStatsCommand() {
|
|
|
929
1105
|
console.log(JSON.stringify(stats, null, 2));
|
|
930
1106
|
return;
|
|
931
1107
|
}
|
|
932
|
-
console.log(
|
|
933
|
-
console.log(
|
|
934
|
-
console.log(` Total entries: ${
|
|
935
|
-
console.log(` Total size: ${
|
|
936
|
-
console.log(` Fresh entries: ${
|
|
937
|
-
console.log(` Stale entries: ${stats.staleCount > 0 ?
|
|
938
|
-
console.log(` Expired entries: ${stats.expiredCount > 0 ?
|
|
1108
|
+
console.log(chalk8.bold("Cache Statistics\n"));
|
|
1109
|
+
console.log(chalk8.bold("Overview"));
|
|
1110
|
+
console.log(` Total entries: ${chalk8.cyan(stats.entryCount.toString())}`);
|
|
1111
|
+
console.log(` Total size: ${chalk8.cyan(formatSize3(stats.totalSize))}`);
|
|
1112
|
+
console.log(` Fresh entries: ${chalk8.green(stats.freshCount.toString())}`);
|
|
1113
|
+
console.log(` Stale entries: ${stats.staleCount > 0 ? chalk8.yellow(stats.staleCount.toString()) : chalk8.dim("0")}`);
|
|
1114
|
+
console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk8.red(stats.expiredCount.toString()) : chalk8.dim("0")}`);
|
|
939
1115
|
console.log("");
|
|
940
1116
|
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
941
|
-
const healthColor = healthPercent > 80 ?
|
|
1117
|
+
const healthColor = healthPercent > 80 ? chalk8.green : healthPercent > 50 ? chalk8.yellow : chalk8.red;
|
|
942
1118
|
console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
|
|
943
1119
|
if (stats.expiredCount > 0) {
|
|
944
|
-
console.log(
|
|
1120
|
+
console.log(chalk8.dim('\nRun "fractary codex cache clear --pattern <pattern>" to clean up entries.'));
|
|
945
1121
|
}
|
|
946
1122
|
if (stats.entryCount === 0) {
|
|
947
|
-
console.log(
|
|
1123
|
+
console.log(chalk8.dim("\nNo cached entries. Fetch some documents to populate the cache."));
|
|
948
1124
|
}
|
|
949
1125
|
} catch (error) {
|
|
950
|
-
console.error(
|
|
1126
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
951
1127
|
process.exit(1);
|
|
952
1128
|
}
|
|
953
1129
|
});
|
|
954
1130
|
return cmd;
|
|
955
1131
|
}
|
|
956
1132
|
|
|
957
|
-
// src/commands/cache/
|
|
958
|
-
function cacheCommand() {
|
|
959
|
-
const cmd = new Command("cache");
|
|
960
|
-
cmd.description("Manage the codex document cache");
|
|
961
|
-
cmd.addCommand(cacheListCommand());
|
|
962
|
-
cmd.addCommand(cacheClearCommand());
|
|
963
|
-
cmd.addCommand(cacheStatsCommand());
|
|
964
|
-
return cmd;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// src/commands/sync/index.ts
|
|
968
|
-
init_esm_shims();
|
|
969
|
-
|
|
970
|
-
// src/commands/sync/project.ts
|
|
1133
|
+
// src/commands/cache/health.ts
|
|
971
1134
|
init_esm_shims();
|
|
972
1135
|
init_migrate_config();
|
|
973
|
-
function
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
return envMap[env] || env;
|
|
981
|
-
}
|
|
982
|
-
function formatBytes(bytes) {
|
|
983
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
984
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
985
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
986
|
-
}
|
|
987
|
-
function formatDuration(ms) {
|
|
988
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
989
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1136
|
+
async function fileExists3(filePath) {
|
|
1137
|
+
try {
|
|
1138
|
+
await fs.access(filePath);
|
|
1139
|
+
return true;
|
|
1140
|
+
} catch {
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
990
1143
|
}
|
|
991
|
-
function
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1144
|
+
async function checkConfiguration() {
|
|
1145
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1146
|
+
const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
1147
|
+
try {
|
|
1148
|
+
if (!await fileExists3(configPath)) {
|
|
1149
|
+
if (await fileExists3(legacyConfigPath)) {
|
|
1150
|
+
return {
|
|
1151
|
+
name: "Configuration",
|
|
1152
|
+
status: "warn",
|
|
1153
|
+
message: "Legacy JSON configuration detected",
|
|
1154
|
+
details: 'Run "fractary codex migrate" to upgrade to YAML format'
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
name: "Configuration",
|
|
1159
|
+
status: "fail",
|
|
1160
|
+
message: "No configuration found",
|
|
1161
|
+
details: 'Run "fractary codex init" to create configuration'
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
const config = await readYamlConfig(configPath);
|
|
1165
|
+
if (!config.organization) {
|
|
1166
|
+
return {
|
|
1167
|
+
name: "Configuration",
|
|
1168
|
+
status: "warn",
|
|
1169
|
+
message: "No organization configured",
|
|
1170
|
+
details: "Organization slug is required"
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
const providerCount = config.storage?.length || 0;
|
|
1174
|
+
if (providerCount === 0) {
|
|
1175
|
+
return {
|
|
1176
|
+
name: "Configuration",
|
|
1177
|
+
status: "warn",
|
|
1178
|
+
message: "No storage providers configured",
|
|
1179
|
+
details: "At least one storage provider is recommended"
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
return {
|
|
1183
|
+
name: "Configuration",
|
|
1184
|
+
status: "pass",
|
|
1185
|
+
message: "Valid YAML configuration",
|
|
1186
|
+
details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
|
|
1187
|
+
};
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
return {
|
|
1190
|
+
name: "Configuration",
|
|
1191
|
+
status: "fail",
|
|
1192
|
+
message: "Invalid configuration",
|
|
1193
|
+
details: error.message
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
async function checkSDKClient() {
|
|
1198
|
+
try {
|
|
1199
|
+
const client = await getClient();
|
|
1200
|
+
const organization = client.getOrganization();
|
|
1201
|
+
return {
|
|
1202
|
+
name: "SDK Client",
|
|
1203
|
+
status: "pass",
|
|
1204
|
+
message: "CodexClient initialized successfully",
|
|
1205
|
+
details: `Organization: ${organization}`
|
|
1206
|
+
};
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
return {
|
|
1209
|
+
name: "SDK Client",
|
|
1210
|
+
status: "fail",
|
|
1211
|
+
message: "Failed to initialize CodexClient",
|
|
1212
|
+
details: error.message
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
async function checkCache() {
|
|
1217
|
+
try {
|
|
1218
|
+
const client = await getClient();
|
|
1219
|
+
const stats = await client.getCacheStats();
|
|
1220
|
+
if (stats.entryCount === 0) {
|
|
1221
|
+
return {
|
|
1222
|
+
name: "Cache",
|
|
1223
|
+
status: "warn",
|
|
1224
|
+
message: "Cache is empty",
|
|
1225
|
+
details: "Fetch some documents to populate cache"
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
1229
|
+
if (healthPercent < 50) {
|
|
1230
|
+
return {
|
|
1231
|
+
name: "Cache",
|
|
1232
|
+
status: "warn",
|
|
1233
|
+
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1234
|
+
details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
name: "Cache",
|
|
1239
|
+
status: "pass",
|
|
1240
|
+
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1241
|
+
details: `${formatSize4(stats.totalSize)} total`
|
|
1242
|
+
};
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
return {
|
|
1245
|
+
name: "Cache",
|
|
1246
|
+
status: "fail",
|
|
1247
|
+
message: "Cache check failed",
|
|
1248
|
+
details: error.message
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
async function checkStorage() {
|
|
1253
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1254
|
+
try {
|
|
1255
|
+
const config = await readYamlConfig(configPath);
|
|
1256
|
+
const providers = config.storage || [];
|
|
1257
|
+
if (providers.length === 0) {
|
|
1258
|
+
return {
|
|
1259
|
+
name: "Storage",
|
|
1260
|
+
status: "warn",
|
|
1261
|
+
message: "No storage providers configured",
|
|
1262
|
+
details: "Configure at least one provider in .fractary/codex.yaml"
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
const providerTypes = providers.map((p) => p.type).join(", ");
|
|
1266
|
+
const hasGitHub = providers.some((p) => p.type === "github");
|
|
1267
|
+
if (hasGitHub && !process.env.GITHUB_TOKEN) {
|
|
1268
|
+
return {
|
|
1269
|
+
name: "Storage",
|
|
1270
|
+
status: "warn",
|
|
1271
|
+
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
1272
|
+
details: "GITHUB_TOKEN not set (required for GitHub provider)"
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
name: "Storage",
|
|
1277
|
+
status: "pass",
|
|
1278
|
+
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
1279
|
+
details: "All configured providers available"
|
|
1280
|
+
};
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
return {
|
|
1283
|
+
name: "Storage",
|
|
1284
|
+
status: "fail",
|
|
1285
|
+
message: "Storage check failed",
|
|
1286
|
+
details: error.message
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
async function checkTypes() {
|
|
1291
|
+
try {
|
|
1292
|
+
const client = await getClient();
|
|
1293
|
+
const registry = client.getTypeRegistry();
|
|
1294
|
+
const allTypes = registry.list();
|
|
1295
|
+
const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
1296
|
+
const customCount = allTypes.length - builtinCount;
|
|
1297
|
+
return {
|
|
1298
|
+
name: "Type Registry",
|
|
1299
|
+
status: "pass",
|
|
1300
|
+
message: `${allTypes.length} types registered`,
|
|
1301
|
+
details: `${builtinCount} built-in, ${customCount} custom`
|
|
1302
|
+
};
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
return {
|
|
1305
|
+
name: "Type Registry",
|
|
1306
|
+
status: "fail",
|
|
1307
|
+
message: "Type registry check failed",
|
|
1308
|
+
details: error.message
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function formatSize4(bytes) {
|
|
1313
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1314
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1315
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1316
|
+
}
|
|
1317
|
+
function healthCommand() {
|
|
1318
|
+
const cmd = new Command("health");
|
|
1319
|
+
cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
|
|
1320
|
+
try {
|
|
1321
|
+
const checks = [];
|
|
1322
|
+
checks.push(await checkConfiguration());
|
|
1323
|
+
checks.push(await checkSDKClient());
|
|
1324
|
+
checks.push(await checkCache());
|
|
1325
|
+
checks.push(await checkStorage());
|
|
1326
|
+
checks.push(await checkTypes());
|
|
1327
|
+
const passed = checks.filter((c) => c.status === "pass").length;
|
|
1328
|
+
const warned = checks.filter((c) => c.status === "warn").length;
|
|
1329
|
+
const failed = checks.filter((c) => c.status === "fail").length;
|
|
1330
|
+
if (options.json) {
|
|
1331
|
+
console.log(JSON.stringify({
|
|
1332
|
+
summary: {
|
|
1333
|
+
total: checks.length,
|
|
1334
|
+
passed,
|
|
1335
|
+
warned,
|
|
1336
|
+
failed,
|
|
1337
|
+
healthy: failed === 0
|
|
1338
|
+
},
|
|
1339
|
+
checks
|
|
1340
|
+
}, null, 2));
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
console.log(chalk8.bold("Codex Health Check\n"));
|
|
1344
|
+
for (const check of checks) {
|
|
1345
|
+
const icon = check.status === "pass" ? chalk8.green("\u2713") : check.status === "warn" ? chalk8.yellow("\u26A0") : chalk8.red("\u2717");
|
|
1346
|
+
const statusColor = check.status === "pass" ? chalk8.green : check.status === "warn" ? chalk8.yellow : chalk8.red;
|
|
1347
|
+
console.log(`${icon} ${chalk8.bold(check.name)}`);
|
|
1348
|
+
console.log(` ${statusColor(check.message)}`);
|
|
1349
|
+
if (check.details) {
|
|
1350
|
+
console.log(` ${chalk8.dim(check.details)}`);
|
|
1351
|
+
}
|
|
1352
|
+
console.log("");
|
|
1353
|
+
}
|
|
1354
|
+
console.log(chalk8.dim("\u2500".repeat(60)));
|
|
1355
|
+
const overallStatus = failed > 0 ? chalk8.red("UNHEALTHY") : warned > 0 ? chalk8.yellow("DEGRADED") : chalk8.green("HEALTHY");
|
|
1356
|
+
console.log(`Status: ${overallStatus}`);
|
|
1357
|
+
console.log(chalk8.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
|
|
1358
|
+
if (failed > 0 || warned > 0) {
|
|
1359
|
+
console.log("");
|
|
1360
|
+
console.log(chalk8.dim("Run checks individually for more details:"));
|
|
1361
|
+
console.log(chalk8.dim(" fractary codex cache stats"));
|
|
1362
|
+
console.log(chalk8.dim(" fractary codex types list"));
|
|
1363
|
+
}
|
|
1364
|
+
if (failed > 0) {
|
|
1365
|
+
process.exit(1);
|
|
1366
|
+
}
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
return cmd;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/commands/cache/index.ts
|
|
1376
|
+
function cacheCommand() {
|
|
1377
|
+
const cmd = new Command("cache");
|
|
1378
|
+
cmd.description("Manage the codex document cache");
|
|
1379
|
+
cmd.addCommand(cacheListCommand());
|
|
1380
|
+
cmd.addCommand(cacheClearCommand());
|
|
1381
|
+
cmd.addCommand(cacheStatsCommand());
|
|
1382
|
+
cmd.addCommand(healthCommand());
|
|
1383
|
+
return cmd;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// src/commands/sync.ts
|
|
1387
|
+
init_esm_shims();
|
|
1388
|
+
init_migrate_config();
|
|
1389
|
+
function getEnvironmentBranch(config, env) {
|
|
1390
|
+
const envMap = config.sync?.environments || {
|
|
1391
|
+
dev: "develop",
|
|
1392
|
+
test: "test",
|
|
1393
|
+
staging: "staging",
|
|
1394
|
+
prod: "main"
|
|
1395
|
+
};
|
|
1396
|
+
return envMap[env] || env;
|
|
1397
|
+
}
|
|
1398
|
+
function formatBytes(bytes) {
|
|
1399
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1400
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1401
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1402
|
+
}
|
|
1403
|
+
function formatDuration(ms) {
|
|
1404
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
1405
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1406
|
+
}
|
|
1407
|
+
function syncCommand() {
|
|
1408
|
+
const cmd = new Command("sync");
|
|
1409
|
+
cmd.description("Sync single project with codex repository").argument("[name]", "Project name (auto-detected if not provided)").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--include <pattern>", "Include files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--exclude <pattern>", "Exclude files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--force", "Force sync without checking timestamps").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1410
|
+
try {
|
|
1411
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1412
|
+
let config;
|
|
1413
|
+
try {
|
|
1414
|
+
config = await readYamlConfig(configPath);
|
|
1415
|
+
} catch (error) {
|
|
1416
|
+
console.error(chalk8.red("Error:"), "Codex not initialized.");
|
|
1417
|
+
console.log(chalk8.dim('Run "fractary codex init" first.'));
|
|
1418
|
+
process.exit(1);
|
|
1419
|
+
}
|
|
1420
|
+
const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
|
|
1421
|
+
let projectName = name;
|
|
1422
|
+
if (!projectName) {
|
|
1423
|
+
const detected = detectCurrentProject();
|
|
1008
1424
|
projectName = detected.project || null;
|
|
1009
1425
|
}
|
|
1010
1426
|
if (!projectName) {
|
|
1011
|
-
console.error(
|
|
1012
|
-
console.log(
|
|
1427
|
+
console.error(chalk8.red("Error:"), "Could not determine project name.");
|
|
1428
|
+
console.log(chalk8.dim("Provide project name as argument or run from a git repository."));
|
|
1013
1429
|
process.exit(1);
|
|
1014
1430
|
}
|
|
1015
1431
|
const validDirections = ["to-codex", "from-codex", "bidirectional"];
|
|
1016
1432
|
if (!validDirections.includes(options.direction)) {
|
|
1017
|
-
console.error(
|
|
1018
|
-
console.log(
|
|
1433
|
+
console.error(chalk8.red("Error:"), `Invalid direction: ${options.direction}`);
|
|
1434
|
+
console.log(chalk8.dim("Valid options: to-codex, from-codex, bidirectional"));
|
|
1019
1435
|
process.exit(1);
|
|
1020
1436
|
}
|
|
1021
1437
|
const direction = options.direction;
|
|
@@ -1026,7 +1442,7 @@ function syncProjectCommand() {
|
|
|
1026
1442
|
const syncManager = createSyncManager({
|
|
1027
1443
|
localStorage,
|
|
1028
1444
|
config: config.sync,
|
|
1029
|
-
manifestPath:
|
|
1445
|
+
manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
|
|
1030
1446
|
});
|
|
1031
1447
|
const defaultPatterns = config.sync?.include || [
|
|
1032
1448
|
"docs/**/*.md",
|
|
@@ -1079,7 +1495,7 @@ function syncProjectCommand() {
|
|
|
1079
1495
|
synced: 0
|
|
1080
1496
|
}, null, 2));
|
|
1081
1497
|
} else {
|
|
1082
|
-
console.log(
|
|
1498
|
+
console.log(chalk8.yellow("No files to sync."));
|
|
1083
1499
|
}
|
|
1084
1500
|
return;
|
|
1085
1501
|
}
|
|
@@ -1122,364 +1538,90 @@ function syncProjectCommand() {
|
|
|
1122
1538
|
}, null, 2));
|
|
1123
1539
|
return;
|
|
1124
1540
|
}
|
|
1125
|
-
console.log(
|
|
1126
|
-
console.log(` Project: ${
|
|
1127
|
-
console.log(` Organization: ${
|
|
1128
|
-
console.log(` Environment: ${
|
|
1129
|
-
console.log(` Direction: ${
|
|
1130
|
-
console.log(` Files: ${
|
|
1131
|
-
console.log(` Total size: ${
|
|
1541
|
+
console.log(chalk8.bold("Sync Plan\n"));
|
|
1542
|
+
console.log(` Project: ${chalk8.cyan(projectName)}`);
|
|
1543
|
+
console.log(` Organization: ${chalk8.cyan(config.organization)}`);
|
|
1544
|
+
console.log(` Environment: ${chalk8.cyan(options.env)} (${targetBranch})`);
|
|
1545
|
+
console.log(` Direction: ${chalk8.cyan(direction)}`);
|
|
1546
|
+
console.log(` Files: ${chalk8.cyan(plan.totalFiles.toString())}`);
|
|
1547
|
+
console.log(` Total size: ${chalk8.cyan(formatBytes(plan.totalBytes))}`);
|
|
1132
1548
|
if (plan.estimatedTime) {
|
|
1133
|
-
console.log(` Est. time: ${
|
|
1549
|
+
console.log(` Est. time: ${chalk8.dim(formatDuration(plan.estimatedTime))}`);
|
|
1134
1550
|
}
|
|
1135
1551
|
console.log("");
|
|
1136
1552
|
if (plan.conflicts.length > 0) {
|
|
1137
|
-
console.log(
|
|
1553
|
+
console.log(chalk8.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
|
|
1138
1554
|
for (const conflict of plan.conflicts.slice(0, 5)) {
|
|
1139
|
-
console.log(
|
|
1555
|
+
console.log(chalk8.yellow(` \u2022 ${conflict.path}`));
|
|
1140
1556
|
}
|
|
1141
1557
|
if (plan.conflicts.length > 5) {
|
|
1142
|
-
console.log(
|
|
1558
|
+
console.log(chalk8.dim(` ... and ${plan.conflicts.length - 5} more`));
|
|
1143
1559
|
}
|
|
1144
1560
|
console.log("");
|
|
1145
1561
|
}
|
|
1146
1562
|
if (plan.skipped.length > 0) {
|
|
1147
|
-
console.log(
|
|
1563
|
+
console.log(chalk8.dim(`${plan.skipped.length} files skipped (no changes)`));
|
|
1148
1564
|
console.log("");
|
|
1149
1565
|
}
|
|
1150
1566
|
if (options.dryRun) {
|
|
1151
|
-
console.log(
|
|
1567
|
+
console.log(chalk8.blue("Dry run - would sync:\n"));
|
|
1152
1568
|
const filesToShow = plan.files.slice(0, 10);
|
|
1153
1569
|
for (const file of filesToShow) {
|
|
1154
1570
|
const arrow = direction === "to-codex" ? "\u2192" : direction === "from-codex" ? "\u2190" : "\u2194";
|
|
1155
|
-
const opColor = file.operation === "create" ?
|
|
1571
|
+
const opColor = file.operation === "create" ? chalk8.green : file.operation === "update" ? chalk8.yellow : chalk8.dim;
|
|
1156
1572
|
console.log(
|
|
1157
|
-
|
|
1573
|
+
chalk8.dim(` ${arrow}`),
|
|
1158
1574
|
opColor(file.operation.padEnd(7)),
|
|
1159
1575
|
file.path,
|
|
1160
|
-
|
|
1576
|
+
chalk8.dim(`(${formatBytes(file.size || 0)})`)
|
|
1161
1577
|
);
|
|
1162
1578
|
}
|
|
1163
1579
|
if (plan.files.length > 10) {
|
|
1164
|
-
console.log(
|
|
1165
|
-
}
|
|
1166
|
-
console.log(chalk6.dim(`
|
|
1167
|
-
Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
|
|
1168
|
-
console.log(chalk6.dim("Run without --dry-run to execute sync."));
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
console.log(chalk6.blue("Syncing...\n"));
|
|
1172
|
-
const startTime = Date.now();
|
|
1173
|
-
const result = await syncManager.executePlan(plan, syncOptions);
|
|
1174
|
-
const duration = Date.now() - startTime;
|
|
1175
|
-
console.log("");
|
|
1176
|
-
if (result.success) {
|
|
1177
|
-
console.log(chalk6.green(`\u2713 Sync completed successfully`));
|
|
1178
|
-
console.log(chalk6.dim(` Synced: ${result.synced} files`));
|
|
1179
|
-
if (result.skipped > 0) {
|
|
1180
|
-
console.log(chalk6.dim(` Skipped: ${result.skipped} files`));
|
|
1181
|
-
}
|
|
1182
|
-
console.log(chalk6.dim(` Duration: ${formatDuration(duration)}`));
|
|
1183
|
-
} else {
|
|
1184
|
-
console.log(chalk6.yellow(`\u26A0 Sync completed with errors`));
|
|
1185
|
-
console.log(chalk6.green(` Synced: ${result.synced} files`));
|
|
1186
|
-
console.log(chalk6.red(` Failed: ${result.failed} files`));
|
|
1187
|
-
if (result.skipped > 0) {
|
|
1188
|
-
console.log(chalk6.dim(` Skipped: ${result.skipped} files`));
|
|
1189
|
-
}
|
|
1190
|
-
if (result.errors.length > 0) {
|
|
1191
|
-
console.log("");
|
|
1192
|
-
console.log(chalk6.red("Errors:"));
|
|
1193
|
-
for (const error of result.errors.slice(0, 5)) {
|
|
1194
|
-
console.log(chalk6.red(` \u2022 ${error.path}: ${error.error}`));
|
|
1195
|
-
}
|
|
1196
|
-
if (result.errors.length > 5) {
|
|
1197
|
-
console.log(chalk6.dim(` ... and ${result.errors.length - 5} more errors`));
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
} catch (error) {
|
|
1202
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1203
|
-
process.exit(1);
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
|
-
return cmd;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
// src/commands/sync/org.ts
|
|
1210
|
-
init_esm_shims();
|
|
1211
|
-
init_migrate_config();
|
|
1212
|
-
function getEnvironmentBranch2(config, env) {
|
|
1213
|
-
const envMap = config.sync?.environments || {
|
|
1214
|
-
dev: "develop",
|
|
1215
|
-
test: "test",
|
|
1216
|
-
staging: "staging",
|
|
1217
|
-
prod: "main"
|
|
1218
|
-
};
|
|
1219
|
-
return envMap[env] || env;
|
|
1220
|
-
}
|
|
1221
|
-
async function discoverRepos(org) {
|
|
1222
|
-
try {
|
|
1223
|
-
const output = execSync(
|
|
1224
|
-
`gh repo list ${org} --json name,url,defaultBranchRef --limit 100`,
|
|
1225
|
-
{ encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
|
|
1226
|
-
);
|
|
1227
|
-
const repos = JSON.parse(output);
|
|
1228
|
-
return repos.map((repo) => ({
|
|
1229
|
-
name: repo.name,
|
|
1230
|
-
url: repo.url,
|
|
1231
|
-
defaultBranch: repo.defaultBranchRef?.name || "main"
|
|
1232
|
-
}));
|
|
1233
|
-
} catch (error) {
|
|
1234
|
-
throw new Error(`Failed to discover repos: ${error.message}. Ensure GitHub CLI is installed and authenticated.`);
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
function shouldExclude(repoName, excludePatterns) {
|
|
1238
|
-
for (const pattern of excludePatterns) {
|
|
1239
|
-
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
1240
|
-
if (regex.test(repoName)) {
|
|
1241
|
-
return true;
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
return false;
|
|
1245
|
-
}
|
|
1246
|
-
async function syncRepository(repo, config, direction, syncOptions) {
|
|
1247
|
-
const startTime = Date.now();
|
|
1248
|
-
try {
|
|
1249
|
-
const { createSyncManager, createLocalStorage } = await import('@fractary/codex');
|
|
1250
|
-
const repoDir = path3.join(process.cwd(), "..", repo.name);
|
|
1251
|
-
const localStorage = createLocalStorage({
|
|
1252
|
-
baseDir: repoDir
|
|
1253
|
-
});
|
|
1254
|
-
const syncManager = createSyncManager({
|
|
1255
|
-
localStorage,
|
|
1256
|
-
config: config.sync,
|
|
1257
|
-
manifestPath: path3.join(repoDir, ".fractary", ".codex-sync-manifest.json")
|
|
1258
|
-
});
|
|
1259
|
-
const includePatterns = config.sync?.include || [
|
|
1260
|
-
"docs/**/*.md",
|
|
1261
|
-
"specs/**/*.md",
|
|
1262
|
-
".fractary/standards/**",
|
|
1263
|
-
".fractary/templates/**"
|
|
1264
|
-
];
|
|
1265
|
-
const allFiles = await syncManager.listLocalFiles(repoDir);
|
|
1266
|
-
const targetFiles = allFiles.filter((file) => {
|
|
1267
|
-
for (const pattern of includePatterns) {
|
|
1268
|
-
const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
|
|
1269
|
-
if (regex.test(file.path)) {
|
|
1270
|
-
return true;
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
return false;
|
|
1274
|
-
});
|
|
1275
|
-
const plan = await syncManager.createPlan(
|
|
1276
|
-
config.organization,
|
|
1277
|
-
repo.name,
|
|
1278
|
-
repoDir,
|
|
1279
|
-
targetFiles,
|
|
1280
|
-
syncOptions
|
|
1281
|
-
);
|
|
1282
|
-
if (plan.totalFiles === 0) {
|
|
1283
|
-
return {
|
|
1284
|
-
repo: repo.name,
|
|
1285
|
-
status: "skipped",
|
|
1286
|
-
filesSynced: 0,
|
|
1287
|
-
duration: Date.now() - startTime
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
const result = await syncManager.executePlan(plan, syncOptions);
|
|
1291
|
-
return {
|
|
1292
|
-
repo: repo.name,
|
|
1293
|
-
status: result.success ? "success" : "error",
|
|
1294
|
-
filesSynced: result.synced,
|
|
1295
|
-
duration: Date.now() - startTime,
|
|
1296
|
-
error: result.success ? void 0 : `${result.failed} files failed`
|
|
1297
|
-
};
|
|
1298
|
-
} catch (error) {
|
|
1299
|
-
return {
|
|
1300
|
-
repo: repo.name,
|
|
1301
|
-
status: "error",
|
|
1302
|
-
duration: Date.now() - startTime,
|
|
1303
|
-
error: error.message
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
function syncOrgCommand() {
|
|
1308
|
-
const cmd = new Command("org");
|
|
1309
|
-
cmd.description("Sync all projects in organization with codex repository").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--exclude <pattern>", "Exclude repos matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--parallel <n>", "Number of parallel syncs", parseInt, 3).option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--json", "Output as JSON").action(async (options) => {
|
|
1310
|
-
try {
|
|
1311
|
-
const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1312
|
-
let config;
|
|
1313
|
-
try {
|
|
1314
|
-
config = await readYamlConfig(configPath);
|
|
1315
|
-
} catch (error) {
|
|
1316
|
-
console.error(chalk6.red("Error:"), "Codex not initialized.");
|
|
1317
|
-
console.log(chalk6.dim('Run "fractary codex init" first.'));
|
|
1318
|
-
process.exit(1);
|
|
1319
|
-
}
|
|
1320
|
-
const validDirections = ["to-codex", "from-codex", "bidirectional"];
|
|
1321
|
-
if (!validDirections.includes(options.direction)) {
|
|
1322
|
-
console.error(chalk6.red("Error:"), `Invalid direction: ${options.direction}`);
|
|
1323
|
-
console.log(chalk6.dim("Valid options: to-codex, from-codex, bidirectional"));
|
|
1324
|
-
process.exit(1);
|
|
1325
|
-
}
|
|
1326
|
-
const direction = options.direction;
|
|
1327
|
-
const org = config.organization;
|
|
1328
|
-
const targetBranch = getEnvironmentBranch2(config, options.env);
|
|
1329
|
-
const excludePatterns = [
|
|
1330
|
-
...config.sync?.exclude || [],
|
|
1331
|
-
...options.exclude
|
|
1332
|
-
];
|
|
1333
|
-
if (!options.json) {
|
|
1334
|
-
console.log(chalk6.bold("Organization Sync\n"));
|
|
1335
|
-
console.log(` Organization: ${chalk6.cyan(org)}`);
|
|
1336
|
-
console.log(` Environment: ${chalk6.cyan(options.env)} (${targetBranch})`);
|
|
1337
|
-
console.log(` Direction: ${chalk6.cyan(direction)}`);
|
|
1338
|
-
console.log(` Parallelism: ${chalk6.cyan(options.parallel.toString())}`);
|
|
1339
|
-
if (excludePatterns.length > 0) {
|
|
1340
|
-
console.log(` Excluding: ${chalk6.dim(excludePatterns.join(", "))}`);
|
|
1341
|
-
}
|
|
1342
|
-
console.log("");
|
|
1343
|
-
console.log(chalk6.dim("Discovering repositories..."));
|
|
1344
|
-
}
|
|
1345
|
-
let repos;
|
|
1346
|
-
try {
|
|
1347
|
-
repos = await discoverRepos(org);
|
|
1348
|
-
} catch (error) {
|
|
1349
|
-
if (options.json) {
|
|
1350
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
1351
|
-
} else {
|
|
1352
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1353
|
-
}
|
|
1354
|
-
process.exit(1);
|
|
1355
|
-
}
|
|
1356
|
-
const eligibleRepos = repos.filter((repo) => !shouldExclude(repo.name, excludePatterns));
|
|
1357
|
-
const excludedRepos = repos.filter((repo) => shouldExclude(repo.name, excludePatterns));
|
|
1358
|
-
if (!options.json) {
|
|
1359
|
-
console.log(chalk6.dim(`Found ${repos.length} repositories (${excludedRepos.length} excluded)
|
|
1360
|
-
`));
|
|
1361
|
-
}
|
|
1362
|
-
if (options.dryRun) {
|
|
1363
|
-
if (options.json) {
|
|
1364
|
-
console.log(JSON.stringify({
|
|
1365
|
-
organization: org,
|
|
1366
|
-
environment: options.env,
|
|
1367
|
-
branch: targetBranch,
|
|
1368
|
-
direction,
|
|
1369
|
-
dryRun: true,
|
|
1370
|
-
repos: {
|
|
1371
|
-
total: repos.length,
|
|
1372
|
-
eligible: eligibleRepos.map((r) => r.name),
|
|
1373
|
-
excluded: excludedRepos.map((r) => r.name)
|
|
1374
|
-
}
|
|
1375
|
-
}, null, 2));
|
|
1376
|
-
} else {
|
|
1377
|
-
console.log(chalk6.blue("Dry run - would sync:\n"));
|
|
1378
|
-
for (const repo of eligibleRepos) {
|
|
1379
|
-
console.log(chalk6.green(" \u2713"), repo.name);
|
|
1380
|
-
}
|
|
1381
|
-
if (excludedRepos.length > 0) {
|
|
1382
|
-
console.log("");
|
|
1383
|
-
console.log(chalk6.dim("Excluded:"));
|
|
1384
|
-
for (const repo of excludedRepos) {
|
|
1385
|
-
console.log(chalk6.dim(` - ${repo.name}`));
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
console.log(chalk6.dim(`
|
|
1389
|
-
Total: ${eligibleRepos.length} repos would be synced`));
|
|
1390
|
-
console.log(chalk6.dim("Run without --dry-run to execute sync."));
|
|
1391
|
-
}
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
if (!options.json) {
|
|
1395
|
-
console.log(chalk6.blue("Syncing repositories...\n"));
|
|
1396
|
-
}
|
|
1397
|
-
const results = [];
|
|
1398
|
-
const syncOptions = {
|
|
1399
|
-
direction,
|
|
1400
|
-
dryRun: false,
|
|
1401
|
-
force: false
|
|
1402
|
-
};
|
|
1403
|
-
for (let i = 0; i < eligibleRepos.length; i += options.parallel) {
|
|
1404
|
-
const batch = eligibleRepos.slice(i, i + options.parallel);
|
|
1405
|
-
const batchResults = await Promise.all(
|
|
1406
|
-
batch.map((repo) => syncRepository(repo, config, direction, syncOptions))
|
|
1407
|
-
);
|
|
1408
|
-
for (const result of batchResults) {
|
|
1409
|
-
results.push(result);
|
|
1410
|
-
if (!options.json) {
|
|
1411
|
-
if (result.status === "success") {
|
|
1412
|
-
console.log(
|
|
1413
|
-
chalk6.green(" \u2713"),
|
|
1414
|
-
result.repo,
|
|
1415
|
-
chalk6.dim(`(${result.filesSynced} files in ${result.duration}ms)`)
|
|
1416
|
-
);
|
|
1417
|
-
} else if (result.status === "skipped") {
|
|
1418
|
-
console.log(
|
|
1419
|
-
chalk6.dim(" -"),
|
|
1420
|
-
result.repo,
|
|
1421
|
-
chalk6.dim("(no files to sync)")
|
|
1422
|
-
);
|
|
1423
|
-
} else if (result.status === "error") {
|
|
1424
|
-
console.log(
|
|
1425
|
-
chalk6.red(" \u2717"),
|
|
1426
|
-
result.repo,
|
|
1427
|
-
chalk6.red(`(${result.error})`)
|
|
1428
|
-
);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1580
|
+
console.log(chalk8.dim(` ... and ${plan.files.length - 10} more files`));
|
|
1431
1581
|
}
|
|
1582
|
+
console.log(chalk8.dim(`
|
|
1583
|
+
Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
|
|
1584
|
+
console.log(chalk8.dim("Run without --dry-run to execute sync."));
|
|
1585
|
+
return;
|
|
1432
1586
|
}
|
|
1433
|
-
|
|
1434
|
-
const
|
|
1435
|
-
const
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
successful: successful.length,
|
|
1446
|
-
skipped: skipped.length,
|
|
1447
|
-
failed: failed.length,
|
|
1448
|
-
filesSynced: totalFiles,
|
|
1449
|
-
details: results
|
|
1450
|
-
}
|
|
1451
|
-
}, null, 2));
|
|
1587
|
+
console.log(chalk8.blue("Syncing...\n"));
|
|
1588
|
+
const startTime = Date.now();
|
|
1589
|
+
const result = await syncManager.executePlan(plan, syncOptions);
|
|
1590
|
+
const duration = Date.now() - startTime;
|
|
1591
|
+
console.log("");
|
|
1592
|
+
if (result.success) {
|
|
1593
|
+
console.log(chalk8.green(`\u2713 Sync completed successfully`));
|
|
1594
|
+
console.log(chalk8.dim(` Synced: ${result.synced} files`));
|
|
1595
|
+
if (result.skipped > 0) {
|
|
1596
|
+
console.log(chalk8.dim(` Skipped: ${result.skipped} files`));
|
|
1597
|
+
}
|
|
1598
|
+
console.log(chalk8.dim(` Duration: ${formatDuration(duration)}`));
|
|
1452
1599
|
} else {
|
|
1453
|
-
console.log(
|
|
1454
|
-
console.log(
|
|
1455
|
-
|
|
1456
|
-
|
|
1600
|
+
console.log(chalk8.yellow(`\u26A0 Sync completed with errors`));
|
|
1601
|
+
console.log(chalk8.green(` Synced: ${result.synced} files`));
|
|
1602
|
+
console.log(chalk8.red(` Failed: ${result.failed} files`));
|
|
1603
|
+
if (result.skipped > 0) {
|
|
1604
|
+
console.log(chalk8.dim(` Skipped: ${result.skipped} files`));
|
|
1457
1605
|
}
|
|
1458
|
-
if (
|
|
1459
|
-
console.log(
|
|
1606
|
+
if (result.errors.length > 0) {
|
|
1607
|
+
console.log("");
|
|
1608
|
+
console.log(chalk8.red("Errors:"));
|
|
1609
|
+
for (const error of result.errors.slice(0, 5)) {
|
|
1610
|
+
console.log(chalk8.red(` \u2022 ${error.path}: ${error.error}`));
|
|
1611
|
+
}
|
|
1612
|
+
if (result.errors.length > 5) {
|
|
1613
|
+
console.log(chalk8.dim(` ... and ${result.errors.length - 5} more errors`));
|
|
1614
|
+
}
|
|
1460
1615
|
}
|
|
1461
1616
|
}
|
|
1462
1617
|
} catch (error) {
|
|
1463
|
-
|
|
1464
|
-
console.log(JSON.stringify({ error: error.message }));
|
|
1465
|
-
} else {
|
|
1466
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1467
|
-
}
|
|
1618
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
1468
1619
|
process.exit(1);
|
|
1469
1620
|
}
|
|
1470
1621
|
});
|
|
1471
1622
|
return cmd;
|
|
1472
1623
|
}
|
|
1473
1624
|
|
|
1474
|
-
// src/commands/sync/index.ts
|
|
1475
|
-
function syncCommand() {
|
|
1476
|
-
const cmd = new Command("sync");
|
|
1477
|
-
cmd.description("Synchronize with codex repository");
|
|
1478
|
-
cmd.addCommand(syncProjectCommand());
|
|
1479
|
-
cmd.addCommand(syncOrgCommand());
|
|
1480
|
-
return cmd;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
1625
|
// src/commands/types/index.ts
|
|
1484
1626
|
init_esm_shims();
|
|
1485
1627
|
|
|
@@ -1505,713 +1647,306 @@ function typesListCommand() {
|
|
|
1505
1647
|
types = allTypes.filter((t) => registry.isBuiltIn(t.name));
|
|
1506
1648
|
}
|
|
1507
1649
|
if (options.json) {
|
|
1508
|
-
const builtinCount = types.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
1509
|
-
const customCount = types.length - builtinCount;
|
|
1510
|
-
console.log(JSON.stringify({
|
|
1511
|
-
count: types.length,
|
|
1512
|
-
builtinCount,
|
|
1513
|
-
customCount,
|
|
1514
|
-
types: types.map((t) => ({
|
|
1515
|
-
name: t.name,
|
|
1516
|
-
description: t.description,
|
|
1517
|
-
patterns: t.patterns,
|
|
1518
|
-
defaultTtl: t.defaultTtl,
|
|
1519
|
-
ttl: formatTtl(t.defaultTtl),
|
|
1520
|
-
builtin: registry.isBuiltIn(t.name),
|
|
1521
|
-
archiveAfterDays: t.archiveAfterDays,
|
|
1522
|
-
archiveStorage: t.archiveStorage
|
|
1523
|
-
}))
|
|
1524
|
-
}, null, 2));
|
|
1525
|
-
return;
|
|
1526
|
-
}
|
|
1527
|
-
if (types.length === 0) {
|
|
1528
|
-
console.log(chalk6.yellow("No types found."));
|
|
1529
|
-
return;
|
|
1530
|
-
}
|
|
1531
|
-
console.log(chalk6.bold("Artifact Types\n"));
|
|
1532
|
-
const builtinTypes = types.filter((t) => registry.isBuiltIn(t.name));
|
|
1533
|
-
const customTypes = types.filter((t) => !registry.isBuiltIn(t.name));
|
|
1534
|
-
if (builtinTypes.length > 0 && !options.customOnly) {
|
|
1535
|
-
console.log(chalk6.bold("Built-in Types"));
|
|
1536
|
-
console.log(chalk6.dim("\u2500".repeat(70)));
|
|
1537
|
-
for (const type of builtinTypes) {
|
|
1538
|
-
const patternStr = type.patterns[0] || "";
|
|
1539
|
-
console.log(` ${chalk6.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1540
|
-
console.log(` ${chalk6.dim(" ".repeat(12) + type.description)}`);
|
|
1541
|
-
}
|
|
1542
|
-
console.log("");
|
|
1543
|
-
}
|
|
1544
|
-
if (customTypes.length > 0 && !options.builtinOnly) {
|
|
1545
|
-
console.log(chalk6.bold("Custom Types"));
|
|
1546
|
-
console.log(chalk6.dim("\u2500".repeat(70)));
|
|
1547
|
-
for (const type of customTypes) {
|
|
1548
|
-
const patternStr = type.patterns[0] || "";
|
|
1549
|
-
console.log(` ${chalk6.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1550
|
-
console.log(` ${chalk6.dim(" ".repeat(12) + type.description)}`);
|
|
1551
|
-
}
|
|
1552
|
-
console.log("");
|
|
1553
|
-
}
|
|
1554
|
-
console.log(chalk6.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
|
|
1555
|
-
} catch (error) {
|
|
1556
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1557
|
-
process.exit(1);
|
|
1558
|
-
}
|
|
1559
|
-
});
|
|
1560
|
-
return cmd;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
// src/commands/types/show.ts
|
|
1564
|
-
init_esm_shims();
|
|
1565
|
-
function formatTtl2(seconds) {
|
|
1566
|
-
if (seconds < 60) return `${seconds}s`;
|
|
1567
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1568
|
-
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1569
|
-
return `${Math.floor(seconds / 86400)}d`;
|
|
1570
|
-
}
|
|
1571
|
-
function typesShowCommand() {
|
|
1572
|
-
const cmd = new Command("show");
|
|
1573
|
-
cmd.description("Show details for a specific type").argument("<name>", "Type name").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1574
|
-
try {
|
|
1575
|
-
const client = await getClient();
|
|
1576
|
-
const registry = client.getTypeRegistry();
|
|
1577
|
-
const type = registry.get(name);
|
|
1578
|
-
if (!type) {
|
|
1579
|
-
console.error(chalk6.red("Error:"), `Type "${name}" not found.`);
|
|
1580
|
-
console.log(chalk6.dim('Run "fractary codex types list" to see available types.'));
|
|
1581
|
-
process.exit(1);
|
|
1582
|
-
}
|
|
1583
|
-
const isBuiltin = registry.isBuiltIn(name);
|
|
1584
|
-
if (options.json) {
|
|
1585
|
-
console.log(JSON.stringify({
|
|
1586
|
-
name: type.name,
|
|
1587
|
-
builtin: isBuiltin,
|
|
1588
|
-
description: type.description,
|
|
1589
|
-
patterns: type.patterns,
|
|
1590
|
-
defaultTtl: type.defaultTtl,
|
|
1591
|
-
ttl: formatTtl2(type.defaultTtl),
|
|
1592
|
-
archiveAfterDays: type.archiveAfterDays,
|
|
1593
|
-
archiveStorage: type.archiveStorage,
|
|
1594
|
-
syncPatterns: type.syncPatterns,
|
|
1595
|
-
excludePatterns: type.excludePatterns
|
|
1596
|
-
}, null, 2));
|
|
1597
|
-
return;
|
|
1598
|
-
}
|
|
1599
|
-
const nameColor = isBuiltin ? chalk6.cyan : chalk6.green;
|
|
1600
|
-
console.log(chalk6.bold(`Type: ${nameColor(name)}
|
|
1601
|
-
`));
|
|
1602
|
-
console.log(` ${chalk6.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
|
|
1603
|
-
console.log(` ${chalk6.dim("Description:")} ${type.description}`);
|
|
1604
|
-
console.log(` ${chalk6.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
|
|
1605
|
-
console.log("");
|
|
1606
|
-
console.log(chalk6.bold("Patterns"));
|
|
1607
|
-
for (const pattern of type.patterns) {
|
|
1608
|
-
console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
|
|
1609
|
-
}
|
|
1610
|
-
if (type.archiveAfterDays !== null) {
|
|
1611
|
-
console.log("");
|
|
1612
|
-
console.log(chalk6.bold("Archive Settings"));
|
|
1613
|
-
console.log(` ${chalk6.dim("After:")} ${type.archiveAfterDays} days`);
|
|
1614
|
-
console.log(` ${chalk6.dim("Storage:")} ${type.archiveStorage || "not set"}`);
|
|
1615
|
-
}
|
|
1616
|
-
if (type.syncPatterns && type.syncPatterns.length > 0) {
|
|
1617
|
-
console.log("");
|
|
1618
|
-
console.log(chalk6.bold("Sync Patterns"));
|
|
1619
|
-
for (const pattern of type.syncPatterns) {
|
|
1620
|
-
console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
if (type.excludePatterns && type.excludePatterns.length > 0) {
|
|
1624
|
-
console.log("");
|
|
1625
|
-
console.log(chalk6.bold("Exclude Patterns"));
|
|
1626
|
-
for (const pattern of type.excludePatterns) {
|
|
1627
|
-
console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
} catch (error) {
|
|
1631
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1632
|
-
process.exit(1);
|
|
1633
|
-
}
|
|
1634
|
-
});
|
|
1635
|
-
return cmd;
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
// src/commands/types/add.ts
|
|
1639
|
-
init_esm_shims();
|
|
1640
|
-
init_migrate_config();
|
|
1641
|
-
function isValidTypeName(name) {
|
|
1642
|
-
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
1643
|
-
}
|
|
1644
|
-
function parseTtl(ttl) {
|
|
1645
|
-
const match = ttl.match(/^(\d+)([smhd])$/);
|
|
1646
|
-
if (!match) {
|
|
1647
|
-
throw new Error("Invalid TTL format");
|
|
1648
|
-
}
|
|
1649
|
-
const value = parseInt(match[1], 10);
|
|
1650
|
-
const unit = match[2];
|
|
1651
|
-
switch (unit) {
|
|
1652
|
-
case "s":
|
|
1653
|
-
return value;
|
|
1654
|
-
case "m":
|
|
1655
|
-
return value * 60;
|
|
1656
|
-
case "h":
|
|
1657
|
-
return value * 3600;
|
|
1658
|
-
case "d":
|
|
1659
|
-
return value * 86400;
|
|
1660
|
-
default:
|
|
1661
|
-
throw new Error("Unknown TTL unit");
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
function formatTtl3(seconds) {
|
|
1665
|
-
if (seconds < 60) return `${seconds}s`;
|
|
1666
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1667
|
-
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1668
|
-
return `${Math.floor(seconds / 86400)}d`;
|
|
1669
|
-
}
|
|
1670
|
-
function typesAddCommand() {
|
|
1671
|
-
const cmd = new Command("add");
|
|
1672
|
-
cmd.description("Add a custom artifact type").argument("<name>", "Type name (lowercase, alphanumeric with hyphens)").requiredOption("--pattern <glob>", "File pattern (glob syntax)").option("--ttl <duration>", 'Cache TTL (e.g., "24h", "7d")', "24h").option("--description <text>", "Type description").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1673
|
-
try {
|
|
1674
|
-
if (!isValidTypeName(name)) {
|
|
1675
|
-
console.error(chalk6.red("Error:"), "Invalid type name.");
|
|
1676
|
-
console.log(chalk6.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
|
|
1677
|
-
process.exit(1);
|
|
1678
|
-
}
|
|
1679
|
-
const client = await getClient();
|
|
1680
|
-
const registry = client.getTypeRegistry();
|
|
1681
|
-
if (registry.isBuiltIn(name)) {
|
|
1682
|
-
console.error(chalk6.red("Error:"), `Cannot override built-in type "${name}".`);
|
|
1683
|
-
const builtinNames = registry.list().filter((t) => registry.isBuiltIn(t.name)).map((t) => t.name);
|
|
1684
|
-
console.log(chalk6.dim("Built-in types: " + builtinNames.join(", ")));
|
|
1685
|
-
process.exit(1);
|
|
1686
|
-
}
|
|
1687
|
-
if (registry.has(name)) {
|
|
1688
|
-
console.error(chalk6.red("Error:"), `Custom type "${name}" already exists.`);
|
|
1689
|
-
console.log(chalk6.dim('Use "fractary codex types remove" first to remove it.'));
|
|
1690
|
-
process.exit(1);
|
|
1691
|
-
}
|
|
1692
|
-
let ttlSeconds;
|
|
1693
|
-
try {
|
|
1694
|
-
ttlSeconds = parseTtl(options.ttl);
|
|
1695
|
-
} catch {
|
|
1696
|
-
console.error(chalk6.red("Error:"), "Invalid TTL format.");
|
|
1697
|
-
console.log(chalk6.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
|
|
1698
|
-
console.log(chalk6.dim("Examples: 30m, 24h, 7d"));
|
|
1699
|
-
process.exit(1);
|
|
1700
|
-
}
|
|
1701
|
-
const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1702
|
-
const config = await readYamlConfig(configPath);
|
|
1703
|
-
if (!config.types) {
|
|
1704
|
-
config.types = { custom: {} };
|
|
1705
|
-
}
|
|
1706
|
-
if (!config.types.custom) {
|
|
1707
|
-
config.types.custom = {};
|
|
1708
|
-
}
|
|
1709
|
-
config.types.custom[name] = {
|
|
1710
|
-
description: options.description || `Custom type: ${name}`,
|
|
1711
|
-
patterns: [options.pattern],
|
|
1712
|
-
defaultTtl: ttlSeconds
|
|
1713
|
-
};
|
|
1714
|
-
await writeYamlConfig(config, configPath);
|
|
1715
|
-
if (options.json) {
|
|
1716
|
-
console.log(JSON.stringify({
|
|
1717
|
-
success: true,
|
|
1718
|
-
type: {
|
|
1719
|
-
name,
|
|
1720
|
-
description: config.types.custom[name].description,
|
|
1721
|
-
patterns: config.types.custom[name].patterns,
|
|
1722
|
-
defaultTtl: ttlSeconds,
|
|
1723
|
-
ttl: formatTtl3(ttlSeconds),
|
|
1724
|
-
builtin: false
|
|
1725
|
-
},
|
|
1726
|
-
message: "Custom type added successfully. Changes will take effect on next CLI invocation."
|
|
1727
|
-
}, null, 2));
|
|
1728
|
-
return;
|
|
1729
|
-
}
|
|
1730
|
-
console.log(chalk6.green("\u2713"), `Added custom type "${chalk6.cyan(name)}"`);
|
|
1731
|
-
console.log("");
|
|
1732
|
-
console.log(` ${chalk6.dim("Pattern:")} ${options.pattern}`);
|
|
1733
|
-
console.log(` ${chalk6.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
|
|
1734
|
-
if (options.description) {
|
|
1735
|
-
console.log(` ${chalk6.dim("Description:")} ${options.description}`);
|
|
1736
|
-
}
|
|
1737
|
-
console.log("");
|
|
1738
|
-
console.log(chalk6.dim("Note: Custom type will be available on next CLI invocation."));
|
|
1739
|
-
} catch (error) {
|
|
1740
|
-
console.error(chalk6.red("Error:"), error.message);
|
|
1741
|
-
if (error.message.includes("Failed to load configuration")) {
|
|
1742
|
-
console.log(chalk6.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
1743
|
-
}
|
|
1744
|
-
process.exit(1);
|
|
1745
|
-
}
|
|
1746
|
-
});
|
|
1747
|
-
return cmd;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
// src/commands/types/remove.ts
|
|
1751
|
-
init_esm_shims();
|
|
1752
|
-
init_migrate_config();
|
|
1753
|
-
function typesRemoveCommand() {
|
|
1754
|
-
const cmd = new Command("remove");
|
|
1755
|
-
cmd.description("Remove a custom artifact type").argument("<name>", "Type name to remove").option("--json", "Output as JSON").option("--force", "Skip confirmation").action(async (name, options) => {
|
|
1756
|
-
try {
|
|
1757
|
-
const client = await getClient();
|
|
1758
|
-
const registry = client.getTypeRegistry();
|
|
1759
|
-
if (registry.isBuiltIn(name)) {
|
|
1760
|
-
console.error(chalk6.red("Error:"), `Cannot remove built-in type "${name}".`);
|
|
1761
|
-
console.log(chalk6.dim("Built-in types are permanent and cannot be removed."));
|
|
1762
|
-
process.exit(1);
|
|
1763
|
-
}
|
|
1764
|
-
if (!registry.has(name)) {
|
|
1765
|
-
console.error(chalk6.red("Error:"), `Custom type "${name}" not found.`);
|
|
1766
|
-
console.log(chalk6.dim('Run "fractary codex types list --custom-only" to see custom types.'));
|
|
1767
|
-
process.exit(1);
|
|
1768
|
-
}
|
|
1769
|
-
const typeInfo = registry.get(name);
|
|
1770
|
-
const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1771
|
-
const config = await readYamlConfig(configPath);
|
|
1772
|
-
if (!config.types?.custom?.[name]) {
|
|
1773
|
-
console.error(chalk6.red("Error:"), `Custom type "${name}" not found in configuration.`);
|
|
1774
|
-
process.exit(1);
|
|
1775
|
-
}
|
|
1776
|
-
delete config.types.custom[name];
|
|
1777
|
-
if (Object.keys(config.types.custom).length === 0) {
|
|
1778
|
-
delete config.types.custom;
|
|
1779
|
-
}
|
|
1780
|
-
if (config.types && Object.keys(config.types).length === 0) {
|
|
1781
|
-
delete config.types;
|
|
1782
|
-
}
|
|
1783
|
-
await writeYamlConfig(config, configPath);
|
|
1784
|
-
if (options.json) {
|
|
1650
|
+
const builtinCount = types.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
1651
|
+
const customCount = types.length - builtinCount;
|
|
1785
1652
|
console.log(JSON.stringify({
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1653
|
+
count: types.length,
|
|
1654
|
+
builtinCount,
|
|
1655
|
+
customCount,
|
|
1656
|
+
types: types.map((t) => ({
|
|
1657
|
+
name: t.name,
|
|
1658
|
+
description: t.description,
|
|
1659
|
+
patterns: t.patterns,
|
|
1660
|
+
defaultTtl: t.defaultTtl,
|
|
1661
|
+
ttl: formatTtl(t.defaultTtl),
|
|
1662
|
+
builtin: registry.isBuiltIn(t.name),
|
|
1663
|
+
archiveAfterDays: t.archiveAfterDays,
|
|
1664
|
+
archiveStorage: t.archiveStorage
|
|
1665
|
+
}))
|
|
1794
1666
|
}, null, 2));
|
|
1795
1667
|
return;
|
|
1796
1668
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
console.log(
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1669
|
+
if (types.length === 0) {
|
|
1670
|
+
console.log(chalk8.yellow("No types found."));
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
console.log(chalk8.bold("Artifact Types\n"));
|
|
1674
|
+
const builtinTypes = types.filter((t) => registry.isBuiltIn(t.name));
|
|
1675
|
+
const customTypes = types.filter((t) => !registry.isBuiltIn(t.name));
|
|
1676
|
+
if (builtinTypes.length > 0 && !options.customOnly) {
|
|
1677
|
+
console.log(chalk8.bold("Built-in Types"));
|
|
1678
|
+
console.log(chalk8.dim("\u2500".repeat(70)));
|
|
1679
|
+
for (const type of builtinTypes) {
|
|
1680
|
+
const patternStr = type.patterns[0] || "";
|
|
1681
|
+
console.log(` ${chalk8.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk8.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1682
|
+
console.log(` ${chalk8.dim(" ".repeat(12) + type.description)}`);
|
|
1683
|
+
}
|
|
1684
|
+
console.log("");
|
|
1685
|
+
}
|
|
1686
|
+
if (customTypes.length > 0 && !options.builtinOnly) {
|
|
1687
|
+
console.log(chalk8.bold("Custom Types"));
|
|
1688
|
+
console.log(chalk8.dim("\u2500".repeat(70)));
|
|
1689
|
+
for (const type of customTypes) {
|
|
1690
|
+
const patternStr = type.patterns[0] || "";
|
|
1691
|
+
console.log(` ${chalk8.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk8.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1692
|
+
console.log(` ${chalk8.dim(" ".repeat(12) + type.description)}`);
|
|
1693
|
+
}
|
|
1694
|
+
console.log("");
|
|
1808
1695
|
}
|
|
1696
|
+
console.log(chalk8.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
1809
1699
|
process.exit(1);
|
|
1810
1700
|
}
|
|
1811
1701
|
});
|
|
1812
1702
|
return cmd;
|
|
1813
1703
|
}
|
|
1814
1704
|
|
|
1815
|
-
// src/commands/types/
|
|
1816
|
-
function typesCommand() {
|
|
1817
|
-
const cmd = new Command("types");
|
|
1818
|
-
cmd.description("Manage artifact type registry");
|
|
1819
|
-
cmd.addCommand(typesListCommand());
|
|
1820
|
-
cmd.addCommand(typesShowCommand());
|
|
1821
|
-
cmd.addCommand(typesAddCommand());
|
|
1822
|
-
cmd.addCommand(typesRemoveCommand());
|
|
1823
|
-
return cmd;
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// src/commands/health.ts
|
|
1705
|
+
// src/commands/types/show.ts
|
|
1827
1706
|
init_esm_shims();
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
} catch {
|
|
1834
|
-
return false;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
async function checkConfiguration() {
|
|
1838
|
-
const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1839
|
-
const legacyConfigPath = path3.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
1840
|
-
try {
|
|
1841
|
-
if (!await fileExists2(configPath)) {
|
|
1842
|
-
if (await fileExists2(legacyConfigPath)) {
|
|
1843
|
-
return {
|
|
1844
|
-
name: "Configuration",
|
|
1845
|
-
status: "warn",
|
|
1846
|
-
message: "Legacy JSON configuration detected",
|
|
1847
|
-
details: 'Run "fractary codex migrate" to upgrade to YAML format'
|
|
1848
|
-
};
|
|
1849
|
-
}
|
|
1850
|
-
return {
|
|
1851
|
-
name: "Configuration",
|
|
1852
|
-
status: "fail",
|
|
1853
|
-
message: "No configuration found",
|
|
1854
|
-
details: 'Run "fractary codex init" to create configuration'
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
const config = await readYamlConfig(configPath);
|
|
1858
|
-
if (!config.organization) {
|
|
1859
|
-
return {
|
|
1860
|
-
name: "Configuration",
|
|
1861
|
-
status: "warn",
|
|
1862
|
-
message: "No organization configured",
|
|
1863
|
-
details: "Organization slug is required"
|
|
1864
|
-
};
|
|
1865
|
-
}
|
|
1866
|
-
const providerCount = config.storage?.length || 0;
|
|
1867
|
-
if (providerCount === 0) {
|
|
1868
|
-
return {
|
|
1869
|
-
name: "Configuration",
|
|
1870
|
-
status: "warn",
|
|
1871
|
-
message: "No storage providers configured",
|
|
1872
|
-
details: "At least one storage provider is recommended"
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
|
-
return {
|
|
1876
|
-
name: "Configuration",
|
|
1877
|
-
status: "pass",
|
|
1878
|
-
message: "Valid YAML configuration",
|
|
1879
|
-
details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
|
|
1880
|
-
};
|
|
1881
|
-
} catch (error) {
|
|
1882
|
-
return {
|
|
1883
|
-
name: "Configuration",
|
|
1884
|
-
status: "fail",
|
|
1885
|
-
message: "Invalid configuration",
|
|
1886
|
-
details: error.message
|
|
1887
|
-
};
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
async function checkSDKClient() {
|
|
1891
|
-
try {
|
|
1892
|
-
const client = await getClient();
|
|
1893
|
-
const organization = client.getOrganization();
|
|
1894
|
-
return {
|
|
1895
|
-
name: "SDK Client",
|
|
1896
|
-
status: "pass",
|
|
1897
|
-
message: "CodexClient initialized successfully",
|
|
1898
|
-
details: `Organization: ${organization}`
|
|
1899
|
-
};
|
|
1900
|
-
} catch (error) {
|
|
1901
|
-
return {
|
|
1902
|
-
name: "SDK Client",
|
|
1903
|
-
status: "fail",
|
|
1904
|
-
message: "Failed to initialize CodexClient",
|
|
1905
|
-
details: error.message
|
|
1906
|
-
};
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
|
-
async function checkCache() {
|
|
1910
|
-
try {
|
|
1911
|
-
const client = await getClient();
|
|
1912
|
-
const stats = await client.getCacheStats();
|
|
1913
|
-
if (stats.entryCount === 0) {
|
|
1914
|
-
return {
|
|
1915
|
-
name: "Cache",
|
|
1916
|
-
status: "warn",
|
|
1917
|
-
message: "Cache is empty",
|
|
1918
|
-
details: "Fetch some documents to populate cache"
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
1922
|
-
if (healthPercent < 50) {
|
|
1923
|
-
return {
|
|
1924
|
-
name: "Cache",
|
|
1925
|
-
status: "warn",
|
|
1926
|
-
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1927
|
-
details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
return {
|
|
1931
|
-
name: "Cache",
|
|
1932
|
-
status: "pass",
|
|
1933
|
-
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1934
|
-
details: `${formatSize4(stats.totalSize)} total`
|
|
1935
|
-
};
|
|
1936
|
-
} catch (error) {
|
|
1937
|
-
return {
|
|
1938
|
-
name: "Cache",
|
|
1939
|
-
status: "fail",
|
|
1940
|
-
message: "Cache check failed",
|
|
1941
|
-
details: error.message
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
async function checkStorage() {
|
|
1946
|
-
const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1947
|
-
try {
|
|
1948
|
-
const config = await readYamlConfig(configPath);
|
|
1949
|
-
const providers = config.storage || [];
|
|
1950
|
-
if (providers.length === 0) {
|
|
1951
|
-
return {
|
|
1952
|
-
name: "Storage",
|
|
1953
|
-
status: "warn",
|
|
1954
|
-
message: "No storage providers configured",
|
|
1955
|
-
details: "Configure at least one provider in .fractary/codex.yaml"
|
|
1956
|
-
};
|
|
1957
|
-
}
|
|
1958
|
-
const providerTypes = providers.map((p) => p.type).join(", ");
|
|
1959
|
-
const hasGitHub = providers.some((p) => p.type === "github");
|
|
1960
|
-
if (hasGitHub && !process.env.GITHUB_TOKEN) {
|
|
1961
|
-
return {
|
|
1962
|
-
name: "Storage",
|
|
1963
|
-
status: "warn",
|
|
1964
|
-
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
1965
|
-
details: "GITHUB_TOKEN not set (required for GitHub provider)"
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1968
|
-
return {
|
|
1969
|
-
name: "Storage",
|
|
1970
|
-
status: "pass",
|
|
1971
|
-
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
1972
|
-
details: "All configured providers available"
|
|
1973
|
-
};
|
|
1974
|
-
} catch (error) {
|
|
1975
|
-
return {
|
|
1976
|
-
name: "Storage",
|
|
1977
|
-
status: "fail",
|
|
1978
|
-
message: "Storage check failed",
|
|
1979
|
-
details: error.message
|
|
1980
|
-
};
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
async function checkTypes() {
|
|
1984
|
-
try {
|
|
1985
|
-
const client = await getClient();
|
|
1986
|
-
const registry = client.getTypeRegistry();
|
|
1987
|
-
const allTypes = registry.list();
|
|
1988
|
-
const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
1989
|
-
const customCount = allTypes.length - builtinCount;
|
|
1990
|
-
return {
|
|
1991
|
-
name: "Type Registry",
|
|
1992
|
-
status: "pass",
|
|
1993
|
-
message: `${allTypes.length} types registered`,
|
|
1994
|
-
details: `${builtinCount} built-in, ${customCount} custom`
|
|
1995
|
-
};
|
|
1996
|
-
} catch (error) {
|
|
1997
|
-
return {
|
|
1998
|
-
name: "Type Registry",
|
|
1999
|
-
status: "fail",
|
|
2000
|
-
message: "Type registry check failed",
|
|
2001
|
-
details: error.message
|
|
2002
|
-
};
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
function formatSize4(bytes) {
|
|
2006
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
2007
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2008
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1707
|
+
function formatTtl2(seconds) {
|
|
1708
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1709
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1710
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1711
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
2009
1712
|
}
|
|
2010
|
-
function
|
|
2011
|
-
const cmd = new Command("
|
|
2012
|
-
cmd.description("
|
|
1713
|
+
function typesShowCommand() {
|
|
1714
|
+
const cmd = new Command("show");
|
|
1715
|
+
cmd.description("Show details for a specific type").argument("<name>", "Type name").option("--json", "Output as JSON").action(async (name, options) => {
|
|
2013
1716
|
try {
|
|
2014
|
-
const
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
const
|
|
1717
|
+
const client = await getClient();
|
|
1718
|
+
const registry = client.getTypeRegistry();
|
|
1719
|
+
const type = registry.get(name);
|
|
1720
|
+
if (!type) {
|
|
1721
|
+
console.error(chalk8.red("Error:"), `Type "${name}" not found.`);
|
|
1722
|
+
console.log(chalk8.dim('Run "fractary codex types list" to see available types.'));
|
|
1723
|
+
process.exit(1);
|
|
1724
|
+
}
|
|
1725
|
+
const isBuiltin = registry.isBuiltIn(name);
|
|
2023
1726
|
if (options.json) {
|
|
2024
1727
|
console.log(JSON.stringify({
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
1728
|
+
name: type.name,
|
|
1729
|
+
builtin: isBuiltin,
|
|
1730
|
+
description: type.description,
|
|
1731
|
+
patterns: type.patterns,
|
|
1732
|
+
defaultTtl: type.defaultTtl,
|
|
1733
|
+
ttl: formatTtl2(type.defaultTtl),
|
|
1734
|
+
archiveAfterDays: type.archiveAfterDays,
|
|
1735
|
+
archiveStorage: type.archiveStorage,
|
|
1736
|
+
syncPatterns: type.syncPatterns,
|
|
1737
|
+
excludePatterns: type.excludePatterns
|
|
2033
1738
|
}, null, 2));
|
|
2034
1739
|
return;
|
|
2035
1740
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
1741
|
+
const nameColor = isBuiltin ? chalk8.cyan : chalk8.green;
|
|
1742
|
+
console.log(chalk8.bold(`Type: ${nameColor(name)}
|
|
1743
|
+
`));
|
|
1744
|
+
console.log(` ${chalk8.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
|
|
1745
|
+
console.log(` ${chalk8.dim("Description:")} ${type.description}`);
|
|
1746
|
+
console.log(` ${chalk8.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
|
|
1747
|
+
console.log("");
|
|
1748
|
+
console.log(chalk8.bold("Patterns"));
|
|
1749
|
+
for (const pattern of type.patterns) {
|
|
1750
|
+
console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
|
|
1751
|
+
}
|
|
1752
|
+
if (type.archiveAfterDays !== null) {
|
|
2045
1753
|
console.log("");
|
|
1754
|
+
console.log(chalk8.bold("Archive Settings"));
|
|
1755
|
+
console.log(` ${chalk8.dim("After:")} ${type.archiveAfterDays} days`);
|
|
1756
|
+
console.log(` ${chalk8.dim("Storage:")} ${type.archiveStorage || "not set"}`);
|
|
2046
1757
|
}
|
|
2047
|
-
|
|
2048
|
-
const overallStatus = failed > 0 ? chalk6.red("UNHEALTHY") : warned > 0 ? chalk6.yellow("DEGRADED") : chalk6.green("HEALTHY");
|
|
2049
|
-
console.log(`Status: ${overallStatus}`);
|
|
2050
|
-
console.log(chalk6.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
|
|
2051
|
-
if (failed > 0 || warned > 0) {
|
|
1758
|
+
if (type.syncPatterns && type.syncPatterns.length > 0) {
|
|
2052
1759
|
console.log("");
|
|
2053
|
-
console.log(
|
|
2054
|
-
|
|
2055
|
-
|
|
1760
|
+
console.log(chalk8.bold("Sync Patterns"));
|
|
1761
|
+
for (const pattern of type.syncPatterns) {
|
|
1762
|
+
console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
|
|
1763
|
+
}
|
|
2056
1764
|
}
|
|
2057
|
-
if (
|
|
2058
|
-
|
|
1765
|
+
if (type.excludePatterns && type.excludePatterns.length > 0) {
|
|
1766
|
+
console.log("");
|
|
1767
|
+
console.log(chalk8.bold("Exclude Patterns"));
|
|
1768
|
+
for (const pattern of type.excludePatterns) {
|
|
1769
|
+
console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
|
|
1770
|
+
}
|
|
2059
1771
|
}
|
|
2060
1772
|
} catch (error) {
|
|
2061
|
-
console.error(
|
|
1773
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
2062
1774
|
process.exit(1);
|
|
2063
1775
|
}
|
|
2064
1776
|
});
|
|
2065
1777
|
return cmd;
|
|
2066
1778
|
}
|
|
2067
1779
|
|
|
2068
|
-
// src/commands/
|
|
1780
|
+
// src/commands/types/add.ts
|
|
2069
1781
|
init_esm_shims();
|
|
2070
1782
|
init_migrate_config();
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
1783
|
+
function isValidTypeName(name) {
|
|
1784
|
+
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
1785
|
+
}
|
|
1786
|
+
function parseTtl(ttl) {
|
|
1787
|
+
const match = ttl.match(/^(\d+)([smhd])$/);
|
|
1788
|
+
if (!match) {
|
|
1789
|
+
throw new Error("Invalid TTL format");
|
|
1790
|
+
}
|
|
1791
|
+
const value = parseInt(match[1], 10);
|
|
1792
|
+
const unit = match[2];
|
|
1793
|
+
switch (unit) {
|
|
1794
|
+
case "s":
|
|
1795
|
+
return value;
|
|
1796
|
+
case "m":
|
|
1797
|
+
return value * 60;
|
|
1798
|
+
case "h":
|
|
1799
|
+
return value * 3600;
|
|
1800
|
+
case "d":
|
|
1801
|
+
return value * 86400;
|
|
1802
|
+
default:
|
|
1803
|
+
throw new Error("Unknown TTL unit");
|
|
2077
1804
|
}
|
|
2078
1805
|
}
|
|
2079
|
-
|
|
2080
|
-
|
|
1806
|
+
function formatTtl3(seconds) {
|
|
1807
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1808
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1809
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1810
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
2081
1811
|
}
|
|
2082
|
-
function
|
|
2083
|
-
const cmd = new Command("
|
|
2084
|
-
cmd.description("
|
|
1812
|
+
function typesAddCommand() {
|
|
1813
|
+
const cmd = new Command("add");
|
|
1814
|
+
cmd.description("Add a custom artifact type").argument("<name>", "Type name (lowercase, alphanumeric with hyphens)").requiredOption("--pattern <glob>", "File pattern (glob syntax)").option("--ttl <duration>", 'Cache TTL (e.g., "24h", "7d")', "24h").option("--description <text>", "Type description").option("--json", "Output as JSON").action(async (name, options) => {
|
|
2085
1815
|
try {
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
console.log(JSON.stringify({
|
|
2091
|
-
status: "no_config",
|
|
2092
|
-
message: "No legacy configuration file found",
|
|
2093
|
-
path: legacyConfigPath
|
|
2094
|
-
}));
|
|
2095
|
-
} else {
|
|
2096
|
-
console.log(chalk6.yellow("\u26A0 No legacy configuration file found."));
|
|
2097
|
-
console.log(chalk6.dim(` Expected: ${legacyConfigPath}`));
|
|
2098
|
-
console.log(chalk6.dim('\nRun "fractary codex init" to create a new v3.0 YAML configuration.'));
|
|
2099
|
-
}
|
|
2100
|
-
return;
|
|
1816
|
+
if (!isValidTypeName(name)) {
|
|
1817
|
+
console.error(chalk8.red("Error:"), "Invalid type name.");
|
|
1818
|
+
console.log(chalk8.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
|
|
1819
|
+
process.exit(1);
|
|
2101
1820
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
} else {
|
|
2110
|
-
console.log(chalk6.yellow("\u26A0 YAML configuration already exists."));
|
|
2111
|
-
console.log(chalk6.dim(` Path: ${newConfigPath}`));
|
|
2112
|
-
console.log(chalk6.dim('\nUse "fractary codex init --force" to recreate.'));
|
|
2113
|
-
}
|
|
2114
|
-
return;
|
|
1821
|
+
const client = await getClient();
|
|
1822
|
+
const registry = client.getTypeRegistry();
|
|
1823
|
+
if (registry.isBuiltIn(name)) {
|
|
1824
|
+
console.error(chalk8.red("Error:"), `Cannot override built-in type "${name}".`);
|
|
1825
|
+
const builtinNames = registry.list().filter((t) => registry.isBuiltIn(t.name)).map((t) => t.name);
|
|
1826
|
+
console.log(chalk8.dim("Built-in types: " + builtinNames.join(", ")));
|
|
1827
|
+
process.exit(1);
|
|
2115
1828
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
1829
|
+
if (registry.has(name)) {
|
|
1830
|
+
console.error(chalk8.red("Error:"), `Custom type "${name}" already exists.`);
|
|
1831
|
+
console.log(chalk8.dim('Use "fractary codex types remove" first to remove it.'));
|
|
1832
|
+
process.exit(1);
|
|
1833
|
+
}
|
|
1834
|
+
let ttlSeconds;
|
|
2118
1835
|
try {
|
|
2119
|
-
|
|
1836
|
+
ttlSeconds = parseTtl(options.ttl);
|
|
2120
1837
|
} catch {
|
|
2121
|
-
console.error(
|
|
1838
|
+
console.error(chalk8.red("Error:"), "Invalid TTL format.");
|
|
1839
|
+
console.log(chalk8.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
|
|
1840
|
+
console.log(chalk8.dim("Examples: 30m, 24h, 7d"));
|
|
2122
1841
|
process.exit(1);
|
|
2123
1842
|
}
|
|
2124
|
-
|
|
2125
|
-
|
|
1843
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1844
|
+
const config = await readYamlConfig(configPath);
|
|
1845
|
+
if (!config.types) {
|
|
1846
|
+
config.types = { custom: {} };
|
|
2126
1847
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
{
|
|
2130
|
-
createBackup: options.backup !== false,
|
|
2131
|
-
backupSuffix: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
|
|
2132
|
-
}
|
|
2133
|
-
);
|
|
2134
|
-
if (!options.json) {
|
|
2135
|
-
console.log(chalk6.bold("Legacy Configuration:"));
|
|
2136
|
-
console.log(chalk6.dim(` Path: ${legacyConfigPath}`));
|
|
2137
|
-
console.log(chalk6.dim(` Organization: ${legacyConfig.organization || legacyConfig.organizationSlug || "unknown"}`));
|
|
2138
|
-
console.log("");
|
|
2139
|
-
console.log(chalk6.bold("Migration Changes:"));
|
|
2140
|
-
console.log(chalk6.green(" + Format: JSON \u2192 YAML"));
|
|
2141
|
-
console.log(chalk6.green(" + Location: .fractary/plugins/codex/ \u2192 .fractary/"));
|
|
2142
|
-
console.log(chalk6.green(" + File: config.json \u2192 codex.yaml"));
|
|
2143
|
-
console.log(chalk6.green(" + Storage: Multi-provider configuration"));
|
|
2144
|
-
console.log(chalk6.green(" + Cache: Modern cache management"));
|
|
2145
|
-
console.log(chalk6.green(" + Types: Custom type registry"));
|
|
2146
|
-
if (migrationResult.warnings.length > 0) {
|
|
2147
|
-
console.log("");
|
|
2148
|
-
console.log(chalk6.yellow("Warnings:"));
|
|
2149
|
-
for (const warning of migrationResult.warnings) {
|
|
2150
|
-
console.log(chalk6.yellow(" \u26A0"), chalk6.dim(warning));
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
console.log("");
|
|
2154
|
-
if (options.dryRun) {
|
|
2155
|
-
console.log(chalk6.blue("Dry run - no changes made."));
|
|
2156
|
-
console.log(chalk6.dim("Run without --dry-run to execute migration."));
|
|
2157
|
-
return;
|
|
2158
|
-
}
|
|
1848
|
+
if (!config.types.custom) {
|
|
1849
|
+
config.types.custom = {};
|
|
2159
1850
|
}
|
|
1851
|
+
config.types.custom[name] = {
|
|
1852
|
+
description: options.description || `Custom type: ${name}`,
|
|
1853
|
+
patterns: [options.pattern],
|
|
1854
|
+
defaultTtl: ttlSeconds
|
|
1855
|
+
};
|
|
1856
|
+
await writeYamlConfig(config, configPath);
|
|
2160
1857
|
if (options.json) {
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
organization: migrationResult.yamlConfig.organization
|
|
1858
|
+
console.log(JSON.stringify({
|
|
1859
|
+
success: true,
|
|
1860
|
+
type: {
|
|
1861
|
+
name,
|
|
1862
|
+
description: config.types.custom[name].description,
|
|
1863
|
+
patterns: config.types.custom[name].patterns,
|
|
1864
|
+
defaultTtl: ttlSeconds,
|
|
1865
|
+
ttl: formatTtl3(ttlSeconds),
|
|
1866
|
+
builtin: false
|
|
2171
1867
|
},
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
console.log(JSON.stringify(output, null, 2));
|
|
2176
|
-
if (options.dryRun) {
|
|
2177
|
-
return;
|
|
2178
|
-
}
|
|
1868
|
+
message: "Custom type added successfully. Changes will take effect on next CLI invocation."
|
|
1869
|
+
}, null, 2));
|
|
1870
|
+
return;
|
|
2179
1871
|
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
console.log(chalk6.green("\u2713"), "Cache directory initialized");
|
|
2187
|
-
if (migrationResult.backupPath) {
|
|
2188
|
-
console.log(chalk6.green("\u2713"), "Legacy config backed up");
|
|
2189
|
-
}
|
|
2190
|
-
console.log("");
|
|
2191
|
-
console.log(chalk6.bold("New Configuration:"));
|
|
2192
|
-
console.log(chalk6.dim(` Path: ${newConfigPath}`));
|
|
2193
|
-
console.log(chalk6.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
|
|
2194
|
-
console.log(chalk6.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
|
|
2195
|
-
console.log(chalk6.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
|
|
2196
|
-
console.log("");
|
|
2197
|
-
console.log(chalk6.bold("Next Steps:"));
|
|
2198
|
-
console.log(chalk6.dim(" 1. Review the new configuration: .fractary/codex.yaml"));
|
|
2199
|
-
console.log(chalk6.dim(' 2. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
2200
|
-
console.log(chalk6.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
|
|
2201
|
-
if (migrationResult.backupPath) {
|
|
2202
|
-
console.log("");
|
|
2203
|
-
console.log(chalk6.dim(`Backup saved: ${path3.basename(migrationResult.backupPath)}`));
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
1872
|
+
console.log(chalk8.green("\u2713"), `Added custom type "${chalk8.cyan(name)}"`);
|
|
1873
|
+
console.log("");
|
|
1874
|
+
console.log(` ${chalk8.dim("Pattern:")} ${options.pattern}`);
|
|
1875
|
+
console.log(` ${chalk8.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
|
|
1876
|
+
if (options.description) {
|
|
1877
|
+
console.log(` ${chalk8.dim("Description:")} ${options.description}`);
|
|
2206
1878
|
}
|
|
1879
|
+
console.log("");
|
|
1880
|
+
console.log(chalk8.dim("Note: Custom type will be available on next CLI invocation."));
|
|
2207
1881
|
} catch (error) {
|
|
1882
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
1883
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
1884
|
+
console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
1885
|
+
}
|
|
1886
|
+
process.exit(1);
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
return cmd;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// src/commands/types/remove.ts
|
|
1893
|
+
init_esm_shims();
|
|
1894
|
+
init_migrate_config();
|
|
1895
|
+
function typesRemoveCommand() {
|
|
1896
|
+
const cmd = new Command("remove");
|
|
1897
|
+
cmd.description("Remove a custom artifact type").argument("<name>", "Type name to remove").option("--json", "Output as JSON").option("--force", "Skip confirmation").action(async (name, options) => {
|
|
1898
|
+
try {
|
|
1899
|
+
const client = await getClient();
|
|
1900
|
+
const registry = client.getTypeRegistry();
|
|
1901
|
+
if (registry.isBuiltIn(name)) {
|
|
1902
|
+
console.error(chalk8.red("Error:"), `Cannot remove built-in type "${name}".`);
|
|
1903
|
+
console.log(chalk8.dim("Built-in types are permanent and cannot be removed."));
|
|
1904
|
+
process.exit(1);
|
|
1905
|
+
}
|
|
1906
|
+
if (!registry.has(name)) {
|
|
1907
|
+
console.error(chalk8.red("Error:"), `Custom type "${name}" not found.`);
|
|
1908
|
+
console.log(chalk8.dim('Run "fractary codex types list --custom-only" to see custom types.'));
|
|
1909
|
+
process.exit(1);
|
|
1910
|
+
}
|
|
1911
|
+
const typeInfo = registry.get(name);
|
|
1912
|
+
const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1913
|
+
const config = await readYamlConfig(configPath);
|
|
1914
|
+
if (!config.types?.custom?.[name]) {
|
|
1915
|
+
console.error(chalk8.red("Error:"), `Custom type "${name}" not found in configuration.`);
|
|
1916
|
+
process.exit(1);
|
|
1917
|
+
}
|
|
1918
|
+
delete config.types.custom[name];
|
|
1919
|
+
if (Object.keys(config.types.custom).length === 0) {
|
|
1920
|
+
delete config.types.custom;
|
|
1921
|
+
}
|
|
1922
|
+
if (config.types && Object.keys(config.types).length === 0) {
|
|
1923
|
+
delete config.types;
|
|
1924
|
+
}
|
|
1925
|
+
await writeYamlConfig(config, configPath);
|
|
2208
1926
|
if (options.json) {
|
|
2209
1927
|
console.log(JSON.stringify({
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
1928
|
+
success: true,
|
|
1929
|
+
removed: {
|
|
1930
|
+
name: typeInfo.name,
|
|
1931
|
+
description: typeInfo.description,
|
|
1932
|
+
patterns: typeInfo.patterns,
|
|
1933
|
+
defaultTtl: typeInfo.defaultTtl
|
|
1934
|
+
},
|
|
1935
|
+
message: "Custom type removed successfully. Changes will take effect on next CLI invocation."
|
|
1936
|
+
}, null, 2));
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
console.log(chalk8.green("\u2713"), `Removed custom type "${chalk8.cyan(name)}"`);
|
|
1940
|
+
console.log("");
|
|
1941
|
+
console.log(chalk8.dim("Removed configuration:"));
|
|
1942
|
+
console.log(` ${chalk8.dim("Pattern:")} ${typeInfo.patterns.join(", ")}`);
|
|
1943
|
+
console.log(` ${chalk8.dim("Description:")} ${typeInfo.description}`);
|
|
1944
|
+
console.log("");
|
|
1945
|
+
console.log(chalk8.dim("Note: Custom type will be removed on next CLI invocation."));
|
|
1946
|
+
} catch (error) {
|
|
1947
|
+
console.error(chalk8.red("Error:"), error.message);
|
|
1948
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
1949
|
+
console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
2215
1950
|
}
|
|
2216
1951
|
process.exit(1);
|
|
2217
1952
|
}
|
|
@@ -2219,17 +1954,26 @@ function migrateCommand() {
|
|
|
2219
1954
|
return cmd;
|
|
2220
1955
|
}
|
|
2221
1956
|
|
|
1957
|
+
// src/commands/types/index.ts
|
|
1958
|
+
function typesCommand() {
|
|
1959
|
+
const cmd = new Command("types");
|
|
1960
|
+
cmd.description("Manage artifact type registry");
|
|
1961
|
+
cmd.addCommand(typesListCommand());
|
|
1962
|
+
cmd.addCommand(typesShowCommand());
|
|
1963
|
+
cmd.addCommand(typesAddCommand());
|
|
1964
|
+
cmd.addCommand(typesRemoveCommand());
|
|
1965
|
+
return cmd;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
2222
1968
|
// src/cli.ts
|
|
2223
1969
|
function createCLI() {
|
|
2224
1970
|
const program = new Command("fractary-codex");
|
|
2225
|
-
program.description("Centralized knowledge management and distribution for AI agents").version("0.
|
|
2226
|
-
program.addCommand(
|
|
2227
|
-
program.addCommand(
|
|
1971
|
+
program.description("Centralized knowledge management and distribution for AI agents").version("0.3.0");
|
|
1972
|
+
program.addCommand(documentCommand());
|
|
1973
|
+
program.addCommand(configCommand());
|
|
2228
1974
|
program.addCommand(cacheCommand());
|
|
2229
1975
|
program.addCommand(syncCommand());
|
|
2230
1976
|
program.addCommand(typesCommand());
|
|
2231
|
-
program.addCommand(healthCommand());
|
|
2232
|
-
program.addCommand(migrateCommand());
|
|
2233
1977
|
return program;
|
|
2234
1978
|
}
|
|
2235
1979
|
async function main() {
|