@fractary/codex-mcp 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +524 -16
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +153 -2
- package/dist/index.cjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -5586,6 +5586,7 @@ var {
|
|
|
5586
5586
|
// ../../sdk/js/dist/index.js
|
|
5587
5587
|
init_cjs_shims();
|
|
5588
5588
|
var import_micromatch = __toESM(require_micromatch(), 1);
|
|
5589
|
+
var path3 = __toESM(require("path"), 1);
|
|
5589
5590
|
var import_path = __toESM(require("path"), 1);
|
|
5590
5591
|
var import_child_process = require("child_process");
|
|
5591
5592
|
|
|
@@ -12237,6 +12238,7 @@ var safeLoadAll = renamed("safeLoadAll", "loadAll");
|
|
|
12237
12238
|
var safeDump = renamed("safeDump", "dump");
|
|
12238
12239
|
|
|
12239
12240
|
// ../../sdk/js/dist/index.js
|
|
12241
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
12240
12242
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
12241
12243
|
var import_util4 = require("util");
|
|
12242
12244
|
var __defProp2 = Object.defineProperty;
|
|
@@ -12488,9 +12490,45 @@ function resolveReference(uri, options = {}) {
|
|
|
12488
12490
|
};
|
|
12489
12491
|
if (isCurrentProject && parsed.path) {
|
|
12490
12492
|
resolved.localPath = parsed.path;
|
|
12493
|
+
if (options.config) {
|
|
12494
|
+
const fileSource = detectFilePluginSource(parsed.path, options.config);
|
|
12495
|
+
if (fileSource) {
|
|
12496
|
+
resolved.sourceType = "file-plugin";
|
|
12497
|
+
resolved.filePluginSource = fileSource.name;
|
|
12498
|
+
resolved.localPath = fileSource.fullPath;
|
|
12499
|
+
}
|
|
12500
|
+
}
|
|
12491
12501
|
}
|
|
12492
12502
|
return resolved;
|
|
12493
12503
|
}
|
|
12504
|
+
function detectFilePluginSource(filePath, config) {
|
|
12505
|
+
if (!config?.file?.sources) {
|
|
12506
|
+
return null;
|
|
12507
|
+
}
|
|
12508
|
+
for (const [sourceName, source] of Object.entries(config.file.sources)) {
|
|
12509
|
+
const basePath = source.local?.base_path;
|
|
12510
|
+
if (!basePath) {
|
|
12511
|
+
continue;
|
|
12512
|
+
}
|
|
12513
|
+
const normalizedPath = filePath.replace(/^\.\//, "").replace(/^\//, "");
|
|
12514
|
+
const normalizedBasePath = basePath.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
|
|
12515
|
+
if (normalizedPath.startsWith(normalizedBasePath)) {
|
|
12516
|
+
return {
|
|
12517
|
+
name: sourceName,
|
|
12518
|
+
fullPath: import_path.default.join(basePath, normalizedPath.substring(normalizedBasePath.length).replace(/^\//, ""))
|
|
12519
|
+
};
|
|
12520
|
+
}
|
|
12521
|
+
const sourceNameInPath = normalizedBasePath.split("/").pop();
|
|
12522
|
+
if (sourceNameInPath && normalizedPath.startsWith(sourceNameInPath + "/")) {
|
|
12523
|
+
const pathWithoutSource = normalizedPath.substring(sourceNameInPath.length + 1);
|
|
12524
|
+
return {
|
|
12525
|
+
name: sourceName,
|
|
12526
|
+
fullPath: import_path.default.join(basePath, pathWithoutSource)
|
|
12527
|
+
};
|
|
12528
|
+
}
|
|
12529
|
+
}
|
|
12530
|
+
return null;
|
|
12531
|
+
}
|
|
12494
12532
|
var TTL = {
|
|
12495
12533
|
ONE_HOUR: 3600,
|
|
12496
12534
|
ONE_DAY: 86400,
|
|
@@ -12649,8 +12687,40 @@ var ArchiveProjectConfigSchema = external_exports.object({
|
|
|
12649
12687
|
var ArchiveConfigSchema = external_exports.object({
|
|
12650
12688
|
projects: external_exports.record(ArchiveProjectConfigSchema)
|
|
12651
12689
|
});
|
|
12690
|
+
var GitHubAuthConfigSchema = external_exports.object({
|
|
12691
|
+
/** Default token environment variable name (default: GITHUB_TOKEN) */
|
|
12692
|
+
default_token_env: external_exports.string().optional(),
|
|
12693
|
+
/** Fallback to public access if authentication fails */
|
|
12694
|
+
fallback_to_public: external_exports.boolean().optional()
|
|
12695
|
+
});
|
|
12696
|
+
var AuthConfigSchema = external_exports.object({
|
|
12697
|
+
/** GitHub authentication configuration */
|
|
12698
|
+
github: GitHubAuthConfigSchema.optional()
|
|
12699
|
+
});
|
|
12700
|
+
var SourceConfigSchema = external_exports.object({
|
|
12701
|
+
/** Source type */
|
|
12702
|
+
type: external_exports.enum(["github", "s3", "http", "local"]),
|
|
12703
|
+
/** Environment variable containing the authentication token */
|
|
12704
|
+
token_env: external_exports.string().optional(),
|
|
12705
|
+
/** Direct token value (not recommended, use token_env instead) */
|
|
12706
|
+
token: external_exports.string().optional(),
|
|
12707
|
+
/** Branch to fetch from (for GitHub sources) */
|
|
12708
|
+
branch: external_exports.string().optional(),
|
|
12709
|
+
/** Base URL (for HTTP sources) */
|
|
12710
|
+
base_url: external_exports.string().optional(),
|
|
12711
|
+
/** Bucket name (for S3 sources) */
|
|
12712
|
+
bucket: external_exports.string().optional(),
|
|
12713
|
+
/** Prefix/path within bucket (for S3 sources) */
|
|
12714
|
+
prefix: external_exports.string().optional()
|
|
12715
|
+
});
|
|
12716
|
+
var DependencyConfigSchema = external_exports.object({
|
|
12717
|
+
/** Sources within this dependency */
|
|
12718
|
+
sources: external_exports.record(SourceConfigSchema)
|
|
12719
|
+
});
|
|
12652
12720
|
var CodexConfigSchema = external_exports.object({
|
|
12653
12721
|
organizationSlug: external_exports.string(),
|
|
12722
|
+
/** Project name (optional) */
|
|
12723
|
+
project: external_exports.string().optional(),
|
|
12654
12724
|
directories: external_exports.object({
|
|
12655
12725
|
source: external_exports.string().optional(),
|
|
12656
12726
|
target: external_exports.string().optional(),
|
|
@@ -12660,8 +12730,47 @@ var CodexConfigSchema = external_exports.object({
|
|
|
12660
12730
|
// Directional sync configuration
|
|
12661
12731
|
sync: DirectionalSyncSchema.optional(),
|
|
12662
12732
|
// Archive configuration
|
|
12663
|
-
archive: ArchiveConfigSchema.optional()
|
|
12733
|
+
archive: ArchiveConfigSchema.optional(),
|
|
12734
|
+
// Authentication configuration
|
|
12735
|
+
auth: AuthConfigSchema.optional(),
|
|
12736
|
+
// Dependencies configuration (external projects)
|
|
12737
|
+
dependencies: external_exports.record(DependencyConfigSchema).optional()
|
|
12664
12738
|
}).strict();
|
|
12739
|
+
var FileSourceSchema = external_exports.object({
|
|
12740
|
+
type: external_exports.enum(["s3", "r2", "gcs", "local"]),
|
|
12741
|
+
bucket: external_exports.string().optional(),
|
|
12742
|
+
prefix: external_exports.string().optional(),
|
|
12743
|
+
region: external_exports.string().optional(),
|
|
12744
|
+
local: external_exports.object({
|
|
12745
|
+
base_path: external_exports.string()
|
|
12746
|
+
}),
|
|
12747
|
+
push: external_exports.object({
|
|
12748
|
+
compress: external_exports.boolean().optional(),
|
|
12749
|
+
keep_local: external_exports.boolean().optional()
|
|
12750
|
+
}).optional(),
|
|
12751
|
+
auth: external_exports.object({
|
|
12752
|
+
profile: external_exports.string().optional()
|
|
12753
|
+
}).optional()
|
|
12754
|
+
}).refine(
|
|
12755
|
+
(data) => {
|
|
12756
|
+
if (data.type !== "local" && !data.bucket) {
|
|
12757
|
+
return false;
|
|
12758
|
+
}
|
|
12759
|
+
return true;
|
|
12760
|
+
},
|
|
12761
|
+
{
|
|
12762
|
+
message: "Bucket is required for s3, r2, and gcs storage types",
|
|
12763
|
+
path: ["bucket"]
|
|
12764
|
+
}
|
|
12765
|
+
);
|
|
12766
|
+
var FileConfigSchema = external_exports.object({
|
|
12767
|
+
schema_version: external_exports.string(),
|
|
12768
|
+
sources: external_exports.record(FileSourceSchema)
|
|
12769
|
+
});
|
|
12770
|
+
external_exports.object({
|
|
12771
|
+
file: FileConfigSchema.optional(),
|
|
12772
|
+
codex: CodexConfigSchema.optional()
|
|
12773
|
+
});
|
|
12665
12774
|
init_matcher();
|
|
12666
12775
|
init_matcher();
|
|
12667
12776
|
var DEFAULT_FETCH_OPTIONS = {
|
|
@@ -12680,8 +12789,8 @@ function mergeFetchOptions(options) {
|
|
|
12680
12789
|
...options
|
|
12681
12790
|
};
|
|
12682
12791
|
}
|
|
12683
|
-
function detectContentType(
|
|
12684
|
-
const ext =
|
|
12792
|
+
function detectContentType(path7) {
|
|
12793
|
+
const ext = path7.split(".").pop()?.toLowerCase();
|
|
12685
12794
|
const mimeTypes = {
|
|
12686
12795
|
md: "text/markdown",
|
|
12687
12796
|
markdown: "text/markdown",
|
|
@@ -13146,6 +13255,294 @@ var HttpStorage = class {
|
|
|
13146
13255
|
}
|
|
13147
13256
|
}
|
|
13148
13257
|
};
|
|
13258
|
+
var FileSourceResolver = class {
|
|
13259
|
+
constructor(config) {
|
|
13260
|
+
this.config = config;
|
|
13261
|
+
this.initializeSources();
|
|
13262
|
+
}
|
|
13263
|
+
sources = /* @__PURE__ */ new Map();
|
|
13264
|
+
/**
|
|
13265
|
+
* Initialize sources from config
|
|
13266
|
+
*/
|
|
13267
|
+
initializeSources() {
|
|
13268
|
+
if (!this.config.file?.sources) {
|
|
13269
|
+
return;
|
|
13270
|
+
}
|
|
13271
|
+
for (const [name, sourceConfig] of Object.entries(this.config.file.sources)) {
|
|
13272
|
+
const resolved = {
|
|
13273
|
+
name,
|
|
13274
|
+
type: "file-plugin",
|
|
13275
|
+
localPath: sourceConfig.local.base_path,
|
|
13276
|
+
isCurrentProject: true,
|
|
13277
|
+
config: sourceConfig
|
|
13278
|
+
};
|
|
13279
|
+
if (sourceConfig.bucket && sourceConfig.type !== "local") {
|
|
13280
|
+
resolved.bucket = sourceConfig.bucket;
|
|
13281
|
+
resolved.prefix = sourceConfig.prefix;
|
|
13282
|
+
resolved.remotePath = this.buildRemotePath(sourceConfig);
|
|
13283
|
+
}
|
|
13284
|
+
this.sources.set(name, resolved);
|
|
13285
|
+
}
|
|
13286
|
+
}
|
|
13287
|
+
/**
|
|
13288
|
+
* Build remote path from source config
|
|
13289
|
+
*/
|
|
13290
|
+
buildRemotePath(source) {
|
|
13291
|
+
const protocol = source.type === "s3" ? "s3://" : source.type === "r2" ? "r2://" : "gcs://";
|
|
13292
|
+
const bucket = source.bucket;
|
|
13293
|
+
const prefix = source.prefix ? `/${source.prefix}` : "";
|
|
13294
|
+
return `${protocol}${bucket}${prefix}`;
|
|
13295
|
+
}
|
|
13296
|
+
/**
|
|
13297
|
+
* Get all available file plugin sources
|
|
13298
|
+
*
|
|
13299
|
+
* @returns Array of resolved file sources
|
|
13300
|
+
*/
|
|
13301
|
+
getAvailableSources() {
|
|
13302
|
+
return Array.from(this.sources.values());
|
|
13303
|
+
}
|
|
13304
|
+
/**
|
|
13305
|
+
* Resolve a source by name
|
|
13306
|
+
*
|
|
13307
|
+
* @param name - Source name (e.g., "specs", "logs")
|
|
13308
|
+
* @returns Resolved source or null if not found
|
|
13309
|
+
*/
|
|
13310
|
+
resolveSource(name) {
|
|
13311
|
+
return this.sources.get(name) || null;
|
|
13312
|
+
}
|
|
13313
|
+
/**
|
|
13314
|
+
* Check if a path belongs to any file plugin source
|
|
13315
|
+
*
|
|
13316
|
+
* @param path - File path to check
|
|
13317
|
+
* @returns True if path matches any source's base_path
|
|
13318
|
+
*/
|
|
13319
|
+
isFilePluginPath(path7) {
|
|
13320
|
+
return this.getSourceForPath(path7) !== null;
|
|
13321
|
+
}
|
|
13322
|
+
/**
|
|
13323
|
+
* Get the source for a given path
|
|
13324
|
+
*
|
|
13325
|
+
* Matches path against all source base_paths and returns the matching source.
|
|
13326
|
+
* Uses longest-match strategy if multiple sources match.
|
|
13327
|
+
*
|
|
13328
|
+
* @param path - File path to match
|
|
13329
|
+
* @returns Matching source or null
|
|
13330
|
+
*/
|
|
13331
|
+
getSourceForPath(path7) {
|
|
13332
|
+
let bestMatch = null;
|
|
13333
|
+
let bestMatchLength = 0;
|
|
13334
|
+
for (const source of this.sources.values()) {
|
|
13335
|
+
const normalizedPath = this.normalizePath(path7);
|
|
13336
|
+
const normalizedBasePath = this.normalizePath(source.localPath);
|
|
13337
|
+
if (normalizedPath.startsWith(normalizedBasePath)) {
|
|
13338
|
+
const matchLength = normalizedBasePath.length;
|
|
13339
|
+
if (matchLength > bestMatchLength) {
|
|
13340
|
+
bestMatch = source;
|
|
13341
|
+
bestMatchLength = matchLength;
|
|
13342
|
+
}
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13345
|
+
return bestMatch;
|
|
13346
|
+
}
|
|
13347
|
+
/**
|
|
13348
|
+
* Normalize path for comparison
|
|
13349
|
+
* - Remove leading "./" or "/"
|
|
13350
|
+
* - Remove trailing "/"
|
|
13351
|
+
* - Convert to lowercase for case-insensitive comparison
|
|
13352
|
+
*/
|
|
13353
|
+
normalizePath(path7) {
|
|
13354
|
+
return path7.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "").toLowerCase();
|
|
13355
|
+
}
|
|
13356
|
+
/**
|
|
13357
|
+
* Get source names
|
|
13358
|
+
*
|
|
13359
|
+
* @returns Array of source names
|
|
13360
|
+
*/
|
|
13361
|
+
getSourceNames() {
|
|
13362
|
+
return Array.from(this.sources.keys());
|
|
13363
|
+
}
|
|
13364
|
+
/**
|
|
13365
|
+
* Check if sources are configured
|
|
13366
|
+
*
|
|
13367
|
+
* @returns True if any file plugin sources are configured
|
|
13368
|
+
*/
|
|
13369
|
+
hasSources() {
|
|
13370
|
+
return this.sources.size > 0;
|
|
13371
|
+
}
|
|
13372
|
+
};
|
|
13373
|
+
var FilePluginFileNotFoundError = class _FilePluginFileNotFoundError extends Error {
|
|
13374
|
+
constructor(filePath, sourceName, options) {
|
|
13375
|
+
const includeCloudSuggestions = options?.includeCloudSuggestions !== false;
|
|
13376
|
+
const storageType = options?.storageType;
|
|
13377
|
+
let message = `File not found: ${filePath}
|
|
13378
|
+
|
|
13379
|
+
`;
|
|
13380
|
+
if (includeCloudSuggestions) {
|
|
13381
|
+
if (storageType) {
|
|
13382
|
+
message += `This file may be in cloud storage (${storageType}).
|
|
13383
|
+
|
|
13384
|
+
`;
|
|
13385
|
+
} else {
|
|
13386
|
+
message += `This file may not have been synced from remote storage yet.
|
|
13387
|
+
`;
|
|
13388
|
+
}
|
|
13389
|
+
message += `To fetch from cloud storage, run:
|
|
13390
|
+
`;
|
|
13391
|
+
message += ` file pull ${sourceName}
|
|
13392
|
+
|
|
13393
|
+
`;
|
|
13394
|
+
message += `Or sync all sources:
|
|
13395
|
+
`;
|
|
13396
|
+
message += ` file sync`;
|
|
13397
|
+
} else {
|
|
13398
|
+
message += `Please ensure the file exists locally or pull it from cloud storage.`;
|
|
13399
|
+
}
|
|
13400
|
+
super(message);
|
|
13401
|
+
this.filePath = filePath;
|
|
13402
|
+
this.sourceName = sourceName;
|
|
13403
|
+
this.name = "FilePluginFileNotFoundError";
|
|
13404
|
+
if (Error.captureStackTrace) {
|
|
13405
|
+
Error.captureStackTrace(this, _FilePluginFileNotFoundError);
|
|
13406
|
+
}
|
|
13407
|
+
}
|
|
13408
|
+
};
|
|
13409
|
+
var FilePluginStorage = class {
|
|
13410
|
+
constructor(options) {
|
|
13411
|
+
this.options = options;
|
|
13412
|
+
this.sourceResolver = new FileSourceResolver(options.config);
|
|
13413
|
+
this.baseDir = options.baseDir || process.cwd();
|
|
13414
|
+
}
|
|
13415
|
+
name = "file-plugin";
|
|
13416
|
+
type = "local";
|
|
13417
|
+
// Reuse local type for compatibility
|
|
13418
|
+
sourceResolver;
|
|
13419
|
+
baseDir;
|
|
13420
|
+
/**
|
|
13421
|
+
* Check if this provider can handle the reference
|
|
13422
|
+
*
|
|
13423
|
+
* Only handles:
|
|
13424
|
+
* - Current project references
|
|
13425
|
+
* - With sourceType === 'file-plugin'
|
|
13426
|
+
*
|
|
13427
|
+
* @param reference - Resolved reference
|
|
13428
|
+
* @returns True if this provider can handle the reference
|
|
13429
|
+
*/
|
|
13430
|
+
canHandle(reference) {
|
|
13431
|
+
return reference.isCurrentProject && reference.sourceType === "file-plugin";
|
|
13432
|
+
}
|
|
13433
|
+
/**
|
|
13434
|
+
* Fetch content for a reference
|
|
13435
|
+
*
|
|
13436
|
+
* Reads from local filesystem based on the resolved local path.
|
|
13437
|
+
* If file not found and S3 fallback is enabled, throws helpful error.
|
|
13438
|
+
*
|
|
13439
|
+
* @param reference - Resolved reference
|
|
13440
|
+
* @param options - Fetch options (unused for local reads)
|
|
13441
|
+
* @returns Fetch result with content
|
|
13442
|
+
*/
|
|
13443
|
+
async fetch(reference, _options) {
|
|
13444
|
+
if (!reference.localPath) {
|
|
13445
|
+
throw new Error(`File plugin reference missing localPath: ${reference.uri}`);
|
|
13446
|
+
}
|
|
13447
|
+
if (!reference.filePluginSource) {
|
|
13448
|
+
throw new Error(`File plugin reference missing source name: ${reference.uri}`);
|
|
13449
|
+
}
|
|
13450
|
+
const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
|
|
13451
|
+
const source = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
13452
|
+
if (source) {
|
|
13453
|
+
const allowedDir = path3.resolve(this.baseDir, source.localPath);
|
|
13454
|
+
if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
|
|
13455
|
+
throw new Error(
|
|
13456
|
+
`Path traversal detected: ${reference.localPath} resolves outside allowed directory ${source.localPath}`
|
|
13457
|
+
);
|
|
13458
|
+
}
|
|
13459
|
+
}
|
|
13460
|
+
try {
|
|
13461
|
+
const content = await fs3.readFile(absolutePath);
|
|
13462
|
+
const contentType = this.detectContentType(absolutePath);
|
|
13463
|
+
return {
|
|
13464
|
+
content,
|
|
13465
|
+
contentType,
|
|
13466
|
+
size: content.length,
|
|
13467
|
+
source: "file-plugin",
|
|
13468
|
+
metadata: {
|
|
13469
|
+
filePluginSource: reference.filePluginSource,
|
|
13470
|
+
localPath: absolutePath
|
|
13471
|
+
}
|
|
13472
|
+
};
|
|
13473
|
+
} catch (error) {
|
|
13474
|
+
if (error.code === "ENOENT") {
|
|
13475
|
+
const source2 = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
13476
|
+
throw this.createFileNotFoundError(reference, source2);
|
|
13477
|
+
}
|
|
13478
|
+
throw error;
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
/**
|
|
13482
|
+
* Check if a reference exists
|
|
13483
|
+
*
|
|
13484
|
+
* @param reference - Resolved reference
|
|
13485
|
+
* @param options - Fetch options (unused)
|
|
13486
|
+
* @returns True if file exists
|
|
13487
|
+
*/
|
|
13488
|
+
async exists(reference, _options) {
|
|
13489
|
+
if (!reference.localPath) {
|
|
13490
|
+
return false;
|
|
13491
|
+
}
|
|
13492
|
+
const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
|
|
13493
|
+
if (reference.filePluginSource) {
|
|
13494
|
+
const source = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
13495
|
+
if (source) {
|
|
13496
|
+
const allowedDir = path3.resolve(this.baseDir, source.localPath);
|
|
13497
|
+
if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
|
|
13498
|
+
return false;
|
|
13499
|
+
}
|
|
13500
|
+
}
|
|
13501
|
+
}
|
|
13502
|
+
try {
|
|
13503
|
+
await fs3.access(absolutePath);
|
|
13504
|
+
return true;
|
|
13505
|
+
} catch {
|
|
13506
|
+
return false;
|
|
13507
|
+
}
|
|
13508
|
+
}
|
|
13509
|
+
/**
|
|
13510
|
+
* Detect content type from file extension
|
|
13511
|
+
*/
|
|
13512
|
+
detectContentType(filePath) {
|
|
13513
|
+
const ext = path3.extname(filePath).toLowerCase();
|
|
13514
|
+
const mimeTypes = {
|
|
13515
|
+
".md": "text/markdown",
|
|
13516
|
+
".txt": "text/plain",
|
|
13517
|
+
".json": "application/json",
|
|
13518
|
+
".yaml": "text/yaml",
|
|
13519
|
+
".yml": "text/yaml",
|
|
13520
|
+
".html": "text/html",
|
|
13521
|
+
".xml": "application/xml",
|
|
13522
|
+
".log": "text/plain",
|
|
13523
|
+
".js": "application/javascript",
|
|
13524
|
+
".ts": "application/typescript",
|
|
13525
|
+
".py": "text/x-python",
|
|
13526
|
+
".sh": "application/x-sh"
|
|
13527
|
+
};
|
|
13528
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
13529
|
+
}
|
|
13530
|
+
/**
|
|
13531
|
+
* Create a helpful error message when file is not found
|
|
13532
|
+
*/
|
|
13533
|
+
createFileNotFoundError(reference, source) {
|
|
13534
|
+
const includeCloudSuggestions = this.options.enableS3Fallback !== false;
|
|
13535
|
+
const storageType = source?.config.type;
|
|
13536
|
+
return new FilePluginFileNotFoundError(
|
|
13537
|
+
reference.localPath || reference.path || "",
|
|
13538
|
+
reference.filePluginSource || "",
|
|
13539
|
+
{
|
|
13540
|
+
includeCloudSuggestions,
|
|
13541
|
+
storageType
|
|
13542
|
+
}
|
|
13543
|
+
);
|
|
13544
|
+
}
|
|
13545
|
+
};
|
|
13149
13546
|
var execFileAsync = (0, import_util4.promisify)(import_child_process.execFile);
|
|
13150
13547
|
async function execFileNoThrow(command, args = [], options) {
|
|
13151
13548
|
try {
|
|
@@ -13293,10 +13690,10 @@ var S3ArchiveStorage = class {
|
|
|
13293
13690
|
*
|
|
13294
13691
|
* Used to organize archives by type
|
|
13295
13692
|
*/
|
|
13296
|
-
detectType(
|
|
13297
|
-
if (
|
|
13298
|
-
if (
|
|
13299
|
-
if (
|
|
13693
|
+
detectType(path7) {
|
|
13694
|
+
if (path7.startsWith("specs/")) return "specs";
|
|
13695
|
+
if (path7.startsWith("docs/")) return "docs";
|
|
13696
|
+
if (path7.includes("/logs/")) return "logs";
|
|
13300
13697
|
return "misc";
|
|
13301
13698
|
}
|
|
13302
13699
|
/**
|
|
@@ -13307,9 +13704,9 @@ var S3ArchiveStorage = class {
|
|
|
13307
13704
|
* - *.md (all markdown files)
|
|
13308
13705
|
* - docs/*.md (markdown files in docs/)
|
|
13309
13706
|
*/
|
|
13310
|
-
matchesPatterns(
|
|
13707
|
+
matchesPatterns(path7, patterns) {
|
|
13311
13708
|
for (const pattern of patterns) {
|
|
13312
|
-
if (this.matchesPattern(
|
|
13709
|
+
if (this.matchesPattern(path7, pattern)) {
|
|
13313
13710
|
return true;
|
|
13314
13711
|
}
|
|
13315
13712
|
}
|
|
@@ -13318,27 +13715,74 @@ var S3ArchiveStorage = class {
|
|
|
13318
13715
|
/**
|
|
13319
13716
|
* Check if path matches a single pattern
|
|
13320
13717
|
*/
|
|
13321
|
-
matchesPattern(
|
|
13718
|
+
matchesPattern(path7, pattern) {
|
|
13322
13719
|
const DOUBLE_STAR = "\0DOUBLE_STAR\0";
|
|
13323
13720
|
let regexPattern = pattern.replace(/\*\*/g, DOUBLE_STAR);
|
|
13324
13721
|
regexPattern = regexPattern.replace(/[.[\](){}+^$|\\]/g, "\\$&");
|
|
13325
13722
|
regexPattern = regexPattern.replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
|
|
13326
13723
|
regexPattern = regexPattern.replace(new RegExp(DOUBLE_STAR, "g"), ".*");
|
|
13327
13724
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
13328
|
-
return regex.test(
|
|
13725
|
+
return regex.test(path7);
|
|
13329
13726
|
}
|
|
13330
13727
|
};
|
|
13331
13728
|
var StorageManager = class {
|
|
13332
13729
|
providers = /* @__PURE__ */ new Map();
|
|
13333
13730
|
priority;
|
|
13731
|
+
codexConfig;
|
|
13334
13732
|
constructor(config = {}) {
|
|
13733
|
+
this.codexConfig = config.codexConfig;
|
|
13335
13734
|
this.providers.set("local", new LocalStorage(config.local));
|
|
13336
13735
|
this.providers.set("github", new GitHubStorage(config.github));
|
|
13337
13736
|
this.providers.set("http", new HttpStorage(config.http));
|
|
13338
13737
|
if (config.s3Archive) {
|
|
13339
13738
|
this.providers.set("s3-archive", new S3ArchiveStorage(config.s3Archive));
|
|
13340
13739
|
}
|
|
13341
|
-
|
|
13740
|
+
if (config.filePlugin) {
|
|
13741
|
+
this.providers.set("file-plugin", new FilePluginStorage(config.filePlugin));
|
|
13742
|
+
}
|
|
13743
|
+
this.priority = config.priority || (config.filePlugin && config.s3Archive ? ["file-plugin", "local", "s3-archive", "github", "http"] : config.filePlugin ? ["file-plugin", "local", "github", "http"] : config.s3Archive ? ["local", "s3-archive", "github", "http"] : ["local", "github", "http"]);
|
|
13744
|
+
}
|
|
13745
|
+
/**
|
|
13746
|
+
* Resolve authentication token for a reference
|
|
13747
|
+
*
|
|
13748
|
+
* Looks up dependency-specific authentication or falls back to default
|
|
13749
|
+
*/
|
|
13750
|
+
resolveToken(reference) {
|
|
13751
|
+
if (!this.codexConfig) {
|
|
13752
|
+
return void 0;
|
|
13753
|
+
}
|
|
13754
|
+
const dependencyKey = `${reference.org}/${reference.project}`;
|
|
13755
|
+
if (this.codexConfig.dependencies?.[dependencyKey]) {
|
|
13756
|
+
const dependency = this.codexConfig.dependencies[dependencyKey];
|
|
13757
|
+
for (const [, sourceConfig] of Object.entries(dependency.sources)) {
|
|
13758
|
+
if (sourceConfig.type === "github") {
|
|
13759
|
+
if (sourceConfig.token_env) {
|
|
13760
|
+
const token = process.env[sourceConfig.token_env];
|
|
13761
|
+
if (token) {
|
|
13762
|
+
return token;
|
|
13763
|
+
}
|
|
13764
|
+
}
|
|
13765
|
+
if (sourceConfig.token) {
|
|
13766
|
+
return sourceConfig.token;
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
}
|
|
13770
|
+
}
|
|
13771
|
+
const defaultTokenEnv = this.codexConfig.auth?.github?.default_token_env || "GITHUB_TOKEN";
|
|
13772
|
+
return process.env[defaultTokenEnv];
|
|
13773
|
+
}
|
|
13774
|
+
/**
|
|
13775
|
+
* Resolve fetch options with authentication
|
|
13776
|
+
*
|
|
13777
|
+
* Merges reference-specific authentication with provided options
|
|
13778
|
+
*/
|
|
13779
|
+
resolveFetchOptions(reference, options) {
|
|
13780
|
+
const token = this.resolveToken(reference);
|
|
13781
|
+
return {
|
|
13782
|
+
...options,
|
|
13783
|
+
token: options?.token || token
|
|
13784
|
+
// Explicit option overrides resolved token
|
|
13785
|
+
};
|
|
13342
13786
|
}
|
|
13343
13787
|
/**
|
|
13344
13788
|
* Register a custom storage provider
|
|
@@ -13380,8 +13824,10 @@ var StorageManager = class {
|
|
|
13380
13824
|
* Fetch content for a reference
|
|
13381
13825
|
*
|
|
13382
13826
|
* Tries providers in priority order until one succeeds.
|
|
13827
|
+
* Automatically resolves authentication based on dependency configuration.
|
|
13383
13828
|
*/
|
|
13384
13829
|
async fetch(reference, options) {
|
|
13830
|
+
const resolvedOptions = this.resolveFetchOptions(reference, options);
|
|
13385
13831
|
const errors = [];
|
|
13386
13832
|
for (const type2 of this.priority) {
|
|
13387
13833
|
const provider = this.providers.get(type2);
|
|
@@ -13389,7 +13835,7 @@ var StorageManager = class {
|
|
|
13389
13835
|
continue;
|
|
13390
13836
|
}
|
|
13391
13837
|
try {
|
|
13392
|
-
return await provider.fetch(reference,
|
|
13838
|
+
return await provider.fetch(reference, resolvedOptions);
|
|
13393
13839
|
} catch (error) {
|
|
13394
13840
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
13395
13841
|
}
|
|
@@ -13410,15 +13856,17 @@ var StorageManager = class {
|
|
|
13410
13856
|
* Check if content exists for a reference
|
|
13411
13857
|
*
|
|
13412
13858
|
* Returns true if any provider reports the content exists.
|
|
13859
|
+
* Automatically resolves authentication based on dependency configuration.
|
|
13413
13860
|
*/
|
|
13414
13861
|
async exists(reference, options) {
|
|
13862
|
+
const resolvedOptions = this.resolveFetchOptions(reference, options);
|
|
13415
13863
|
for (const type2 of this.priority) {
|
|
13416
13864
|
const provider = this.providers.get(type2);
|
|
13417
13865
|
if (!provider || !provider.canHandle(reference)) {
|
|
13418
13866
|
continue;
|
|
13419
13867
|
}
|
|
13420
13868
|
try {
|
|
13421
|
-
if (await provider.exists(reference,
|
|
13869
|
+
if (await provider.exists(reference, resolvedOptions)) {
|
|
13422
13870
|
return true;
|
|
13423
13871
|
}
|
|
13424
13872
|
} catch {
|
|
@@ -13438,6 +13886,8 @@ var StorageManager = class {
|
|
|
13438
13886
|
}
|
|
13439
13887
|
/**
|
|
13440
13888
|
* Fetch multiple references in parallel
|
|
13889
|
+
*
|
|
13890
|
+
* Automatically resolves authentication for each reference based on dependency configuration.
|
|
13441
13891
|
*/
|
|
13442
13892
|
async fetchMany(references, options) {
|
|
13443
13893
|
const results = /* @__PURE__ */ new Map();
|
|
@@ -13806,8 +14256,17 @@ var CacheManager = class {
|
|
|
13806
14256
|
* Get content for a reference
|
|
13807
14257
|
*
|
|
13808
14258
|
* Implements cache-first strategy with stale-while-revalidate.
|
|
14259
|
+
*
|
|
14260
|
+
* EXCEPTION: File plugin sources (current project files) bypass cache entirely.
|
|
14261
|
+
* They are always read fresh from disk for optimal development experience.
|
|
13809
14262
|
*/
|
|
13810
14263
|
async get(reference, options) {
|
|
14264
|
+
if (reference.isCurrentProject && reference.sourceType === "file-plugin") {
|
|
14265
|
+
if (!this.storage) {
|
|
14266
|
+
throw new Error("Storage manager not set");
|
|
14267
|
+
}
|
|
14268
|
+
return await this.storage.fetch(reference, options);
|
|
14269
|
+
}
|
|
13811
14270
|
const ttl = options?.ttl ?? this.config.defaultTtl;
|
|
13812
14271
|
let entry = this.memoryCache.get(reference.uri);
|
|
13813
14272
|
if (!entry && this.persistence) {
|
|
@@ -14013,13 +14472,18 @@ var CacheManager = class {
|
|
|
14013
14472
|
}
|
|
14014
14473
|
/**
|
|
14015
14474
|
* Fetch content and store in cache
|
|
14475
|
+
*
|
|
14476
|
+
* EXCEPTION: File plugin sources are not cached (should not reach here,
|
|
14477
|
+
* but added as safety check).
|
|
14016
14478
|
*/
|
|
14017
14479
|
async fetchAndCache(reference, ttl, options) {
|
|
14018
14480
|
if (!this.storage) {
|
|
14019
14481
|
throw new Error("Storage manager not set");
|
|
14020
14482
|
}
|
|
14021
14483
|
const result = await this.storage.fetch(reference, options);
|
|
14022
|
-
|
|
14484
|
+
if (!(reference.isCurrentProject && reference.sourceType === "file-plugin")) {
|
|
14485
|
+
await this.set(reference.uri, result, ttl);
|
|
14486
|
+
}
|
|
14023
14487
|
return result;
|
|
14024
14488
|
}
|
|
14025
14489
|
/**
|
|
@@ -14203,6 +14667,14 @@ var CODEX_TOOLS = [
|
|
|
14203
14667
|
},
|
|
14204
14668
|
required: ["pattern"]
|
|
14205
14669
|
}
|
|
14670
|
+
},
|
|
14671
|
+
{
|
|
14672
|
+
name: "codex_file_sources_list",
|
|
14673
|
+
description: "List file plugin sources available in the current project.",
|
|
14674
|
+
inputSchema: {
|
|
14675
|
+
type: "object",
|
|
14676
|
+
properties: {}
|
|
14677
|
+
}
|
|
14206
14678
|
}
|
|
14207
14679
|
];
|
|
14208
14680
|
function textResult(text, isError = false) {
|
|
@@ -14239,9 +14711,12 @@ async function handleFetch(args, ctx) {
|
|
|
14239
14711
|
}
|
|
14240
14712
|
try {
|
|
14241
14713
|
let result;
|
|
14714
|
+
const isFilePlugin = ref.isCurrentProject && ref.sourceType === "file-plugin";
|
|
14242
14715
|
if (noCache) {
|
|
14243
14716
|
result = await ctx.storage.fetch(ref, { branch });
|
|
14244
|
-
|
|
14717
|
+
if (!isFilePlugin) {
|
|
14718
|
+
await ctx.cache.set(uri, result);
|
|
14719
|
+
}
|
|
14245
14720
|
} else {
|
|
14246
14721
|
result = await ctx.cache.get(ref, { branch });
|
|
14247
14722
|
}
|
|
@@ -14249,6 +14724,10 @@ async function handleFetch(args, ctx) {
|
|
|
14249
14724
|
return resourceResult(uri, content, result.contentType);
|
|
14250
14725
|
} catch (error) {
|
|
14251
14726
|
const message = error instanceof Error ? error.message : String(error);
|
|
14727
|
+
const errorName = error instanceof Error ? error.name : "Error";
|
|
14728
|
+
if (errorName === "FilePluginFileNotFoundError") {
|
|
14729
|
+
return textResult(message, true);
|
|
14730
|
+
}
|
|
14252
14731
|
return textResult(`Failed to fetch ${uri}: ${message}`, true);
|
|
14253
14732
|
}
|
|
14254
14733
|
}
|
|
@@ -14347,6 +14826,33 @@ async function handleCacheClear(args, ctx) {
|
|
|
14347
14826
|
return textResult(`Failed to clear cache: ${message}`, true);
|
|
14348
14827
|
}
|
|
14349
14828
|
}
|
|
14829
|
+
async function handleFileSourcesList(ctx) {
|
|
14830
|
+
try {
|
|
14831
|
+
const filePluginProvider = ctx.storage.getProvider("file-plugin");
|
|
14832
|
+
if (!filePluginProvider) {
|
|
14833
|
+
return textResult(`No file plugin sources configured.
|
|
14834
|
+
|
|
14835
|
+
To enable file plugin integration, the storage manager must be initialized with filePlugin configuration.
|
|
14836
|
+
|
|
14837
|
+
File plugin sources allow codex to access current project artifacts (specs, logs, etc.) directly from the local filesystem without caching.`);
|
|
14838
|
+
}
|
|
14839
|
+
return textResult(`File plugin provider is configured.
|
|
14840
|
+
|
|
14841
|
+
File plugin sources are available for current project artifact access.
|
|
14842
|
+
Sources are read directly from local filesystem without caching.
|
|
14843
|
+
|
|
14844
|
+
To see available sources, check the unified configuration at:
|
|
14845
|
+
.fractary/config.yaml
|
|
14846
|
+
|
|
14847
|
+
Look for the 'file.sources' section which defines:
|
|
14848
|
+
- specs: .fractary/specs
|
|
14849
|
+
- logs: .fractary/logs
|
|
14850
|
+
- etc.`);
|
|
14851
|
+
} catch (error) {
|
|
14852
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14853
|
+
return textResult(`Failed to list file sources: ${message}`, true);
|
|
14854
|
+
}
|
|
14855
|
+
}
|
|
14350
14856
|
async function handleToolCall(name, args, ctx) {
|
|
14351
14857
|
switch (name) {
|
|
14352
14858
|
case "codex_document_fetch":
|
|
@@ -14357,6 +14863,8 @@ async function handleToolCall(name, args, ctx) {
|
|
|
14357
14863
|
return handleList(args, ctx);
|
|
14358
14864
|
case "codex_cache_clear":
|
|
14359
14865
|
return handleCacheClear(args, ctx);
|
|
14866
|
+
case "codex_file_sources_list":
|
|
14867
|
+
return handleFileSourcesList(ctx);
|
|
14360
14868
|
default:
|
|
14361
14869
|
return textResult(`Unknown tool: ${name}`, true);
|
|
14362
14870
|
}
|