@fractary/codex-cli 0.2.2
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 +336 -0
- package/dist/cli.cjs +2274 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +2246 -0
- package/dist/cli.js.map +1 -0
- package/package.json +67 -0
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,2274 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var fs = require('fs/promises');
|
|
5
|
+
var path2 = require('path');
|
|
6
|
+
var yaml = require('js-yaml');
|
|
7
|
+
var codex = require('@fractary/codex');
|
|
8
|
+
var commander = require('commander');
|
|
9
|
+
var chalk6 = require('chalk');
|
|
10
|
+
var child_process = require('child_process');
|
|
11
|
+
|
|
12
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
|
|
14
|
+
function _interopNamespace(e) {
|
|
15
|
+
if (e && e.__esModule) return e;
|
|
16
|
+
var n = Object.create(null);
|
|
17
|
+
if (e) {
|
|
18
|
+
Object.keys(e).forEach(function (k) {
|
|
19
|
+
if (k !== 'default') {
|
|
20
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () { return e[k]; }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
n.default = e;
|
|
29
|
+
return Object.freeze(n);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
33
|
+
var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
34
|
+
var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
|
|
35
|
+
var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
|
|
36
|
+
|
|
37
|
+
var __defProp = Object.defineProperty;
|
|
38
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
39
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
40
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
41
|
+
}) : x)(function(x) {
|
|
42
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
43
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
44
|
+
});
|
|
45
|
+
var __esm = (fn, res) => function __init() {
|
|
46
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
47
|
+
};
|
|
48
|
+
var __export = (target, all) => {
|
|
49
|
+
for (var name in all)
|
|
50
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// ../node_modules/tsup/assets/cjs_shims.js
|
|
54
|
+
var init_cjs_shims = __esm({
|
|
55
|
+
"../node_modules/tsup/assets/cjs_shims.js"() {
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// src/config/migrate-config.ts
|
|
60
|
+
var migrate_config_exports = {};
|
|
61
|
+
__export(migrate_config_exports, {
|
|
62
|
+
getDefaultYamlConfig: () => getDefaultYamlConfig,
|
|
63
|
+
isLegacyConfig: () => isLegacyConfig,
|
|
64
|
+
migrateConfig: () => migrateConfig,
|
|
65
|
+
readYamlConfig: () => readYamlConfig,
|
|
66
|
+
writeYamlConfig: () => writeYamlConfig
|
|
67
|
+
});
|
|
68
|
+
async function isLegacyConfig(configPath) {
|
|
69
|
+
try {
|
|
70
|
+
const content = await fs__namespace.readFile(configPath, "utf-8");
|
|
71
|
+
const config = JSON.parse(content);
|
|
72
|
+
return config.version === "3.0" || config.organizationSlug !== void 0 || config.directories !== void 0 || config.rules !== void 0;
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function migrateConfig(legacyConfigPath, options) {
|
|
78
|
+
const warnings = [];
|
|
79
|
+
try {
|
|
80
|
+
const content = await fs__namespace.readFile(legacyConfigPath, "utf-8");
|
|
81
|
+
const legacy = JSON.parse(content);
|
|
82
|
+
let backupPath;
|
|
83
|
+
if (options?.createBackup !== false) {
|
|
84
|
+
const suffix = options?.backupSuffix || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
85
|
+
backupPath = `${legacyConfigPath}.backup-${suffix}`;
|
|
86
|
+
await fs__namespace.writeFile(backupPath, content, "utf-8");
|
|
87
|
+
}
|
|
88
|
+
const yamlConfig = {
|
|
89
|
+
organization: legacy.organization || legacy.organizationSlug || "default"
|
|
90
|
+
};
|
|
91
|
+
if (legacy.cache) {
|
|
92
|
+
yamlConfig.cacheDir = legacy.cache.directory || ".codex-cache";
|
|
93
|
+
}
|
|
94
|
+
if (legacy.storage?.providers) {
|
|
95
|
+
yamlConfig.storage = [];
|
|
96
|
+
for (const [type, config] of Object.entries(legacy.storage.providers)) {
|
|
97
|
+
if (type === "github") {
|
|
98
|
+
const githubConfig = config;
|
|
99
|
+
yamlConfig.storage.push({
|
|
100
|
+
type: "github",
|
|
101
|
+
token: githubConfig.token || "${GITHUB_TOKEN}",
|
|
102
|
+
apiBaseUrl: githubConfig.baseUrl || "https://api.github.com",
|
|
103
|
+
branch: githubConfig.branch || "main",
|
|
104
|
+
priority: 50
|
|
105
|
+
});
|
|
106
|
+
} else if (type === "http") {
|
|
107
|
+
const httpConfig = config;
|
|
108
|
+
yamlConfig.storage.push({
|
|
109
|
+
type: "http",
|
|
110
|
+
baseUrl: httpConfig.baseUrl,
|
|
111
|
+
headers: httpConfig.headers,
|
|
112
|
+
timeout: httpConfig.timeout || 3e4,
|
|
113
|
+
priority: 100
|
|
114
|
+
});
|
|
115
|
+
} else if (type === "local") {
|
|
116
|
+
const localConfig = config;
|
|
117
|
+
yamlConfig.storage.push({
|
|
118
|
+
type: "local",
|
|
119
|
+
basePath: localConfig.basePath || "./knowledge",
|
|
120
|
+
followSymlinks: localConfig.followSymlinks || false,
|
|
121
|
+
priority: 10
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
warnings.push(`Unknown storage provider type: ${type}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (yamlConfig.storage.length === 0) {
|
|
128
|
+
yamlConfig.storage.push({
|
|
129
|
+
type: "github",
|
|
130
|
+
token: "${GITHUB_TOKEN}",
|
|
131
|
+
apiBaseUrl: "https://api.github.com",
|
|
132
|
+
branch: "main",
|
|
133
|
+
priority: 50
|
|
134
|
+
});
|
|
135
|
+
warnings.push("No storage providers found, added default GitHub provider");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (legacy.types?.custom && Array.isArray(legacy.types.custom)) {
|
|
139
|
+
yamlConfig.types = {
|
|
140
|
+
custom: {}
|
|
141
|
+
};
|
|
142
|
+
for (const customType of legacy.types.custom) {
|
|
143
|
+
if (customType.name) {
|
|
144
|
+
yamlConfig.types.custom[customType.name] = {
|
|
145
|
+
description: customType.description,
|
|
146
|
+
patterns: customType.patterns || [],
|
|
147
|
+
defaultTtl: customType.defaultTtl,
|
|
148
|
+
archiveAfterDays: customType.archiveAfterDays,
|
|
149
|
+
archiveStorage: customType.archiveStorage
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (legacy.sync) {
|
|
155
|
+
yamlConfig.sync = {
|
|
156
|
+
bidirectional: true,
|
|
157
|
+
conflictResolution: "prompt",
|
|
158
|
+
exclude: [
|
|
159
|
+
"node_modules/**",
|
|
160
|
+
".git/**",
|
|
161
|
+
"**/*.log",
|
|
162
|
+
".env"
|
|
163
|
+
]
|
|
164
|
+
};
|
|
165
|
+
if (legacy.sync.environments) {
|
|
166
|
+
warnings.push("Sync environments are not directly supported in v3.0 - please configure sync rules manually");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (legacy.mcp) {
|
|
170
|
+
yamlConfig.mcp = {
|
|
171
|
+
enabled: legacy.mcp.enabled || false,
|
|
172
|
+
port: legacy.mcp.port || 3e3
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
success: true,
|
|
177
|
+
yamlConfig,
|
|
178
|
+
warnings,
|
|
179
|
+
backupPath
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Migration failed: ${error instanceof Error ? error.message : String(error)}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function writeYamlConfig(config, outputPath) {
|
|
188
|
+
const dir = path2__namespace.dirname(outputPath);
|
|
189
|
+
await fs__namespace.mkdir(dir, { recursive: true });
|
|
190
|
+
const yamlContent = yaml__namespace.dump(config, {
|
|
191
|
+
indent: 2,
|
|
192
|
+
lineWidth: 80,
|
|
193
|
+
noRefs: true,
|
|
194
|
+
sortKeys: false
|
|
195
|
+
});
|
|
196
|
+
await fs__namespace.writeFile(outputPath, yamlContent, "utf-8");
|
|
197
|
+
}
|
|
198
|
+
function getDefaultYamlConfig(organization) {
|
|
199
|
+
return {
|
|
200
|
+
organization,
|
|
201
|
+
cacheDir: ".codex-cache",
|
|
202
|
+
storage: [
|
|
203
|
+
{
|
|
204
|
+
type: "local",
|
|
205
|
+
basePath: "./knowledge",
|
|
206
|
+
followSymlinks: false,
|
|
207
|
+
priority: 10
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: "github",
|
|
211
|
+
token: "${GITHUB_TOKEN}",
|
|
212
|
+
apiBaseUrl: "https://api.github.com",
|
|
213
|
+
branch: "main",
|
|
214
|
+
priority: 50
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: "http",
|
|
218
|
+
baseUrl: "https://codex.example.com",
|
|
219
|
+
timeout: 3e4,
|
|
220
|
+
priority: 100
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
types: {
|
|
224
|
+
custom: {}
|
|
225
|
+
},
|
|
226
|
+
permissions: {
|
|
227
|
+
default: "read",
|
|
228
|
+
rules: [
|
|
229
|
+
{
|
|
230
|
+
pattern: "internal/**",
|
|
231
|
+
permission: "none"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
pattern: "public/**",
|
|
235
|
+
permission: "read"
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
sync: {
|
|
240
|
+
bidirectional: true,
|
|
241
|
+
conflictResolution: "prompt",
|
|
242
|
+
exclude: [
|
|
243
|
+
"node_modules/**",
|
|
244
|
+
".git/**",
|
|
245
|
+
"**/*.log",
|
|
246
|
+
".env"
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
mcp: {
|
|
250
|
+
enabled: false,
|
|
251
|
+
port: 3e3
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async function readYamlConfig(configPath) {
|
|
256
|
+
const content = await fs__namespace.readFile(configPath, "utf-8");
|
|
257
|
+
const config = yaml__namespace.load(content);
|
|
258
|
+
if (!config.organization) {
|
|
259
|
+
throw new Error("Invalid config: organization is required");
|
|
260
|
+
}
|
|
261
|
+
return config;
|
|
262
|
+
}
|
|
263
|
+
var init_migrate_config = __esm({
|
|
264
|
+
"src/config/migrate-config.ts"() {
|
|
265
|
+
init_cjs_shims();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// src/config/config-types.ts
|
|
270
|
+
var config_types_exports = {};
|
|
271
|
+
__export(config_types_exports, {
|
|
272
|
+
parseDuration: () => parseDuration,
|
|
273
|
+
parseSize: () => parseSize,
|
|
274
|
+
resolveEnvVars: () => resolveEnvVars,
|
|
275
|
+
resolveEnvVarsInConfig: () => resolveEnvVarsInConfig
|
|
276
|
+
});
|
|
277
|
+
function parseDuration(duration) {
|
|
278
|
+
if (typeof duration === "number") {
|
|
279
|
+
return duration;
|
|
280
|
+
}
|
|
281
|
+
const match = duration.match(/^(\d+)([smhdwMy])$/);
|
|
282
|
+
if (!match) {
|
|
283
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
284
|
+
}
|
|
285
|
+
const [, valueStr, unit] = match;
|
|
286
|
+
const value = parseInt(valueStr, 10);
|
|
287
|
+
switch (unit) {
|
|
288
|
+
case "s":
|
|
289
|
+
return value;
|
|
290
|
+
case "m":
|
|
291
|
+
return value * 60;
|
|
292
|
+
case "h":
|
|
293
|
+
return value * 3600;
|
|
294
|
+
case "d":
|
|
295
|
+
return value * 86400;
|
|
296
|
+
case "w":
|
|
297
|
+
return value * 604800;
|
|
298
|
+
case "M":
|
|
299
|
+
return value * 2592e3;
|
|
300
|
+
// 30 days
|
|
301
|
+
case "y":
|
|
302
|
+
return value * 31536e3;
|
|
303
|
+
// 365 days
|
|
304
|
+
default:
|
|
305
|
+
throw new Error(`Unknown duration unit: ${unit}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function parseSize(size) {
|
|
309
|
+
if (typeof size === "number") {
|
|
310
|
+
return size;
|
|
311
|
+
}
|
|
312
|
+
const match = size.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB)$/i);
|
|
313
|
+
if (!match) {
|
|
314
|
+
throw new Error(`Invalid size format: ${size}`);
|
|
315
|
+
}
|
|
316
|
+
const [, valueStr, unit] = match;
|
|
317
|
+
const value = parseFloat(valueStr);
|
|
318
|
+
switch (unit.toUpperCase()) {
|
|
319
|
+
case "B":
|
|
320
|
+
return value;
|
|
321
|
+
case "KB":
|
|
322
|
+
return value * 1024;
|
|
323
|
+
case "MB":
|
|
324
|
+
return value * 1024 * 1024;
|
|
325
|
+
case "GB":
|
|
326
|
+
return value * 1024 * 1024 * 1024;
|
|
327
|
+
default:
|
|
328
|
+
throw new Error(`Unknown size unit: ${unit}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function resolveEnvVars(value) {
|
|
332
|
+
return value.replace(/\$\{([^}]+)\}/g, (_, varName) => {
|
|
333
|
+
const envValue = process.env[varName];
|
|
334
|
+
if (envValue === void 0) {
|
|
335
|
+
console.warn(`Warning: Environment variable ${varName} is not set`);
|
|
336
|
+
return `\${${varName}}`;
|
|
337
|
+
}
|
|
338
|
+
return envValue;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function resolveEnvVarsInConfig(config) {
|
|
342
|
+
if (typeof config === "string") {
|
|
343
|
+
return resolveEnvVars(config);
|
|
344
|
+
}
|
|
345
|
+
if (Array.isArray(config)) {
|
|
346
|
+
return config.map((item) => resolveEnvVarsInConfig(item));
|
|
347
|
+
}
|
|
348
|
+
if (config !== null && typeof config === "object") {
|
|
349
|
+
const result = {};
|
|
350
|
+
for (const [key, value] of Object.entries(config)) {
|
|
351
|
+
result[key] = resolveEnvVarsInConfig(value);
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
return config;
|
|
356
|
+
}
|
|
357
|
+
var init_config_types = __esm({
|
|
358
|
+
"src/config/config-types.ts"() {
|
|
359
|
+
init_cjs_shims();
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// src/client/codex-client.ts
|
|
364
|
+
var codex_client_exports = {};
|
|
365
|
+
__export(codex_client_exports, {
|
|
366
|
+
CodexClient: () => CodexClient,
|
|
367
|
+
CodexError: () => codex.CodexError,
|
|
368
|
+
ConfigurationError: () => codex.ConfigurationError,
|
|
369
|
+
PermissionDeniedError: () => codex.PermissionDeniedError,
|
|
370
|
+
ValidationError: () => codex.ValidationError
|
|
371
|
+
});
|
|
372
|
+
var CodexClient;
|
|
373
|
+
var init_codex_client = __esm({
|
|
374
|
+
"src/client/codex-client.ts"() {
|
|
375
|
+
init_cjs_shims();
|
|
376
|
+
CodexClient = class _CodexClient {
|
|
377
|
+
cache;
|
|
378
|
+
storage;
|
|
379
|
+
types;
|
|
380
|
+
organization;
|
|
381
|
+
/**
|
|
382
|
+
* Private constructor - use CodexClient.create() instead
|
|
383
|
+
*/
|
|
384
|
+
constructor(cache, storage, types, organization) {
|
|
385
|
+
this.cache = cache;
|
|
386
|
+
this.storage = storage;
|
|
387
|
+
this.types = types;
|
|
388
|
+
this.organization = organization;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Create a new CodexClient instance
|
|
392
|
+
*
|
|
393
|
+
* @param options - Optional configuration
|
|
394
|
+
* @returns Promise resolving to CodexClient instance
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* const client = await CodexClient.create();
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
static async create(options) {
|
|
402
|
+
const {
|
|
403
|
+
CacheManager,
|
|
404
|
+
createStorageManager,
|
|
405
|
+
createDefaultRegistry,
|
|
406
|
+
CodexError: CodexError2,
|
|
407
|
+
ConfigurationError: ConfigurationError2
|
|
408
|
+
} = await import('@fractary/codex');
|
|
409
|
+
const { readYamlConfig: readYamlConfig2 } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
|
|
410
|
+
const { resolveEnvVarsInConfig: resolveEnvVarsInConfig2 } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
|
|
411
|
+
try {
|
|
412
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
413
|
+
let config;
|
|
414
|
+
try {
|
|
415
|
+
config = await readYamlConfig2(configPath);
|
|
416
|
+
config = resolveEnvVarsInConfig2(config);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
throw new ConfigurationError2(
|
|
419
|
+
`Failed to load configuration from ${configPath}. Run "fractary codex init" to create a configuration.`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
const organization = options?.organizationSlug || config.organization;
|
|
423
|
+
const cacheDir = options?.cacheDir || config.cacheDir || ".codex-cache";
|
|
424
|
+
const storageConfig = {};
|
|
425
|
+
if (config.storage && Array.isArray(config.storage)) {
|
|
426
|
+
for (const provider of config.storage) {
|
|
427
|
+
if (provider.type === "github") {
|
|
428
|
+
storageConfig.github = {
|
|
429
|
+
token: provider.token || process.env.GITHUB_TOKEN,
|
|
430
|
+
apiBaseUrl: provider.apiBaseUrl || "https://api.github.com",
|
|
431
|
+
branch: provider.branch || "main"
|
|
432
|
+
};
|
|
433
|
+
} else if (provider.type === "http") {
|
|
434
|
+
storageConfig.http = {
|
|
435
|
+
baseUrl: provider.baseUrl,
|
|
436
|
+
headers: provider.headers,
|
|
437
|
+
timeout: provider.timeout || 3e4
|
|
438
|
+
};
|
|
439
|
+
} else if (provider.type === "local") {
|
|
440
|
+
storageConfig.local = {
|
|
441
|
+
basePath: provider.basePath || "./knowledge",
|
|
442
|
+
followSymlinks: provider.followSymlinks || false
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const storage = createStorageManager(storageConfig);
|
|
448
|
+
const cache = new CacheManager({
|
|
449
|
+
cacheDir,
|
|
450
|
+
defaultTtl: 86400,
|
|
451
|
+
// 24 hours
|
|
452
|
+
maxMemoryEntries: 100,
|
|
453
|
+
maxMemorySize: 50 * 1024 * 1024,
|
|
454
|
+
// 50MB
|
|
455
|
+
enablePersistence: true
|
|
456
|
+
});
|
|
457
|
+
cache.setStorageManager(storage);
|
|
458
|
+
const types = createDefaultRegistry();
|
|
459
|
+
if (config.types?.custom) {
|
|
460
|
+
for (const [name, customType] of Object.entries(config.types.custom)) {
|
|
461
|
+
const ct = customType;
|
|
462
|
+
types.register({
|
|
463
|
+
name,
|
|
464
|
+
description: ct.description || `Custom type: ${name}`,
|
|
465
|
+
patterns: ct.patterns || [],
|
|
466
|
+
defaultTtl: ct.defaultTtl || 86400,
|
|
467
|
+
archiveAfterDays: ct.archiveAfterDays !== void 0 ? ct.archiveAfterDays : null,
|
|
468
|
+
archiveStorage: ct.archiveStorage || null
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return new _CodexClient(cache, storage, types, organization);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (error instanceof CodexError2) {
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
throw new CodexError2(
|
|
478
|
+
`Failed to initialize CodexClient: ${error instanceof Error ? error.message : String(error)}`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Fetch a document by codex:// URI
|
|
484
|
+
*
|
|
485
|
+
* This method:
|
|
486
|
+
* 1. Validates the URI format
|
|
487
|
+
* 2. Resolves the URI to a reference
|
|
488
|
+
* 3. Uses CacheManager.get() which handles cache-first fetch
|
|
489
|
+
*
|
|
490
|
+
* @param uri - Codex URI (e.g., codex://org/project/path/to/file.md)
|
|
491
|
+
* @param options - Fetch options
|
|
492
|
+
* @returns Promise resolving to fetch result
|
|
493
|
+
*
|
|
494
|
+
* @throws {CodexError} If URI format is invalid or fetch fails
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* const result = await client.fetch('codex://fractary/codex/docs/README.md');
|
|
499
|
+
* console.log(result.content.toString());
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
502
|
+
async fetch(uri, options) {
|
|
503
|
+
const { validateUri, resolveReference, CodexError: CodexError2 } = await import('@fractary/codex');
|
|
504
|
+
if (!validateUri(uri)) {
|
|
505
|
+
throw new CodexError2(`Invalid codex URI: ${uri}`);
|
|
506
|
+
}
|
|
507
|
+
const resolved = resolveReference(uri);
|
|
508
|
+
if (!resolved) {
|
|
509
|
+
throw new CodexError2(`Failed to resolve URI: ${uri}`);
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
if (options?.bypassCache) {
|
|
513
|
+
const result2 = await this.storage.fetch(resolved);
|
|
514
|
+
return {
|
|
515
|
+
content: result2.content,
|
|
516
|
+
fromCache: false,
|
|
517
|
+
metadata: {
|
|
518
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
519
|
+
contentLength: result2.size
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
const result = await this.cache.get(resolved, {
|
|
524
|
+
ttl: options?.ttl
|
|
525
|
+
});
|
|
526
|
+
return {
|
|
527
|
+
content: result.content,
|
|
528
|
+
fromCache: true,
|
|
529
|
+
// CacheManager.get handles cache logic
|
|
530
|
+
metadata: {
|
|
531
|
+
contentLength: result.size
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
} catch (error) {
|
|
535
|
+
throw new CodexError2(
|
|
536
|
+
`Failed to fetch ${uri}: ${error instanceof Error ? error.message : String(error)}`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Invalidate cache entries
|
|
542
|
+
*
|
|
543
|
+
* @param pattern - Optional glob pattern to match entries
|
|
544
|
+
* If not provided, clears all entries
|
|
545
|
+
*
|
|
546
|
+
* @example
|
|
547
|
+
* ```typescript
|
|
548
|
+
* // Clear all cache
|
|
549
|
+
* await client.invalidateCache();
|
|
550
|
+
*
|
|
551
|
+
* // Clear specific URI
|
|
552
|
+
* await client.invalidateCache('codex://fractary/codex/docs/README.md');
|
|
553
|
+
* ```
|
|
554
|
+
*/
|
|
555
|
+
async invalidateCache(pattern) {
|
|
556
|
+
if (pattern) {
|
|
557
|
+
await this.cache.invalidate(pattern);
|
|
558
|
+
} else {
|
|
559
|
+
await this.cache.clear();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Get cache statistics
|
|
564
|
+
*
|
|
565
|
+
* @returns Promise resolving to cache stats
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* const stats = await client.getCacheStats();
|
|
570
|
+
* console.log(`Cache entries: ${stats.totalEntries}`);
|
|
571
|
+
* console.log(`Total size: ${stats.totalSize}`);
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
async getCacheStats() {
|
|
575
|
+
return this.cache.getStats();
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get the type registry
|
|
579
|
+
*
|
|
580
|
+
* Provides access to built-in and custom artifact types
|
|
581
|
+
*
|
|
582
|
+
* @returns TypeRegistry instance
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* const registry = client.getTypeRegistry();
|
|
587
|
+
* const types = registry.list();
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
getTypeRegistry() {
|
|
591
|
+
return this.types;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Get the cache manager (for advanced operations)
|
|
595
|
+
*
|
|
596
|
+
* @returns CacheManager instance
|
|
597
|
+
*/
|
|
598
|
+
getCacheManager() {
|
|
599
|
+
return this.cache;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Get the storage manager (for advanced operations)
|
|
603
|
+
*
|
|
604
|
+
* @returns StorageManager instance
|
|
605
|
+
*/
|
|
606
|
+
getStorageManager() {
|
|
607
|
+
return this.storage;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Get the organization slug
|
|
611
|
+
*
|
|
612
|
+
* @returns Organization slug string
|
|
613
|
+
*/
|
|
614
|
+
getOrganization() {
|
|
615
|
+
return this.organization;
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// src/cli.ts
|
|
622
|
+
init_cjs_shims();
|
|
623
|
+
|
|
624
|
+
// src/commands/init.ts
|
|
625
|
+
init_cjs_shims();
|
|
626
|
+
init_migrate_config();
|
|
627
|
+
async function getOrgFromGitRemote() {
|
|
628
|
+
try {
|
|
629
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
630
|
+
const remote = execSync2("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
631
|
+
const sshMatch = remote.match(/git@github\.com:([^/]+)\//);
|
|
632
|
+
const httpsMatch = remote.match(/github\.com\/([^/]+)\//);
|
|
633
|
+
return sshMatch?.[1] || httpsMatch?.[1] || null;
|
|
634
|
+
} catch {
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async function fileExists(filePath) {
|
|
639
|
+
try {
|
|
640
|
+
await fs__namespace.access(filePath);
|
|
641
|
+
return true;
|
|
642
|
+
} catch {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function initCommand() {
|
|
647
|
+
const cmd = new commander.Command("init");
|
|
648
|
+
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) => {
|
|
649
|
+
try {
|
|
650
|
+
console.log(chalk6__default.default.blue("Initializing Codex v3.0 (YAML format)...\n"));
|
|
651
|
+
let org = options.org;
|
|
652
|
+
if (!org) {
|
|
653
|
+
org = await getOrgFromGitRemote();
|
|
654
|
+
}
|
|
655
|
+
if (!org) {
|
|
656
|
+
try {
|
|
657
|
+
const { resolveOrganization } = await import('@fractary/codex');
|
|
658
|
+
org = resolveOrganization({
|
|
659
|
+
repoName: path2__namespace.basename(process.cwd())
|
|
660
|
+
});
|
|
661
|
+
} catch {
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (!org) {
|
|
665
|
+
org = path2__namespace.basename(process.cwd()).split("-")[0] || "default";
|
|
666
|
+
console.log(chalk6__default.default.yellow(`\u26A0 Could not detect organization, using: ${org}`));
|
|
667
|
+
console.log(chalk6__default.default.dim(" Use --org <slug> to specify explicitly\n"));
|
|
668
|
+
} else {
|
|
669
|
+
console.log(chalk6__default.default.dim(`Organization: ${chalk6__default.default.cyan(org)}
|
|
670
|
+
`));
|
|
671
|
+
}
|
|
672
|
+
const configDir = path2__namespace.join(process.cwd(), ".fractary");
|
|
673
|
+
const configPath = path2__namespace.join(configDir, "codex.yaml");
|
|
674
|
+
const configExists = await fileExists(configPath);
|
|
675
|
+
const legacyConfigPath = path2__namespace.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
676
|
+
const legacyExists = await fileExists(legacyConfigPath);
|
|
677
|
+
if (configExists && !options.force) {
|
|
678
|
+
console.log(chalk6__default.default.yellow("\u26A0 Configuration already exists at .fractary/codex.yaml"));
|
|
679
|
+
console.log(chalk6__default.default.dim("Use --force to overwrite"));
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
if (legacyExists && !configExists) {
|
|
683
|
+
console.log(chalk6__default.default.yellow("\u26A0 Legacy configuration detected at .fractary/plugins/codex/config.json"));
|
|
684
|
+
console.log(chalk6__default.default.dim('Run "fractary codex migrate" to upgrade to YAML format\n'));
|
|
685
|
+
}
|
|
686
|
+
console.log("Creating directory structure...");
|
|
687
|
+
const dirs = [
|
|
688
|
+
".fractary",
|
|
689
|
+
".codex-cache"
|
|
690
|
+
];
|
|
691
|
+
for (const dir of dirs) {
|
|
692
|
+
await fs__namespace.mkdir(path2__namespace.join(process.cwd(), dir), { recursive: true });
|
|
693
|
+
console.log(chalk6__default.default.green("\u2713"), chalk6__default.default.dim(dir + "/"));
|
|
694
|
+
}
|
|
695
|
+
console.log("\nCreating YAML configuration...");
|
|
696
|
+
const config = getDefaultYamlConfig(org);
|
|
697
|
+
if (options.mcp && config.mcp) {
|
|
698
|
+
config.mcp.enabled = true;
|
|
699
|
+
}
|
|
700
|
+
await writeYamlConfig(config, configPath);
|
|
701
|
+
console.log(chalk6__default.default.green("\u2713"), chalk6__default.default.dim(".fractary/codex.yaml"));
|
|
702
|
+
console.log(chalk6__default.default.green("\n\u2713 Codex v3.0 initialized successfully!\n"));
|
|
703
|
+
console.log(chalk6__default.default.bold("Configuration:"));
|
|
704
|
+
console.log(chalk6__default.default.dim(` Organization: ${org}`));
|
|
705
|
+
console.log(chalk6__default.default.dim(` Cache: .codex-cache/`));
|
|
706
|
+
console.log(chalk6__default.default.dim(` Config: .fractary/codex.yaml`));
|
|
707
|
+
if (options.mcp) {
|
|
708
|
+
console.log(chalk6__default.default.dim(` MCP Server: Enabled (port 3000)`));
|
|
709
|
+
}
|
|
710
|
+
console.log(chalk6__default.default.bold("\nStorage providers configured:"));
|
|
711
|
+
console.log(chalk6__default.default.dim(" - Local filesystem (./knowledge)"));
|
|
712
|
+
console.log(chalk6__default.default.dim(" - GitHub (requires GITHUB_TOKEN)"));
|
|
713
|
+
console.log(chalk6__default.default.dim(" - HTTP endpoint"));
|
|
714
|
+
console.log(chalk6__default.default.bold("\nNext steps:"));
|
|
715
|
+
console.log(chalk6__default.default.dim(' 1. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
716
|
+
console.log(chalk6__default.default.dim(" 2. Edit .fractary/codex.yaml to configure storage providers"));
|
|
717
|
+
console.log(chalk6__default.default.dim(" 3. Fetch a document: fractary codex fetch codex://org/project/path"));
|
|
718
|
+
console.log(chalk6__default.default.dim(" 4. Check cache: fractary codex cache list"));
|
|
719
|
+
if (legacyExists) {
|
|
720
|
+
console.log(chalk6__default.default.yellow("\n\u26A0 Legacy config detected:"));
|
|
721
|
+
console.log(chalk6__default.default.dim(' Run "fractary codex migrate" to convert your existing config'));
|
|
722
|
+
}
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
725
|
+
process.exit(1);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
return cmd;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/commands/fetch.ts
|
|
732
|
+
init_cjs_shims();
|
|
733
|
+
|
|
734
|
+
// src/client/get-client.ts
|
|
735
|
+
init_cjs_shims();
|
|
736
|
+
var clientInstance = null;
|
|
737
|
+
async function getClient(options) {
|
|
738
|
+
if (!clientInstance) {
|
|
739
|
+
const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
|
|
740
|
+
clientInstance = await CodexClient2.create(options);
|
|
741
|
+
}
|
|
742
|
+
return clientInstance;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/commands/fetch.ts
|
|
746
|
+
function hashContent(content) {
|
|
747
|
+
const crypto = __require("crypto");
|
|
748
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
749
|
+
}
|
|
750
|
+
function fetchCommand() {
|
|
751
|
+
const cmd = new commander.Command("fetch");
|
|
752
|
+
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) => {
|
|
753
|
+
try {
|
|
754
|
+
const { validateUri } = await import('@fractary/codex');
|
|
755
|
+
if (!validateUri(uri)) {
|
|
756
|
+
console.error(chalk6__default.default.red("Error: Invalid URI format"));
|
|
757
|
+
console.log(chalk6__default.default.dim("Expected: codex://org/project/path/to/file.md"));
|
|
758
|
+
console.log(chalk6__default.default.dim("Example: codex://fractary/codex/docs/api.md"));
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
761
|
+
const client = await getClient();
|
|
762
|
+
if (!options.json && !options.bypassCache) {
|
|
763
|
+
console.error(chalk6__default.default.dim(`Fetching ${uri}...`));
|
|
764
|
+
}
|
|
765
|
+
const result = await client.fetch(uri, {
|
|
766
|
+
bypassCache: options.bypassCache,
|
|
767
|
+
ttl: options.ttl
|
|
768
|
+
});
|
|
769
|
+
if (options.json) {
|
|
770
|
+
const output = {
|
|
771
|
+
uri,
|
|
772
|
+
content: result.content.toString("utf-8"),
|
|
773
|
+
metadata: {
|
|
774
|
+
fromCache: result.fromCache,
|
|
775
|
+
fetchedAt: result.metadata?.fetchedAt,
|
|
776
|
+
expiresAt: result.metadata?.expiresAt,
|
|
777
|
+
contentLength: result.metadata?.contentLength || result.content.length,
|
|
778
|
+
contentHash: hashContent(result.content)
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
console.log(JSON.stringify(output, null, 2));
|
|
782
|
+
} else if (options.output) {
|
|
783
|
+
await fs__namespace.writeFile(options.output, result.content);
|
|
784
|
+
console.log(chalk6__default.default.green("\u2713"), `Written to ${options.output}`);
|
|
785
|
+
console.log(chalk6__default.default.dim(` Size: ${result.content.length} bytes`));
|
|
786
|
+
if (result.fromCache) {
|
|
787
|
+
console.log(chalk6__default.default.dim(" Source: cache"));
|
|
788
|
+
} else {
|
|
789
|
+
console.log(chalk6__default.default.dim(" Source: storage"));
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
if (result.fromCache && !options.bypassCache) {
|
|
793
|
+
console.error(chalk6__default.default.green("\u2713"), chalk6__default.default.dim("from cache\n"));
|
|
794
|
+
} else {
|
|
795
|
+
console.error(chalk6__default.default.green("\u2713"), chalk6__default.default.dim("fetched\n"));
|
|
796
|
+
}
|
|
797
|
+
console.log(result.content.toString("utf-8"));
|
|
798
|
+
}
|
|
799
|
+
} catch (error) {
|
|
800
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
801
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
802
|
+
console.log(chalk6__default.default.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
803
|
+
} else if (error.message.includes("GITHUB_TOKEN")) {
|
|
804
|
+
console.log(chalk6__default.default.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
805
|
+
} else if (error.message.includes("not found") || error.message.includes("404")) {
|
|
806
|
+
console.log(chalk6__default.default.dim("\nThe document may not exist or you may not have access."));
|
|
807
|
+
console.log(chalk6__default.default.dim("Check the URI and ensure your storage providers are configured correctly."));
|
|
808
|
+
}
|
|
809
|
+
process.exit(1);
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
return cmd;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/commands/cache/index.ts
|
|
816
|
+
init_cjs_shims();
|
|
817
|
+
|
|
818
|
+
// src/commands/cache/list.ts
|
|
819
|
+
init_cjs_shims();
|
|
820
|
+
function formatSize(bytes) {
|
|
821
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
822
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
823
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
824
|
+
}
|
|
825
|
+
function cacheListCommand() {
|
|
826
|
+
const cmd = new commander.Command("list");
|
|
827
|
+
cmd.description("List cache information").option("--json", "Output as JSON").action(async (options) => {
|
|
828
|
+
try {
|
|
829
|
+
const client = await getClient();
|
|
830
|
+
const stats = await client.getCacheStats();
|
|
831
|
+
if (stats.entryCount === 0) {
|
|
832
|
+
if (options.json) {
|
|
833
|
+
console.log(JSON.stringify({ entries: 0, message: "Cache is empty" }));
|
|
834
|
+
} else {
|
|
835
|
+
console.log(chalk6__default.default.yellow("Cache is empty."));
|
|
836
|
+
console.log(chalk6__default.default.dim("Fetch some documents to populate the cache."));
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (options.json) {
|
|
841
|
+
console.log(JSON.stringify({
|
|
842
|
+
entryCount: stats.entryCount,
|
|
843
|
+
totalSize: stats.totalSize,
|
|
844
|
+
freshCount: stats.freshCount,
|
|
845
|
+
staleCount: stats.staleCount,
|
|
846
|
+
expiredCount: stats.expiredCount
|
|
847
|
+
}, null, 2));
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
console.log(chalk6__default.default.bold("Cache Overview\n"));
|
|
851
|
+
console.log(chalk6__default.default.bold("Entries:"));
|
|
852
|
+
console.log(` Total: ${chalk6__default.default.cyan(stats.entryCount.toString())} entries`);
|
|
853
|
+
console.log(` Fresh: ${chalk6__default.default.green(stats.freshCount.toString())} entries`);
|
|
854
|
+
console.log(` Stale: ${stats.staleCount > 0 ? chalk6__default.default.yellow(stats.staleCount.toString()) : chalk6__default.default.dim("0")} entries`);
|
|
855
|
+
console.log(` Expired: ${stats.expiredCount > 0 ? chalk6__default.default.red(stats.expiredCount.toString()) : chalk6__default.default.dim("0")} entries`);
|
|
856
|
+
console.log("");
|
|
857
|
+
console.log(chalk6__default.default.bold("Storage:"));
|
|
858
|
+
console.log(` Total size: ${chalk6__default.default.cyan(formatSize(stats.totalSize))}`);
|
|
859
|
+
console.log("");
|
|
860
|
+
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
861
|
+
const healthColor = healthPercent > 80 ? chalk6__default.default.green : healthPercent > 50 ? chalk6__default.default.yellow : chalk6__default.default.red;
|
|
862
|
+
console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
|
|
863
|
+
console.log("");
|
|
864
|
+
console.log(chalk6__default.default.dim("Note: Individual cache entries are managed by the SDK."));
|
|
865
|
+
console.log(chalk6__default.default.dim('Use "fractary codex cache stats" for detailed statistics.'));
|
|
866
|
+
console.log(chalk6__default.default.dim('Use "fractary codex cache clear" to clear cache entries.'));
|
|
867
|
+
} catch (error) {
|
|
868
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
869
|
+
process.exit(1);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
return cmd;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/commands/cache/clear.ts
|
|
876
|
+
init_cjs_shims();
|
|
877
|
+
function cacheClearCommand() {
|
|
878
|
+
const cmd = new commander.Command("clear");
|
|
879
|
+
cmd.description("Clear cache entries").option("--all", "Clear entire cache").option("--pattern <glob>", 'Clear entries matching URI pattern (e.g., "codex://fractary/*")').option("--dry-run", "Show what would be cleared without actually clearing").action(async (options) => {
|
|
880
|
+
try {
|
|
881
|
+
const client = await getClient();
|
|
882
|
+
const statsBefore = await client.getCacheStats();
|
|
883
|
+
if (statsBefore.entryCount === 0) {
|
|
884
|
+
console.log(chalk6__default.default.yellow("Cache is already empty. Nothing to clear."));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
let pattern;
|
|
888
|
+
if (options.all) {
|
|
889
|
+
pattern = void 0;
|
|
890
|
+
} else if (options.pattern) {
|
|
891
|
+
pattern = options.pattern;
|
|
892
|
+
} else {
|
|
893
|
+
console.log(chalk6__default.default.yellow("Please specify what to clear:"));
|
|
894
|
+
console.log(chalk6__default.default.dim(" --all Clear entire cache"));
|
|
895
|
+
console.log(chalk6__default.default.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
|
|
896
|
+
console.log("");
|
|
897
|
+
console.log(chalk6__default.default.dim("Examples:"));
|
|
898
|
+
console.log(chalk6__default.default.dim(" fractary codex cache clear --all"));
|
|
899
|
+
console.log(chalk6__default.default.dim(' fractary codex cache clear --pattern "codex://fractary/cli/*"'));
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
if (options.dryRun) {
|
|
903
|
+
console.log(chalk6__default.default.blue("Dry run - would clear:\n"));
|
|
904
|
+
if (pattern) {
|
|
905
|
+
console.log(chalk6__default.default.dim(` Pattern: ${pattern}`));
|
|
906
|
+
console.log(chalk6__default.default.dim(` This would invalidate matching cache entries`));
|
|
907
|
+
} else {
|
|
908
|
+
console.log(chalk6__default.default.dim(` All cache entries (${statsBefore.entryCount} entries)`));
|
|
909
|
+
}
|
|
910
|
+
console.log(chalk6__default.default.dim(`
|
|
911
|
+
Total size: ${formatSize2(statsBefore.totalSize)}`));
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (pattern) {
|
|
915
|
+
console.log(chalk6__default.default.blue(`Clearing cache entries matching pattern: ${pattern}
|
|
916
|
+
`));
|
|
917
|
+
await client.invalidateCache(pattern);
|
|
918
|
+
} else {
|
|
919
|
+
console.log(chalk6__default.default.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
|
|
920
|
+
`));
|
|
921
|
+
await client.invalidateCache();
|
|
922
|
+
}
|
|
923
|
+
const statsAfter = await client.getCacheStats();
|
|
924
|
+
const entriesCleared = statsBefore.entryCount - statsAfter.entryCount;
|
|
925
|
+
const sizeFreed = statsBefore.totalSize - statsAfter.totalSize;
|
|
926
|
+
console.log(chalk6__default.default.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
|
|
927
|
+
if (statsAfter.entryCount > 0) {
|
|
928
|
+
console.log(chalk6__default.default.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
|
|
929
|
+
}
|
|
930
|
+
} catch (error) {
|
|
931
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
932
|
+
process.exit(1);
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
return cmd;
|
|
936
|
+
}
|
|
937
|
+
function formatSize2(bytes) {
|
|
938
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
939
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
940
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/commands/cache/stats.ts
|
|
944
|
+
init_cjs_shims();
|
|
945
|
+
function formatSize3(bytes) {
|
|
946
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
947
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
948
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
949
|
+
}
|
|
950
|
+
function cacheStatsCommand() {
|
|
951
|
+
const cmd = new commander.Command("stats");
|
|
952
|
+
cmd.description("Display cache statistics").option("--json", "Output as JSON").action(async (options) => {
|
|
953
|
+
try {
|
|
954
|
+
const client = await getClient();
|
|
955
|
+
const stats = await client.getCacheStats();
|
|
956
|
+
if (options.json) {
|
|
957
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
console.log(chalk6__default.default.bold("Cache Statistics\n"));
|
|
961
|
+
console.log(chalk6__default.default.bold("Overview"));
|
|
962
|
+
console.log(` Total entries: ${chalk6__default.default.cyan(stats.entryCount.toString())}`);
|
|
963
|
+
console.log(` Total size: ${chalk6__default.default.cyan(formatSize3(stats.totalSize))}`);
|
|
964
|
+
console.log(` Fresh entries: ${chalk6__default.default.green(stats.freshCount.toString())}`);
|
|
965
|
+
console.log(` Stale entries: ${stats.staleCount > 0 ? chalk6__default.default.yellow(stats.staleCount.toString()) : chalk6__default.default.dim("0")}`);
|
|
966
|
+
console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk6__default.default.red(stats.expiredCount.toString()) : chalk6__default.default.dim("0")}`);
|
|
967
|
+
console.log("");
|
|
968
|
+
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
969
|
+
const healthColor = healthPercent > 80 ? chalk6__default.default.green : healthPercent > 50 ? chalk6__default.default.yellow : chalk6__default.default.red;
|
|
970
|
+
console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
|
|
971
|
+
if (stats.expiredCount > 0) {
|
|
972
|
+
console.log(chalk6__default.default.dim('\nRun "fractary codex cache clear --pattern <pattern>" to clean up entries.'));
|
|
973
|
+
}
|
|
974
|
+
if (stats.entryCount === 0) {
|
|
975
|
+
console.log(chalk6__default.default.dim("\nNo cached entries. Fetch some documents to populate the cache."));
|
|
976
|
+
}
|
|
977
|
+
} catch (error) {
|
|
978
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
979
|
+
process.exit(1);
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
return cmd;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/commands/cache/index.ts
|
|
986
|
+
function cacheCommand() {
|
|
987
|
+
const cmd = new commander.Command("cache");
|
|
988
|
+
cmd.description("Manage the codex document cache");
|
|
989
|
+
cmd.addCommand(cacheListCommand());
|
|
990
|
+
cmd.addCommand(cacheClearCommand());
|
|
991
|
+
cmd.addCommand(cacheStatsCommand());
|
|
992
|
+
return cmd;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/commands/sync/index.ts
|
|
996
|
+
init_cjs_shims();
|
|
997
|
+
|
|
998
|
+
// src/commands/sync/project.ts
|
|
999
|
+
init_cjs_shims();
|
|
1000
|
+
init_migrate_config();
|
|
1001
|
+
function getEnvironmentBranch(config, env) {
|
|
1002
|
+
const envMap = config.sync?.environments || {
|
|
1003
|
+
dev: "develop",
|
|
1004
|
+
test: "test",
|
|
1005
|
+
staging: "staging",
|
|
1006
|
+
prod: "main"
|
|
1007
|
+
};
|
|
1008
|
+
return envMap[env] || env;
|
|
1009
|
+
}
|
|
1010
|
+
function formatBytes(bytes) {
|
|
1011
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1012
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1013
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1014
|
+
}
|
|
1015
|
+
function formatDuration(ms) {
|
|
1016
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
1017
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1018
|
+
}
|
|
1019
|
+
function syncProjectCommand() {
|
|
1020
|
+
const cmd = new commander.Command("project");
|
|
1021
|
+
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) => {
|
|
1022
|
+
try {
|
|
1023
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1024
|
+
let config;
|
|
1025
|
+
try {
|
|
1026
|
+
config = await readYamlConfig(configPath);
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
console.error(chalk6__default.default.red("Error:"), "Codex not initialized.");
|
|
1029
|
+
console.log(chalk6__default.default.dim('Run "fractary codex init" first.'));
|
|
1030
|
+
process.exit(1);
|
|
1031
|
+
}
|
|
1032
|
+
const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
|
|
1033
|
+
let projectName = name;
|
|
1034
|
+
if (!projectName) {
|
|
1035
|
+
const detected = detectCurrentProject();
|
|
1036
|
+
projectName = detected.project || null;
|
|
1037
|
+
}
|
|
1038
|
+
if (!projectName) {
|
|
1039
|
+
console.error(chalk6__default.default.red("Error:"), "Could not determine project name.");
|
|
1040
|
+
console.log(chalk6__default.default.dim("Provide project name as argument or run from a git repository."));
|
|
1041
|
+
process.exit(1);
|
|
1042
|
+
}
|
|
1043
|
+
const validDirections = ["to-codex", "from-codex", "bidirectional"];
|
|
1044
|
+
if (!validDirections.includes(options.direction)) {
|
|
1045
|
+
console.error(chalk6__default.default.red("Error:"), `Invalid direction: ${options.direction}`);
|
|
1046
|
+
console.log(chalk6__default.default.dim("Valid options: to-codex, from-codex, bidirectional"));
|
|
1047
|
+
process.exit(1);
|
|
1048
|
+
}
|
|
1049
|
+
const direction = options.direction;
|
|
1050
|
+
const targetBranch = getEnvironmentBranch(config, options.env);
|
|
1051
|
+
const localStorage = createLocalStorage({
|
|
1052
|
+
baseDir: process.cwd()
|
|
1053
|
+
});
|
|
1054
|
+
const syncManager = createSyncManager({
|
|
1055
|
+
localStorage,
|
|
1056
|
+
config: config.sync,
|
|
1057
|
+
manifestPath: path2__namespace.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
|
|
1058
|
+
});
|
|
1059
|
+
const defaultPatterns = config.sync?.include || [
|
|
1060
|
+
"docs/**/*.md",
|
|
1061
|
+
"specs/**/*.md",
|
|
1062
|
+
".fractary/standards/**",
|
|
1063
|
+
".fractary/templates/**"
|
|
1064
|
+
];
|
|
1065
|
+
const includePatterns = options.include.length > 0 ? options.include : defaultPatterns;
|
|
1066
|
+
const excludePatterns = [
|
|
1067
|
+
...config.sync?.exclude || [],
|
|
1068
|
+
...options.exclude
|
|
1069
|
+
];
|
|
1070
|
+
const sourceDir = process.cwd();
|
|
1071
|
+
const allFiles = await syncManager.listLocalFiles(sourceDir);
|
|
1072
|
+
const targetFiles = allFiles.filter((file) => {
|
|
1073
|
+
for (const pattern of excludePatterns) {
|
|
1074
|
+
const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
|
|
1075
|
+
if (regex.test(file.path)) {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
for (const pattern of includePatterns) {
|
|
1080
|
+
const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
|
|
1081
|
+
if (regex.test(file.path)) {
|
|
1082
|
+
return true;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return false;
|
|
1086
|
+
});
|
|
1087
|
+
const syncOptions = {
|
|
1088
|
+
direction,
|
|
1089
|
+
dryRun: options.dryRun,
|
|
1090
|
+
force: options.force,
|
|
1091
|
+
include: includePatterns,
|
|
1092
|
+
exclude: excludePatterns
|
|
1093
|
+
};
|
|
1094
|
+
const plan = await syncManager.createPlan(
|
|
1095
|
+
config.organization,
|
|
1096
|
+
projectName,
|
|
1097
|
+
sourceDir,
|
|
1098
|
+
targetFiles,
|
|
1099
|
+
syncOptions
|
|
1100
|
+
);
|
|
1101
|
+
if (plan.totalFiles === 0) {
|
|
1102
|
+
if (options.json) {
|
|
1103
|
+
console.log(JSON.stringify({
|
|
1104
|
+
project: projectName,
|
|
1105
|
+
organization: config.organization,
|
|
1106
|
+
files: [],
|
|
1107
|
+
synced: 0
|
|
1108
|
+
}, null, 2));
|
|
1109
|
+
} else {
|
|
1110
|
+
console.log(chalk6__default.default.yellow("No files to sync."));
|
|
1111
|
+
}
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
if (options.json) {
|
|
1115
|
+
const output = {
|
|
1116
|
+
project: projectName,
|
|
1117
|
+
organization: config.organization,
|
|
1118
|
+
environment: options.env,
|
|
1119
|
+
branch: targetBranch,
|
|
1120
|
+
direction,
|
|
1121
|
+
dryRun: options.dryRun || false,
|
|
1122
|
+
plan: {
|
|
1123
|
+
totalFiles: plan.totalFiles,
|
|
1124
|
+
totalBytes: plan.totalBytes,
|
|
1125
|
+
estimatedTime: plan.estimatedTime,
|
|
1126
|
+
conflicts: plan.conflicts.length,
|
|
1127
|
+
skipped: plan.skipped.length
|
|
1128
|
+
},
|
|
1129
|
+
files: plan.files.map((f) => ({
|
|
1130
|
+
path: f.path,
|
|
1131
|
+
operation: f.operation,
|
|
1132
|
+
size: f.size
|
|
1133
|
+
}))
|
|
1134
|
+
};
|
|
1135
|
+
if (options.dryRun) {
|
|
1136
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const result2 = await syncManager.executePlan(plan, syncOptions);
|
|
1140
|
+
console.log(JSON.stringify({
|
|
1141
|
+
...output,
|
|
1142
|
+
result: {
|
|
1143
|
+
success: result2.success,
|
|
1144
|
+
synced: result2.synced,
|
|
1145
|
+
failed: result2.failed,
|
|
1146
|
+
skipped: result2.skipped,
|
|
1147
|
+
duration: result2.duration,
|
|
1148
|
+
errors: result2.errors
|
|
1149
|
+
}
|
|
1150
|
+
}, null, 2));
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
console.log(chalk6__default.default.bold("Sync Plan\n"));
|
|
1154
|
+
console.log(` Project: ${chalk6__default.default.cyan(projectName)}`);
|
|
1155
|
+
console.log(` Organization: ${chalk6__default.default.cyan(config.organization)}`);
|
|
1156
|
+
console.log(` Environment: ${chalk6__default.default.cyan(options.env)} (${targetBranch})`);
|
|
1157
|
+
console.log(` Direction: ${chalk6__default.default.cyan(direction)}`);
|
|
1158
|
+
console.log(` Files: ${chalk6__default.default.cyan(plan.totalFiles.toString())}`);
|
|
1159
|
+
console.log(` Total size: ${chalk6__default.default.cyan(formatBytes(plan.totalBytes))}`);
|
|
1160
|
+
if (plan.estimatedTime) {
|
|
1161
|
+
console.log(` Est. time: ${chalk6__default.default.dim(formatDuration(plan.estimatedTime))}`);
|
|
1162
|
+
}
|
|
1163
|
+
console.log("");
|
|
1164
|
+
if (plan.conflicts.length > 0) {
|
|
1165
|
+
console.log(chalk6__default.default.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
|
|
1166
|
+
for (const conflict of plan.conflicts.slice(0, 5)) {
|
|
1167
|
+
console.log(chalk6__default.default.yellow(` \u2022 ${conflict.path}`));
|
|
1168
|
+
}
|
|
1169
|
+
if (plan.conflicts.length > 5) {
|
|
1170
|
+
console.log(chalk6__default.default.dim(` ... and ${plan.conflicts.length - 5} more`));
|
|
1171
|
+
}
|
|
1172
|
+
console.log("");
|
|
1173
|
+
}
|
|
1174
|
+
if (plan.skipped.length > 0) {
|
|
1175
|
+
console.log(chalk6__default.default.dim(`${plan.skipped.length} files skipped (no changes)`));
|
|
1176
|
+
console.log("");
|
|
1177
|
+
}
|
|
1178
|
+
if (options.dryRun) {
|
|
1179
|
+
console.log(chalk6__default.default.blue("Dry run - would sync:\n"));
|
|
1180
|
+
const filesToShow = plan.files.slice(0, 10);
|
|
1181
|
+
for (const file of filesToShow) {
|
|
1182
|
+
const arrow = direction === "to-codex" ? "\u2192" : direction === "from-codex" ? "\u2190" : "\u2194";
|
|
1183
|
+
const opColor = file.operation === "create" ? chalk6__default.default.green : file.operation === "update" ? chalk6__default.default.yellow : chalk6__default.default.dim;
|
|
1184
|
+
console.log(
|
|
1185
|
+
chalk6__default.default.dim(` ${arrow}`),
|
|
1186
|
+
opColor(file.operation.padEnd(7)),
|
|
1187
|
+
file.path,
|
|
1188
|
+
chalk6__default.default.dim(`(${formatBytes(file.size || 0)})`)
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
if (plan.files.length > 10) {
|
|
1192
|
+
console.log(chalk6__default.default.dim(` ... and ${plan.files.length - 10} more files`));
|
|
1193
|
+
}
|
|
1194
|
+
console.log(chalk6__default.default.dim(`
|
|
1195
|
+
Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
|
|
1196
|
+
console.log(chalk6__default.default.dim("Run without --dry-run to execute sync."));
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
console.log(chalk6__default.default.blue("Syncing...\n"));
|
|
1200
|
+
const startTime = Date.now();
|
|
1201
|
+
const result = await syncManager.executePlan(plan, syncOptions);
|
|
1202
|
+
const duration = Date.now() - startTime;
|
|
1203
|
+
console.log("");
|
|
1204
|
+
if (result.success) {
|
|
1205
|
+
console.log(chalk6__default.default.green(`\u2713 Sync completed successfully`));
|
|
1206
|
+
console.log(chalk6__default.default.dim(` Synced: ${result.synced} files`));
|
|
1207
|
+
if (result.skipped > 0) {
|
|
1208
|
+
console.log(chalk6__default.default.dim(` Skipped: ${result.skipped} files`));
|
|
1209
|
+
}
|
|
1210
|
+
console.log(chalk6__default.default.dim(` Duration: ${formatDuration(duration)}`));
|
|
1211
|
+
} else {
|
|
1212
|
+
console.log(chalk6__default.default.yellow(`\u26A0 Sync completed with errors`));
|
|
1213
|
+
console.log(chalk6__default.default.green(` Synced: ${result.synced} files`));
|
|
1214
|
+
console.log(chalk6__default.default.red(` Failed: ${result.failed} files`));
|
|
1215
|
+
if (result.skipped > 0) {
|
|
1216
|
+
console.log(chalk6__default.default.dim(` Skipped: ${result.skipped} files`));
|
|
1217
|
+
}
|
|
1218
|
+
if (result.errors.length > 0) {
|
|
1219
|
+
console.log("");
|
|
1220
|
+
console.log(chalk6__default.default.red("Errors:"));
|
|
1221
|
+
for (const error of result.errors.slice(0, 5)) {
|
|
1222
|
+
console.log(chalk6__default.default.red(` \u2022 ${error.path}: ${error.error}`));
|
|
1223
|
+
}
|
|
1224
|
+
if (result.errors.length > 5) {
|
|
1225
|
+
console.log(chalk6__default.default.dim(` ... and ${result.errors.length - 5} more errors`));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1231
|
+
process.exit(1);
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
return cmd;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// src/commands/sync/org.ts
|
|
1238
|
+
init_cjs_shims();
|
|
1239
|
+
init_migrate_config();
|
|
1240
|
+
function getEnvironmentBranch2(config, env) {
|
|
1241
|
+
const envMap = config.sync?.environments || {
|
|
1242
|
+
dev: "develop",
|
|
1243
|
+
test: "test",
|
|
1244
|
+
staging: "staging",
|
|
1245
|
+
prod: "main"
|
|
1246
|
+
};
|
|
1247
|
+
return envMap[env] || env;
|
|
1248
|
+
}
|
|
1249
|
+
async function discoverRepos(org) {
|
|
1250
|
+
try {
|
|
1251
|
+
const output = child_process.execSync(
|
|
1252
|
+
`gh repo list ${org} --json name,url,defaultBranchRef --limit 100`,
|
|
1253
|
+
{ encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
|
|
1254
|
+
);
|
|
1255
|
+
const repos = JSON.parse(output);
|
|
1256
|
+
return repos.map((repo) => ({
|
|
1257
|
+
name: repo.name,
|
|
1258
|
+
url: repo.url,
|
|
1259
|
+
defaultBranch: repo.defaultBranchRef?.name || "main"
|
|
1260
|
+
}));
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
throw new Error(`Failed to discover repos: ${error.message}. Ensure GitHub CLI is installed and authenticated.`);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function shouldExclude(repoName, excludePatterns) {
|
|
1266
|
+
for (const pattern of excludePatterns) {
|
|
1267
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
1268
|
+
if (regex.test(repoName)) {
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return false;
|
|
1273
|
+
}
|
|
1274
|
+
async function syncRepository(repo, config, direction, syncOptions) {
|
|
1275
|
+
const startTime = Date.now();
|
|
1276
|
+
try {
|
|
1277
|
+
const { createSyncManager, createLocalStorage } = await import('@fractary/codex');
|
|
1278
|
+
const repoDir = path2__namespace.join(process.cwd(), "..", repo.name);
|
|
1279
|
+
const localStorage = createLocalStorage({
|
|
1280
|
+
baseDir: repoDir
|
|
1281
|
+
});
|
|
1282
|
+
const syncManager = createSyncManager({
|
|
1283
|
+
localStorage,
|
|
1284
|
+
config: config.sync,
|
|
1285
|
+
manifestPath: path2__namespace.join(repoDir, ".fractary", ".codex-sync-manifest.json")
|
|
1286
|
+
});
|
|
1287
|
+
const includePatterns = config.sync?.include || [
|
|
1288
|
+
"docs/**/*.md",
|
|
1289
|
+
"specs/**/*.md",
|
|
1290
|
+
".fractary/standards/**",
|
|
1291
|
+
".fractary/templates/**"
|
|
1292
|
+
];
|
|
1293
|
+
const allFiles = await syncManager.listLocalFiles(repoDir);
|
|
1294
|
+
const targetFiles = allFiles.filter((file) => {
|
|
1295
|
+
for (const pattern of includePatterns) {
|
|
1296
|
+
const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
|
|
1297
|
+
if (regex.test(file.path)) {
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return false;
|
|
1302
|
+
});
|
|
1303
|
+
const plan = await syncManager.createPlan(
|
|
1304
|
+
config.organization,
|
|
1305
|
+
repo.name,
|
|
1306
|
+
repoDir,
|
|
1307
|
+
targetFiles,
|
|
1308
|
+
syncOptions
|
|
1309
|
+
);
|
|
1310
|
+
if (plan.totalFiles === 0) {
|
|
1311
|
+
return {
|
|
1312
|
+
repo: repo.name,
|
|
1313
|
+
status: "skipped",
|
|
1314
|
+
filesSynced: 0,
|
|
1315
|
+
duration: Date.now() - startTime
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
const result = await syncManager.executePlan(plan, syncOptions);
|
|
1319
|
+
return {
|
|
1320
|
+
repo: repo.name,
|
|
1321
|
+
status: result.success ? "success" : "error",
|
|
1322
|
+
filesSynced: result.synced,
|
|
1323
|
+
duration: Date.now() - startTime,
|
|
1324
|
+
error: result.success ? void 0 : `${result.failed} files failed`
|
|
1325
|
+
};
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
return {
|
|
1328
|
+
repo: repo.name,
|
|
1329
|
+
status: "error",
|
|
1330
|
+
duration: Date.now() - startTime,
|
|
1331
|
+
error: error.message
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
function syncOrgCommand() {
|
|
1336
|
+
const cmd = new commander.Command("org");
|
|
1337
|
+
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) => {
|
|
1338
|
+
try {
|
|
1339
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1340
|
+
let config;
|
|
1341
|
+
try {
|
|
1342
|
+
config = await readYamlConfig(configPath);
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
console.error(chalk6__default.default.red("Error:"), "Codex not initialized.");
|
|
1345
|
+
console.log(chalk6__default.default.dim('Run "fractary codex init" first.'));
|
|
1346
|
+
process.exit(1);
|
|
1347
|
+
}
|
|
1348
|
+
const validDirections = ["to-codex", "from-codex", "bidirectional"];
|
|
1349
|
+
if (!validDirections.includes(options.direction)) {
|
|
1350
|
+
console.error(chalk6__default.default.red("Error:"), `Invalid direction: ${options.direction}`);
|
|
1351
|
+
console.log(chalk6__default.default.dim("Valid options: to-codex, from-codex, bidirectional"));
|
|
1352
|
+
process.exit(1);
|
|
1353
|
+
}
|
|
1354
|
+
const direction = options.direction;
|
|
1355
|
+
const org = config.organization;
|
|
1356
|
+
const targetBranch = getEnvironmentBranch2(config, options.env);
|
|
1357
|
+
const excludePatterns = [
|
|
1358
|
+
...config.sync?.exclude || [],
|
|
1359
|
+
...options.exclude
|
|
1360
|
+
];
|
|
1361
|
+
if (!options.json) {
|
|
1362
|
+
console.log(chalk6__default.default.bold("Organization Sync\n"));
|
|
1363
|
+
console.log(` Organization: ${chalk6__default.default.cyan(org)}`);
|
|
1364
|
+
console.log(` Environment: ${chalk6__default.default.cyan(options.env)} (${targetBranch})`);
|
|
1365
|
+
console.log(` Direction: ${chalk6__default.default.cyan(direction)}`);
|
|
1366
|
+
console.log(` Parallelism: ${chalk6__default.default.cyan(options.parallel.toString())}`);
|
|
1367
|
+
if (excludePatterns.length > 0) {
|
|
1368
|
+
console.log(` Excluding: ${chalk6__default.default.dim(excludePatterns.join(", "))}`);
|
|
1369
|
+
}
|
|
1370
|
+
console.log("");
|
|
1371
|
+
console.log(chalk6__default.default.dim("Discovering repositories..."));
|
|
1372
|
+
}
|
|
1373
|
+
let repos;
|
|
1374
|
+
try {
|
|
1375
|
+
repos = await discoverRepos(org);
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
if (options.json) {
|
|
1378
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
1379
|
+
} else {
|
|
1380
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1381
|
+
}
|
|
1382
|
+
process.exit(1);
|
|
1383
|
+
}
|
|
1384
|
+
const eligibleRepos = repos.filter((repo) => !shouldExclude(repo.name, excludePatterns));
|
|
1385
|
+
const excludedRepos = repos.filter((repo) => shouldExclude(repo.name, excludePatterns));
|
|
1386
|
+
if (!options.json) {
|
|
1387
|
+
console.log(chalk6__default.default.dim(`Found ${repos.length} repositories (${excludedRepos.length} excluded)
|
|
1388
|
+
`));
|
|
1389
|
+
}
|
|
1390
|
+
if (options.dryRun) {
|
|
1391
|
+
if (options.json) {
|
|
1392
|
+
console.log(JSON.stringify({
|
|
1393
|
+
organization: org,
|
|
1394
|
+
environment: options.env,
|
|
1395
|
+
branch: targetBranch,
|
|
1396
|
+
direction,
|
|
1397
|
+
dryRun: true,
|
|
1398
|
+
repos: {
|
|
1399
|
+
total: repos.length,
|
|
1400
|
+
eligible: eligibleRepos.map((r) => r.name),
|
|
1401
|
+
excluded: excludedRepos.map((r) => r.name)
|
|
1402
|
+
}
|
|
1403
|
+
}, null, 2));
|
|
1404
|
+
} else {
|
|
1405
|
+
console.log(chalk6__default.default.blue("Dry run - would sync:\n"));
|
|
1406
|
+
for (const repo of eligibleRepos) {
|
|
1407
|
+
console.log(chalk6__default.default.green(" \u2713"), repo.name);
|
|
1408
|
+
}
|
|
1409
|
+
if (excludedRepos.length > 0) {
|
|
1410
|
+
console.log("");
|
|
1411
|
+
console.log(chalk6__default.default.dim("Excluded:"));
|
|
1412
|
+
for (const repo of excludedRepos) {
|
|
1413
|
+
console.log(chalk6__default.default.dim(` - ${repo.name}`));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
console.log(chalk6__default.default.dim(`
|
|
1417
|
+
Total: ${eligibleRepos.length} repos would be synced`));
|
|
1418
|
+
console.log(chalk6__default.default.dim("Run without --dry-run to execute sync."));
|
|
1419
|
+
}
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
if (!options.json) {
|
|
1423
|
+
console.log(chalk6__default.default.blue("Syncing repositories...\n"));
|
|
1424
|
+
}
|
|
1425
|
+
const results = [];
|
|
1426
|
+
const syncOptions = {
|
|
1427
|
+
direction,
|
|
1428
|
+
dryRun: false,
|
|
1429
|
+
force: false
|
|
1430
|
+
};
|
|
1431
|
+
for (let i = 0; i < eligibleRepos.length; i += options.parallel) {
|
|
1432
|
+
const batch = eligibleRepos.slice(i, i + options.parallel);
|
|
1433
|
+
const batchResults = await Promise.all(
|
|
1434
|
+
batch.map((repo) => syncRepository(repo, config, direction, syncOptions))
|
|
1435
|
+
);
|
|
1436
|
+
for (const result of batchResults) {
|
|
1437
|
+
results.push(result);
|
|
1438
|
+
if (!options.json) {
|
|
1439
|
+
if (result.status === "success") {
|
|
1440
|
+
console.log(
|
|
1441
|
+
chalk6__default.default.green(" \u2713"),
|
|
1442
|
+
result.repo,
|
|
1443
|
+
chalk6__default.default.dim(`(${result.filesSynced} files in ${result.duration}ms)`)
|
|
1444
|
+
);
|
|
1445
|
+
} else if (result.status === "skipped") {
|
|
1446
|
+
console.log(
|
|
1447
|
+
chalk6__default.default.dim(" -"),
|
|
1448
|
+
result.repo,
|
|
1449
|
+
chalk6__default.default.dim("(no files to sync)")
|
|
1450
|
+
);
|
|
1451
|
+
} else if (result.status === "error") {
|
|
1452
|
+
console.log(
|
|
1453
|
+
chalk6__default.default.red(" \u2717"),
|
|
1454
|
+
result.repo,
|
|
1455
|
+
chalk6__default.default.red(`(${result.error})`)
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
const successful = results.filter((r) => r.status === "success");
|
|
1462
|
+
const skipped = results.filter((r) => r.status === "skipped");
|
|
1463
|
+
const failed = results.filter((r) => r.status === "error");
|
|
1464
|
+
const totalFiles = successful.reduce((sum, r) => sum + (r.filesSynced || 0), 0);
|
|
1465
|
+
if (options.json) {
|
|
1466
|
+
console.log(JSON.stringify({
|
|
1467
|
+
organization: org,
|
|
1468
|
+
environment: options.env,
|
|
1469
|
+
branch: targetBranch,
|
|
1470
|
+
direction,
|
|
1471
|
+
results: {
|
|
1472
|
+
total: results.length,
|
|
1473
|
+
successful: successful.length,
|
|
1474
|
+
skipped: skipped.length,
|
|
1475
|
+
failed: failed.length,
|
|
1476
|
+
filesSynced: totalFiles,
|
|
1477
|
+
details: results
|
|
1478
|
+
}
|
|
1479
|
+
}, null, 2));
|
|
1480
|
+
} else {
|
|
1481
|
+
console.log("");
|
|
1482
|
+
console.log(chalk6__default.default.green(`\u2713 Synced ${successful.length}/${results.length} repos (${totalFiles} files)`));
|
|
1483
|
+
if (skipped.length > 0) {
|
|
1484
|
+
console.log(chalk6__default.default.dim(` Skipped ${skipped.length} repos (no changes)`));
|
|
1485
|
+
}
|
|
1486
|
+
if (failed.length > 0) {
|
|
1487
|
+
console.log(chalk6__default.default.red(`\u2717 ${failed.length} repos failed`));
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
if (options.json) {
|
|
1492
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
1493
|
+
} else {
|
|
1494
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1495
|
+
}
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
return cmd;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// src/commands/sync/index.ts
|
|
1503
|
+
function syncCommand() {
|
|
1504
|
+
const cmd = new commander.Command("sync");
|
|
1505
|
+
cmd.description("Synchronize with codex repository");
|
|
1506
|
+
cmd.addCommand(syncProjectCommand());
|
|
1507
|
+
cmd.addCommand(syncOrgCommand());
|
|
1508
|
+
return cmd;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// src/commands/types/index.ts
|
|
1512
|
+
init_cjs_shims();
|
|
1513
|
+
|
|
1514
|
+
// src/commands/types/list.ts
|
|
1515
|
+
init_cjs_shims();
|
|
1516
|
+
function formatTtl(seconds) {
|
|
1517
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1518
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1519
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1520
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
1521
|
+
}
|
|
1522
|
+
function typesListCommand() {
|
|
1523
|
+
const cmd = new commander.Command("list");
|
|
1524
|
+
cmd.description("List all artifact types").option("--json", "Output as JSON").option("--custom-only", "Show only custom types").option("--builtin-only", "Show only built-in types").action(async (options) => {
|
|
1525
|
+
try {
|
|
1526
|
+
const client = await getClient();
|
|
1527
|
+
const registry = client.getTypeRegistry();
|
|
1528
|
+
const allTypes = registry.list();
|
|
1529
|
+
let types = allTypes;
|
|
1530
|
+
if (options.customOnly) {
|
|
1531
|
+
types = allTypes.filter((t) => !registry.isBuiltIn(t.name));
|
|
1532
|
+
} else if (options.builtinOnly) {
|
|
1533
|
+
types = allTypes.filter((t) => registry.isBuiltIn(t.name));
|
|
1534
|
+
}
|
|
1535
|
+
if (options.json) {
|
|
1536
|
+
const builtinCount = types.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
1537
|
+
const customCount = types.length - builtinCount;
|
|
1538
|
+
console.log(JSON.stringify({
|
|
1539
|
+
count: types.length,
|
|
1540
|
+
builtinCount,
|
|
1541
|
+
customCount,
|
|
1542
|
+
types: types.map((t) => ({
|
|
1543
|
+
name: t.name,
|
|
1544
|
+
description: t.description,
|
|
1545
|
+
patterns: t.patterns,
|
|
1546
|
+
defaultTtl: t.defaultTtl,
|
|
1547
|
+
ttl: formatTtl(t.defaultTtl),
|
|
1548
|
+
builtin: registry.isBuiltIn(t.name),
|
|
1549
|
+
archiveAfterDays: t.archiveAfterDays,
|
|
1550
|
+
archiveStorage: t.archiveStorage
|
|
1551
|
+
}))
|
|
1552
|
+
}, null, 2));
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
if (types.length === 0) {
|
|
1556
|
+
console.log(chalk6__default.default.yellow("No types found."));
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
console.log(chalk6__default.default.bold("Artifact Types\n"));
|
|
1560
|
+
const builtinTypes = types.filter((t) => registry.isBuiltIn(t.name));
|
|
1561
|
+
const customTypes = types.filter((t) => !registry.isBuiltIn(t.name));
|
|
1562
|
+
if (builtinTypes.length > 0 && !options.customOnly) {
|
|
1563
|
+
console.log(chalk6__default.default.bold("Built-in Types"));
|
|
1564
|
+
console.log(chalk6__default.default.dim("\u2500".repeat(70)));
|
|
1565
|
+
for (const type of builtinTypes) {
|
|
1566
|
+
const patternStr = type.patterns[0] || "";
|
|
1567
|
+
console.log(` ${chalk6__default.default.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6__default.default.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1568
|
+
console.log(` ${chalk6__default.default.dim(" ".repeat(12) + type.description)}`);
|
|
1569
|
+
}
|
|
1570
|
+
console.log("");
|
|
1571
|
+
}
|
|
1572
|
+
if (customTypes.length > 0 && !options.builtinOnly) {
|
|
1573
|
+
console.log(chalk6__default.default.bold("Custom Types"));
|
|
1574
|
+
console.log(chalk6__default.default.dim("\u2500".repeat(70)));
|
|
1575
|
+
for (const type of customTypes) {
|
|
1576
|
+
const patternStr = type.patterns[0] || "";
|
|
1577
|
+
console.log(` ${chalk6__default.default.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6__default.default.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
|
|
1578
|
+
console.log(` ${chalk6__default.default.dim(" ".repeat(12) + type.description)}`);
|
|
1579
|
+
}
|
|
1580
|
+
console.log("");
|
|
1581
|
+
}
|
|
1582
|
+
console.log(chalk6__default.default.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
|
|
1583
|
+
} catch (error) {
|
|
1584
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
return cmd;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// src/commands/types/show.ts
|
|
1592
|
+
init_cjs_shims();
|
|
1593
|
+
function formatTtl2(seconds) {
|
|
1594
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1595
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1596
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1597
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
1598
|
+
}
|
|
1599
|
+
function typesShowCommand() {
|
|
1600
|
+
const cmd = new commander.Command("show");
|
|
1601
|
+
cmd.description("Show details for a specific type").argument("<name>", "Type name").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1602
|
+
try {
|
|
1603
|
+
const client = await getClient();
|
|
1604
|
+
const registry = client.getTypeRegistry();
|
|
1605
|
+
const type = registry.get(name);
|
|
1606
|
+
if (!type) {
|
|
1607
|
+
console.error(chalk6__default.default.red("Error:"), `Type "${name}" not found.`);
|
|
1608
|
+
console.log(chalk6__default.default.dim('Run "fractary codex types list" to see available types.'));
|
|
1609
|
+
process.exit(1);
|
|
1610
|
+
}
|
|
1611
|
+
const isBuiltin = registry.isBuiltIn(name);
|
|
1612
|
+
if (options.json) {
|
|
1613
|
+
console.log(JSON.stringify({
|
|
1614
|
+
name: type.name,
|
|
1615
|
+
builtin: isBuiltin,
|
|
1616
|
+
description: type.description,
|
|
1617
|
+
patterns: type.patterns,
|
|
1618
|
+
defaultTtl: type.defaultTtl,
|
|
1619
|
+
ttl: formatTtl2(type.defaultTtl),
|
|
1620
|
+
archiveAfterDays: type.archiveAfterDays,
|
|
1621
|
+
archiveStorage: type.archiveStorage,
|
|
1622
|
+
syncPatterns: type.syncPatterns,
|
|
1623
|
+
excludePatterns: type.excludePatterns
|
|
1624
|
+
}, null, 2));
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
const nameColor = isBuiltin ? chalk6__default.default.cyan : chalk6__default.default.green;
|
|
1628
|
+
console.log(chalk6__default.default.bold(`Type: ${nameColor(name)}
|
|
1629
|
+
`));
|
|
1630
|
+
console.log(` ${chalk6__default.default.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
|
|
1631
|
+
console.log(` ${chalk6__default.default.dim("Description:")} ${type.description}`);
|
|
1632
|
+
console.log(` ${chalk6__default.default.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
|
|
1633
|
+
console.log("");
|
|
1634
|
+
console.log(chalk6__default.default.bold("Patterns"));
|
|
1635
|
+
for (const pattern of type.patterns) {
|
|
1636
|
+
console.log(` ${chalk6__default.default.dim("\u2022")} ${pattern}`);
|
|
1637
|
+
}
|
|
1638
|
+
if (type.archiveAfterDays !== null) {
|
|
1639
|
+
console.log("");
|
|
1640
|
+
console.log(chalk6__default.default.bold("Archive Settings"));
|
|
1641
|
+
console.log(` ${chalk6__default.default.dim("After:")} ${type.archiveAfterDays} days`);
|
|
1642
|
+
console.log(` ${chalk6__default.default.dim("Storage:")} ${type.archiveStorage || "not set"}`);
|
|
1643
|
+
}
|
|
1644
|
+
if (type.syncPatterns && type.syncPatterns.length > 0) {
|
|
1645
|
+
console.log("");
|
|
1646
|
+
console.log(chalk6__default.default.bold("Sync Patterns"));
|
|
1647
|
+
for (const pattern of type.syncPatterns) {
|
|
1648
|
+
console.log(` ${chalk6__default.default.dim("\u2022")} ${pattern}`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
if (type.excludePatterns && type.excludePatterns.length > 0) {
|
|
1652
|
+
console.log("");
|
|
1653
|
+
console.log(chalk6__default.default.bold("Exclude Patterns"));
|
|
1654
|
+
for (const pattern of type.excludePatterns) {
|
|
1655
|
+
console.log(` ${chalk6__default.default.dim("\u2022")} ${pattern}`);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
} catch (error) {
|
|
1659
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1660
|
+
process.exit(1);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
return cmd;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/commands/types/add.ts
|
|
1667
|
+
init_cjs_shims();
|
|
1668
|
+
init_migrate_config();
|
|
1669
|
+
function isValidTypeName(name) {
|
|
1670
|
+
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
1671
|
+
}
|
|
1672
|
+
function parseTtl(ttl) {
|
|
1673
|
+
const match = ttl.match(/^(\d+)([smhd])$/);
|
|
1674
|
+
if (!match) {
|
|
1675
|
+
throw new Error("Invalid TTL format");
|
|
1676
|
+
}
|
|
1677
|
+
const value = parseInt(match[1], 10);
|
|
1678
|
+
const unit = match[2];
|
|
1679
|
+
switch (unit) {
|
|
1680
|
+
case "s":
|
|
1681
|
+
return value;
|
|
1682
|
+
case "m":
|
|
1683
|
+
return value * 60;
|
|
1684
|
+
case "h":
|
|
1685
|
+
return value * 3600;
|
|
1686
|
+
case "d":
|
|
1687
|
+
return value * 86400;
|
|
1688
|
+
default:
|
|
1689
|
+
throw new Error("Unknown TTL unit");
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
function formatTtl3(seconds) {
|
|
1693
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1694
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
1695
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
1696
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
1697
|
+
}
|
|
1698
|
+
function typesAddCommand() {
|
|
1699
|
+
const cmd = new commander.Command("add");
|
|
1700
|
+
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) => {
|
|
1701
|
+
try {
|
|
1702
|
+
if (!isValidTypeName(name)) {
|
|
1703
|
+
console.error(chalk6__default.default.red("Error:"), "Invalid type name.");
|
|
1704
|
+
console.log(chalk6__default.default.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
|
|
1705
|
+
process.exit(1);
|
|
1706
|
+
}
|
|
1707
|
+
const client = await getClient();
|
|
1708
|
+
const registry = client.getTypeRegistry();
|
|
1709
|
+
if (registry.isBuiltIn(name)) {
|
|
1710
|
+
console.error(chalk6__default.default.red("Error:"), `Cannot override built-in type "${name}".`);
|
|
1711
|
+
const builtinNames = registry.list().filter((t) => registry.isBuiltIn(t.name)).map((t) => t.name);
|
|
1712
|
+
console.log(chalk6__default.default.dim("Built-in types: " + builtinNames.join(", ")));
|
|
1713
|
+
process.exit(1);
|
|
1714
|
+
}
|
|
1715
|
+
if (registry.has(name)) {
|
|
1716
|
+
console.error(chalk6__default.default.red("Error:"), `Custom type "${name}" already exists.`);
|
|
1717
|
+
console.log(chalk6__default.default.dim('Use "fractary codex types remove" first to remove it.'));
|
|
1718
|
+
process.exit(1);
|
|
1719
|
+
}
|
|
1720
|
+
let ttlSeconds;
|
|
1721
|
+
try {
|
|
1722
|
+
ttlSeconds = parseTtl(options.ttl);
|
|
1723
|
+
} catch {
|
|
1724
|
+
console.error(chalk6__default.default.red("Error:"), "Invalid TTL format.");
|
|
1725
|
+
console.log(chalk6__default.default.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
|
|
1726
|
+
console.log(chalk6__default.default.dim("Examples: 30m, 24h, 7d"));
|
|
1727
|
+
process.exit(1);
|
|
1728
|
+
}
|
|
1729
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1730
|
+
const config = await readYamlConfig(configPath);
|
|
1731
|
+
if (!config.types) {
|
|
1732
|
+
config.types = { custom: {} };
|
|
1733
|
+
}
|
|
1734
|
+
if (!config.types.custom) {
|
|
1735
|
+
config.types.custom = {};
|
|
1736
|
+
}
|
|
1737
|
+
config.types.custom[name] = {
|
|
1738
|
+
description: options.description || `Custom type: ${name}`,
|
|
1739
|
+
patterns: [options.pattern],
|
|
1740
|
+
defaultTtl: ttlSeconds
|
|
1741
|
+
};
|
|
1742
|
+
await writeYamlConfig(config, configPath);
|
|
1743
|
+
if (options.json) {
|
|
1744
|
+
console.log(JSON.stringify({
|
|
1745
|
+
success: true,
|
|
1746
|
+
type: {
|
|
1747
|
+
name,
|
|
1748
|
+
description: config.types.custom[name].description,
|
|
1749
|
+
patterns: config.types.custom[name].patterns,
|
|
1750
|
+
defaultTtl: ttlSeconds,
|
|
1751
|
+
ttl: formatTtl3(ttlSeconds),
|
|
1752
|
+
builtin: false
|
|
1753
|
+
},
|
|
1754
|
+
message: "Custom type added successfully. Changes will take effect on next CLI invocation."
|
|
1755
|
+
}, null, 2));
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
console.log(chalk6__default.default.green("\u2713"), `Added custom type "${chalk6__default.default.cyan(name)}"`);
|
|
1759
|
+
console.log("");
|
|
1760
|
+
console.log(` ${chalk6__default.default.dim("Pattern:")} ${options.pattern}`);
|
|
1761
|
+
console.log(` ${chalk6__default.default.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
|
|
1762
|
+
if (options.description) {
|
|
1763
|
+
console.log(` ${chalk6__default.default.dim("Description:")} ${options.description}`);
|
|
1764
|
+
}
|
|
1765
|
+
console.log("");
|
|
1766
|
+
console.log(chalk6__default.default.dim("Note: Custom type will be available on next CLI invocation."));
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1769
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
1770
|
+
console.log(chalk6__default.default.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
1771
|
+
}
|
|
1772
|
+
process.exit(1);
|
|
1773
|
+
}
|
|
1774
|
+
});
|
|
1775
|
+
return cmd;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/commands/types/remove.ts
|
|
1779
|
+
init_cjs_shims();
|
|
1780
|
+
init_migrate_config();
|
|
1781
|
+
function typesRemoveCommand() {
|
|
1782
|
+
const cmd = new commander.Command("remove");
|
|
1783
|
+
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) => {
|
|
1784
|
+
try {
|
|
1785
|
+
const client = await getClient();
|
|
1786
|
+
const registry = client.getTypeRegistry();
|
|
1787
|
+
if (registry.isBuiltIn(name)) {
|
|
1788
|
+
console.error(chalk6__default.default.red("Error:"), `Cannot remove built-in type "${name}".`);
|
|
1789
|
+
console.log(chalk6__default.default.dim("Built-in types are permanent and cannot be removed."));
|
|
1790
|
+
process.exit(1);
|
|
1791
|
+
}
|
|
1792
|
+
if (!registry.has(name)) {
|
|
1793
|
+
console.error(chalk6__default.default.red("Error:"), `Custom type "${name}" not found.`);
|
|
1794
|
+
console.log(chalk6__default.default.dim('Run "fractary codex types list --custom-only" to see custom types.'));
|
|
1795
|
+
process.exit(1);
|
|
1796
|
+
}
|
|
1797
|
+
const typeInfo = registry.get(name);
|
|
1798
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1799
|
+
const config = await readYamlConfig(configPath);
|
|
1800
|
+
if (!config.types?.custom?.[name]) {
|
|
1801
|
+
console.error(chalk6__default.default.red("Error:"), `Custom type "${name}" not found in configuration.`);
|
|
1802
|
+
process.exit(1);
|
|
1803
|
+
}
|
|
1804
|
+
delete config.types.custom[name];
|
|
1805
|
+
if (Object.keys(config.types.custom).length === 0) {
|
|
1806
|
+
delete config.types.custom;
|
|
1807
|
+
}
|
|
1808
|
+
if (config.types && Object.keys(config.types).length === 0) {
|
|
1809
|
+
delete config.types;
|
|
1810
|
+
}
|
|
1811
|
+
await writeYamlConfig(config, configPath);
|
|
1812
|
+
if (options.json) {
|
|
1813
|
+
console.log(JSON.stringify({
|
|
1814
|
+
success: true,
|
|
1815
|
+
removed: {
|
|
1816
|
+
name: typeInfo.name,
|
|
1817
|
+
description: typeInfo.description,
|
|
1818
|
+
patterns: typeInfo.patterns,
|
|
1819
|
+
defaultTtl: typeInfo.defaultTtl
|
|
1820
|
+
},
|
|
1821
|
+
message: "Custom type removed successfully. Changes will take effect on next CLI invocation."
|
|
1822
|
+
}, null, 2));
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
console.log(chalk6__default.default.green("\u2713"), `Removed custom type "${chalk6__default.default.cyan(name)}"`);
|
|
1826
|
+
console.log("");
|
|
1827
|
+
console.log(chalk6__default.default.dim("Removed configuration:"));
|
|
1828
|
+
console.log(` ${chalk6__default.default.dim("Pattern:")} ${typeInfo.patterns.join(", ")}`);
|
|
1829
|
+
console.log(` ${chalk6__default.default.dim("Description:")} ${typeInfo.description}`);
|
|
1830
|
+
console.log("");
|
|
1831
|
+
console.log(chalk6__default.default.dim("Note: Custom type will be removed on next CLI invocation."));
|
|
1832
|
+
} catch (error) {
|
|
1833
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
1834
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
1835
|
+
console.log(chalk6__default.default.dim('\nRun "fractary codex init" to create a configuration.'));
|
|
1836
|
+
}
|
|
1837
|
+
process.exit(1);
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1840
|
+
return cmd;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// src/commands/types/index.ts
|
|
1844
|
+
function typesCommand() {
|
|
1845
|
+
const cmd = new commander.Command("types");
|
|
1846
|
+
cmd.description("Manage artifact type registry");
|
|
1847
|
+
cmd.addCommand(typesListCommand());
|
|
1848
|
+
cmd.addCommand(typesShowCommand());
|
|
1849
|
+
cmd.addCommand(typesAddCommand());
|
|
1850
|
+
cmd.addCommand(typesRemoveCommand());
|
|
1851
|
+
return cmd;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// src/commands/health.ts
|
|
1855
|
+
init_cjs_shims();
|
|
1856
|
+
init_migrate_config();
|
|
1857
|
+
async function fileExists2(filePath) {
|
|
1858
|
+
try {
|
|
1859
|
+
await fs__namespace.access(filePath);
|
|
1860
|
+
return true;
|
|
1861
|
+
} catch {
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
async function checkConfiguration() {
|
|
1866
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1867
|
+
const legacyConfigPath = path2__namespace.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
1868
|
+
try {
|
|
1869
|
+
if (!await fileExists2(configPath)) {
|
|
1870
|
+
if (await fileExists2(legacyConfigPath)) {
|
|
1871
|
+
return {
|
|
1872
|
+
name: "Configuration",
|
|
1873
|
+
status: "warn",
|
|
1874
|
+
message: "Legacy JSON configuration detected",
|
|
1875
|
+
details: 'Run "fractary codex migrate" to upgrade to YAML format'
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
return {
|
|
1879
|
+
name: "Configuration",
|
|
1880
|
+
status: "fail",
|
|
1881
|
+
message: "No configuration found",
|
|
1882
|
+
details: 'Run "fractary codex init" to create configuration'
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
const config = await readYamlConfig(configPath);
|
|
1886
|
+
if (!config.organization) {
|
|
1887
|
+
return {
|
|
1888
|
+
name: "Configuration",
|
|
1889
|
+
status: "warn",
|
|
1890
|
+
message: "No organization configured",
|
|
1891
|
+
details: "Organization slug is required"
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
const providerCount = config.storage?.length || 0;
|
|
1895
|
+
if (providerCount === 0) {
|
|
1896
|
+
return {
|
|
1897
|
+
name: "Configuration",
|
|
1898
|
+
status: "warn",
|
|
1899
|
+
message: "No storage providers configured",
|
|
1900
|
+
details: "At least one storage provider is recommended"
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
return {
|
|
1904
|
+
name: "Configuration",
|
|
1905
|
+
status: "pass",
|
|
1906
|
+
message: "Valid YAML configuration",
|
|
1907
|
+
details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
|
|
1908
|
+
};
|
|
1909
|
+
} catch (error) {
|
|
1910
|
+
return {
|
|
1911
|
+
name: "Configuration",
|
|
1912
|
+
status: "fail",
|
|
1913
|
+
message: "Invalid configuration",
|
|
1914
|
+
details: error.message
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
async function checkSDKClient() {
|
|
1919
|
+
try {
|
|
1920
|
+
const client = await getClient();
|
|
1921
|
+
const organization = client.getOrganization();
|
|
1922
|
+
return {
|
|
1923
|
+
name: "SDK Client",
|
|
1924
|
+
status: "pass",
|
|
1925
|
+
message: "CodexClient initialized successfully",
|
|
1926
|
+
details: `Organization: ${organization}`
|
|
1927
|
+
};
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
return {
|
|
1930
|
+
name: "SDK Client",
|
|
1931
|
+
status: "fail",
|
|
1932
|
+
message: "Failed to initialize CodexClient",
|
|
1933
|
+
details: error.message
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
async function checkCache() {
|
|
1938
|
+
try {
|
|
1939
|
+
const client = await getClient();
|
|
1940
|
+
const stats = await client.getCacheStats();
|
|
1941
|
+
if (stats.entryCount === 0) {
|
|
1942
|
+
return {
|
|
1943
|
+
name: "Cache",
|
|
1944
|
+
status: "warn",
|
|
1945
|
+
message: "Cache is empty",
|
|
1946
|
+
details: "Fetch some documents to populate cache"
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
|
|
1950
|
+
if (healthPercent < 50) {
|
|
1951
|
+
return {
|
|
1952
|
+
name: "Cache",
|
|
1953
|
+
status: "warn",
|
|
1954
|
+
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1955
|
+
details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
return {
|
|
1959
|
+
name: "Cache",
|
|
1960
|
+
status: "pass",
|
|
1961
|
+
message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
|
|
1962
|
+
details: `${formatSize4(stats.totalSize)} total`
|
|
1963
|
+
};
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
return {
|
|
1966
|
+
name: "Cache",
|
|
1967
|
+
status: "fail",
|
|
1968
|
+
message: "Cache check failed",
|
|
1969
|
+
details: error.message
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
async function checkStorage() {
|
|
1974
|
+
const configPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
1975
|
+
try {
|
|
1976
|
+
const config = await readYamlConfig(configPath);
|
|
1977
|
+
const providers = config.storage || [];
|
|
1978
|
+
if (providers.length === 0) {
|
|
1979
|
+
return {
|
|
1980
|
+
name: "Storage",
|
|
1981
|
+
status: "warn",
|
|
1982
|
+
message: "No storage providers configured",
|
|
1983
|
+
details: "Configure at least one provider in .fractary/codex.yaml"
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
const providerTypes = providers.map((p) => p.type).join(", ");
|
|
1987
|
+
const hasGitHub = providers.some((p) => p.type === "github");
|
|
1988
|
+
if (hasGitHub && !process.env.GITHUB_TOKEN) {
|
|
1989
|
+
return {
|
|
1990
|
+
name: "Storage",
|
|
1991
|
+
status: "warn",
|
|
1992
|
+
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
1993
|
+
details: "GITHUB_TOKEN not set (required for GitHub provider)"
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
return {
|
|
1997
|
+
name: "Storage",
|
|
1998
|
+
status: "pass",
|
|
1999
|
+
message: `${providers.length} provider(s): ${providerTypes}`,
|
|
2000
|
+
details: "All configured providers available"
|
|
2001
|
+
};
|
|
2002
|
+
} catch (error) {
|
|
2003
|
+
return {
|
|
2004
|
+
name: "Storage",
|
|
2005
|
+
status: "fail",
|
|
2006
|
+
message: "Storage check failed",
|
|
2007
|
+
details: error.message
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
async function checkTypes() {
|
|
2012
|
+
try {
|
|
2013
|
+
const client = await getClient();
|
|
2014
|
+
const registry = client.getTypeRegistry();
|
|
2015
|
+
const allTypes = registry.list();
|
|
2016
|
+
const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
|
|
2017
|
+
const customCount = allTypes.length - builtinCount;
|
|
2018
|
+
return {
|
|
2019
|
+
name: "Type Registry",
|
|
2020
|
+
status: "pass",
|
|
2021
|
+
message: `${allTypes.length} types registered`,
|
|
2022
|
+
details: `${builtinCount} built-in, ${customCount} custom`
|
|
2023
|
+
};
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
return {
|
|
2026
|
+
name: "Type Registry",
|
|
2027
|
+
status: "fail",
|
|
2028
|
+
message: "Type registry check failed",
|
|
2029
|
+
details: error.message
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function formatSize4(bytes) {
|
|
2034
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2035
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2036
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2037
|
+
}
|
|
2038
|
+
function healthCommand() {
|
|
2039
|
+
const cmd = new commander.Command("health");
|
|
2040
|
+
cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
|
|
2041
|
+
try {
|
|
2042
|
+
const checks = [];
|
|
2043
|
+
checks.push(await checkConfiguration());
|
|
2044
|
+
checks.push(await checkSDKClient());
|
|
2045
|
+
checks.push(await checkCache());
|
|
2046
|
+
checks.push(await checkStorage());
|
|
2047
|
+
checks.push(await checkTypes());
|
|
2048
|
+
const passed = checks.filter((c) => c.status === "pass").length;
|
|
2049
|
+
const warned = checks.filter((c) => c.status === "warn").length;
|
|
2050
|
+
const failed = checks.filter((c) => c.status === "fail").length;
|
|
2051
|
+
if (options.json) {
|
|
2052
|
+
console.log(JSON.stringify({
|
|
2053
|
+
summary: {
|
|
2054
|
+
total: checks.length,
|
|
2055
|
+
passed,
|
|
2056
|
+
warned,
|
|
2057
|
+
failed,
|
|
2058
|
+
healthy: failed === 0
|
|
2059
|
+
},
|
|
2060
|
+
checks
|
|
2061
|
+
}, null, 2));
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
console.log(chalk6__default.default.bold("Codex Health Check\n"));
|
|
2065
|
+
for (const check of checks) {
|
|
2066
|
+
const icon = check.status === "pass" ? chalk6__default.default.green("\u2713") : check.status === "warn" ? chalk6__default.default.yellow("\u26A0") : chalk6__default.default.red("\u2717");
|
|
2067
|
+
const statusColor = check.status === "pass" ? chalk6__default.default.green : check.status === "warn" ? chalk6__default.default.yellow : chalk6__default.default.red;
|
|
2068
|
+
console.log(`${icon} ${chalk6__default.default.bold(check.name)}`);
|
|
2069
|
+
console.log(` ${statusColor(check.message)}`);
|
|
2070
|
+
if (check.details) {
|
|
2071
|
+
console.log(` ${chalk6__default.default.dim(check.details)}`);
|
|
2072
|
+
}
|
|
2073
|
+
console.log("");
|
|
2074
|
+
}
|
|
2075
|
+
console.log(chalk6__default.default.dim("\u2500".repeat(60)));
|
|
2076
|
+
const overallStatus = failed > 0 ? chalk6__default.default.red("UNHEALTHY") : warned > 0 ? chalk6__default.default.yellow("DEGRADED") : chalk6__default.default.green("HEALTHY");
|
|
2077
|
+
console.log(`Status: ${overallStatus}`);
|
|
2078
|
+
console.log(chalk6__default.default.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
|
|
2079
|
+
if (failed > 0 || warned > 0) {
|
|
2080
|
+
console.log("");
|
|
2081
|
+
console.log(chalk6__default.default.dim("Run checks individually for more details:"));
|
|
2082
|
+
console.log(chalk6__default.default.dim(" fractary codex cache stats"));
|
|
2083
|
+
console.log(chalk6__default.default.dim(" fractary codex types list"));
|
|
2084
|
+
}
|
|
2085
|
+
if (failed > 0) {
|
|
2086
|
+
process.exit(1);
|
|
2087
|
+
}
|
|
2088
|
+
} catch (error) {
|
|
2089
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
2090
|
+
process.exit(1);
|
|
2091
|
+
}
|
|
2092
|
+
});
|
|
2093
|
+
return cmd;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
// src/commands/migrate.ts
|
|
2097
|
+
init_cjs_shims();
|
|
2098
|
+
init_migrate_config();
|
|
2099
|
+
async function fileExists3(filePath) {
|
|
2100
|
+
try {
|
|
2101
|
+
await fs__namespace.access(filePath);
|
|
2102
|
+
return true;
|
|
2103
|
+
} catch {
|
|
2104
|
+
return false;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
async function readFileContent(filePath) {
|
|
2108
|
+
return fs__namespace.readFile(filePath, "utf-8");
|
|
2109
|
+
}
|
|
2110
|
+
function migrateCommand() {
|
|
2111
|
+
const cmd = new commander.Command("migrate");
|
|
2112
|
+
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) => {
|
|
2113
|
+
try {
|
|
2114
|
+
const legacyConfigPath = path2__namespace.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
2115
|
+
const newConfigPath = path2__namespace.join(process.cwd(), ".fractary", "codex.yaml");
|
|
2116
|
+
if (!await fileExists3(legacyConfigPath)) {
|
|
2117
|
+
if (options.json) {
|
|
2118
|
+
console.log(JSON.stringify({
|
|
2119
|
+
status: "no_config",
|
|
2120
|
+
message: "No legacy configuration file found",
|
|
2121
|
+
path: legacyConfigPath
|
|
2122
|
+
}));
|
|
2123
|
+
} else {
|
|
2124
|
+
console.log(chalk6__default.default.yellow("\u26A0 No legacy configuration file found."));
|
|
2125
|
+
console.log(chalk6__default.default.dim(` Expected: ${legacyConfigPath}`));
|
|
2126
|
+
console.log(chalk6__default.default.dim('\nRun "fractary codex init" to create a new v3.0 YAML configuration.'));
|
|
2127
|
+
}
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
if (await fileExists3(newConfigPath) && !options.dryRun) {
|
|
2131
|
+
if (options.json) {
|
|
2132
|
+
console.log(JSON.stringify({
|
|
2133
|
+
status: "already_migrated",
|
|
2134
|
+
message: "YAML configuration already exists",
|
|
2135
|
+
path: newConfigPath
|
|
2136
|
+
}));
|
|
2137
|
+
} else {
|
|
2138
|
+
console.log(chalk6__default.default.yellow("\u26A0 YAML configuration already exists."));
|
|
2139
|
+
console.log(chalk6__default.default.dim(` Path: ${newConfigPath}`));
|
|
2140
|
+
console.log(chalk6__default.default.dim('\nUse "fractary codex init --force" to recreate.'));
|
|
2141
|
+
}
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
const legacyContent = await readFileContent(legacyConfigPath);
|
|
2145
|
+
let legacyConfig;
|
|
2146
|
+
try {
|
|
2147
|
+
legacyConfig = JSON.parse(legacyContent);
|
|
2148
|
+
} catch {
|
|
2149
|
+
console.error(chalk6__default.default.red("Error:"), "Invalid JSON in legacy config file.");
|
|
2150
|
+
process.exit(1);
|
|
2151
|
+
}
|
|
2152
|
+
if (!options.json && !options.dryRun) {
|
|
2153
|
+
console.log(chalk6__default.default.blue("Migrating Codex configuration to v3.0 YAML format...\n"));
|
|
2154
|
+
}
|
|
2155
|
+
const migrationResult = await migrateConfig(
|
|
2156
|
+
legacyConfigPath,
|
|
2157
|
+
{
|
|
2158
|
+
createBackup: options.backup !== false,
|
|
2159
|
+
backupSuffix: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
|
|
2160
|
+
}
|
|
2161
|
+
);
|
|
2162
|
+
if (!options.json) {
|
|
2163
|
+
console.log(chalk6__default.default.bold("Legacy Configuration:"));
|
|
2164
|
+
console.log(chalk6__default.default.dim(` Path: ${legacyConfigPath}`));
|
|
2165
|
+
console.log(chalk6__default.default.dim(` Organization: ${legacyConfig.organization || legacyConfig.organizationSlug || "unknown"}`));
|
|
2166
|
+
console.log("");
|
|
2167
|
+
console.log(chalk6__default.default.bold("Migration Changes:"));
|
|
2168
|
+
console.log(chalk6__default.default.green(" + Format: JSON \u2192 YAML"));
|
|
2169
|
+
console.log(chalk6__default.default.green(" + Location: .fractary/plugins/codex/ \u2192 .fractary/"));
|
|
2170
|
+
console.log(chalk6__default.default.green(" + File: config.json \u2192 codex.yaml"));
|
|
2171
|
+
console.log(chalk6__default.default.green(" + Storage: Multi-provider configuration"));
|
|
2172
|
+
console.log(chalk6__default.default.green(" + Cache: Modern cache management"));
|
|
2173
|
+
console.log(chalk6__default.default.green(" + Types: Custom type registry"));
|
|
2174
|
+
if (migrationResult.warnings.length > 0) {
|
|
2175
|
+
console.log("");
|
|
2176
|
+
console.log(chalk6__default.default.yellow("Warnings:"));
|
|
2177
|
+
for (const warning of migrationResult.warnings) {
|
|
2178
|
+
console.log(chalk6__default.default.yellow(" \u26A0"), chalk6__default.default.dim(warning));
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
console.log("");
|
|
2182
|
+
if (options.dryRun) {
|
|
2183
|
+
console.log(chalk6__default.default.blue("Dry run - no changes made."));
|
|
2184
|
+
console.log(chalk6__default.default.dim("Run without --dry-run to execute migration."));
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
if (options.json) {
|
|
2189
|
+
const output = {
|
|
2190
|
+
status: options.dryRun ? "migration_ready" : "migrated",
|
|
2191
|
+
dryRun: options.dryRun || false,
|
|
2192
|
+
legacyConfig: {
|
|
2193
|
+
path: legacyConfigPath,
|
|
2194
|
+
organization: legacyConfig.organization || legacyConfig.organizationSlug
|
|
2195
|
+
},
|
|
2196
|
+
newConfig: {
|
|
2197
|
+
path: newConfigPath,
|
|
2198
|
+
organization: migrationResult.yamlConfig.organization
|
|
2199
|
+
},
|
|
2200
|
+
warnings: migrationResult.warnings,
|
|
2201
|
+
backupPath: migrationResult.backupPath
|
|
2202
|
+
};
|
|
2203
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2204
|
+
if (options.dryRun) {
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (!options.dryRun) {
|
|
2209
|
+
await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
|
|
2210
|
+
const cacheDir = path2__namespace.join(process.cwd(), ".codex-cache");
|
|
2211
|
+
await fs__namespace.mkdir(cacheDir, { recursive: true });
|
|
2212
|
+
if (!options.json) {
|
|
2213
|
+
console.log(chalk6__default.default.green("\u2713"), "YAML configuration created");
|
|
2214
|
+
console.log(chalk6__default.default.green("\u2713"), "Cache directory initialized");
|
|
2215
|
+
if (migrationResult.backupPath) {
|
|
2216
|
+
console.log(chalk6__default.default.green("\u2713"), "Legacy config backed up");
|
|
2217
|
+
}
|
|
2218
|
+
console.log("");
|
|
2219
|
+
console.log(chalk6__default.default.bold("New Configuration:"));
|
|
2220
|
+
console.log(chalk6__default.default.dim(` Path: ${newConfigPath}`));
|
|
2221
|
+
console.log(chalk6__default.default.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
|
|
2222
|
+
console.log(chalk6__default.default.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
|
|
2223
|
+
console.log(chalk6__default.default.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
|
|
2224
|
+
console.log("");
|
|
2225
|
+
console.log(chalk6__default.default.bold("Next Steps:"));
|
|
2226
|
+
console.log(chalk6__default.default.dim(" 1. Review the new configuration: .fractary/codex.yaml"));
|
|
2227
|
+
console.log(chalk6__default.default.dim(' 2. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
|
|
2228
|
+
console.log(chalk6__default.default.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
|
|
2229
|
+
if (migrationResult.backupPath) {
|
|
2230
|
+
console.log("");
|
|
2231
|
+
console.log(chalk6__default.default.dim(`Backup saved: ${path2__namespace.basename(migrationResult.backupPath)}`));
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
if (options.json) {
|
|
2237
|
+
console.log(JSON.stringify({
|
|
2238
|
+
status: "error",
|
|
2239
|
+
message: error.message
|
|
2240
|
+
}));
|
|
2241
|
+
} else {
|
|
2242
|
+
console.error(chalk6__default.default.red("Error:"), error.message);
|
|
2243
|
+
}
|
|
2244
|
+
process.exit(1);
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
return cmd;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// src/cli.ts
|
|
2251
|
+
function createCLI() {
|
|
2252
|
+
const program = new commander.Command("fractary-codex");
|
|
2253
|
+
program.description("Centralized knowledge management and distribution for AI agents").version("0.2.0");
|
|
2254
|
+
program.addCommand(initCommand());
|
|
2255
|
+
program.addCommand(fetchCommand());
|
|
2256
|
+
program.addCommand(cacheCommand());
|
|
2257
|
+
program.addCommand(syncCommand());
|
|
2258
|
+
program.addCommand(typesCommand());
|
|
2259
|
+
program.addCommand(healthCommand());
|
|
2260
|
+
program.addCommand(migrateCommand());
|
|
2261
|
+
return program;
|
|
2262
|
+
}
|
|
2263
|
+
async function main() {
|
|
2264
|
+
try {
|
|
2265
|
+
const program = createCLI();
|
|
2266
|
+
await program.parseAsync(process.argv);
|
|
2267
|
+
} catch (error) {
|
|
2268
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
2269
|
+
process.exit(1);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
main();
|
|
2273
|
+
//# sourceMappingURL=cli.cjs.map
|
|
2274
|
+
//# sourceMappingURL=cli.cjs.map
|