@fractary/codex 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/index.cjs CHANGED
@@ -5,15 +5,33 @@ var path3 = require('path');
5
5
  var child_process = require('child_process');
6
6
  var zod = require('zod');
7
7
  var yaml = require('js-yaml');
8
- var fs2 = require('fs/promises');
8
+ var fs3 = require('fs/promises');
9
9
  var util = require('util');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
13
+ function _interopNamespace(e) {
14
+ if (e && e.__esModule) return e;
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n.default = e;
28
+ return Object.freeze(n);
29
+ }
30
+
13
31
  var micromatch3__default = /*#__PURE__*/_interopDefault(micromatch3);
14
- var path3__default = /*#__PURE__*/_interopDefault(path3);
32
+ var path3__namespace = /*#__PURE__*/_interopNamespace(path3);
15
33
  var yaml__default = /*#__PURE__*/_interopDefault(yaml);
16
- var fs2__default = /*#__PURE__*/_interopDefault(fs2);
34
+ var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
17
35
 
18
36
  var __defProp = Object.defineProperty;
19
37
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -170,7 +188,7 @@ function validatePath(filePath) {
170
188
  return false;
171
189
  }
172
190
  }
173
- const normalized = path3__default.default.normalize(filePath);
191
+ const normalized = path3__namespace.default.normalize(filePath);
174
192
  if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("\\..\\")) {
175
193
  return false;
176
194
  }
@@ -188,7 +206,7 @@ function sanitizePath(filePath) {
188
206
  }
189
207
  sanitized = sanitized.replace(/^\/+/, "");
190
208
  sanitized = sanitized.replace(/^~\//, "");
191
- sanitized = path3__default.default.normalize(sanitized);
209
+ sanitized = path3__namespace.default.normalize(sanitized);
192
210
  sanitized = sanitized.replace(/\.\.\//g, "").replace(/\.\./g, "");
193
211
  sanitized = sanitized.replace(/^\.\//, "");
194
212
  sanitized = sanitized.replace(/\0/g, "");
@@ -289,10 +307,10 @@ function parseReference(uri, options = {}) {
289
307
  path: filePath
290
308
  };
291
309
  }
292
- function buildUri(org, project, path6) {
310
+ function buildUri(org, project, path7) {
293
311
  const base = `${CODEX_URI_PREFIX}${org}/${project}`;
294
- if (path6) {
295
- const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
312
+ if (path7) {
313
+ const cleanPath = path7.startsWith("/") ? path7.slice(1) : path7;
296
314
  return `${base}/${cleanPath}`;
297
315
  }
298
316
  return base;
@@ -363,7 +381,7 @@ function getCurrentContext(options = {}) {
363
381
  return detectCurrentProject(options.cwd);
364
382
  }
365
383
  function calculateCachePath(org, project, filePath, cacheDir) {
366
- return path3__default.default.join(cacheDir, org, project, filePath);
384
+ return path3__namespace.default.join(cacheDir, org, project, filePath);
367
385
  }
368
386
  function resolveReference(uri, options = {}) {
369
387
  const parsed = parseReference(uri);
@@ -372,7 +390,7 @@ function resolveReference(uri, options = {}) {
372
390
  }
373
391
  const cacheDir = options.cacheDir || DEFAULT_CACHE_DIR;
374
392
  const currentContext = getCurrentContext(options);
375
- const cachePath = parsed.path ? calculateCachePath(parsed.org, parsed.project, parsed.path, cacheDir) : path3__default.default.join(cacheDir, parsed.org, parsed.project);
393
+ const cachePath = parsed.path ? calculateCachePath(parsed.org, parsed.project, parsed.path, cacheDir) : path3__namespace.default.join(cacheDir, parsed.org, parsed.project);
376
394
  const isCurrentProject = currentContext.org === parsed.org && currentContext.project === parsed.project;
377
395
  const resolved = {
378
396
  ...parsed,
@@ -381,9 +399,45 @@ function resolveReference(uri, options = {}) {
381
399
  };
382
400
  if (isCurrentProject && parsed.path) {
383
401
  resolved.localPath = parsed.path;
402
+ if (options.config) {
403
+ const fileSource = detectFilePluginSource(parsed.path, options.config);
404
+ if (fileSource) {
405
+ resolved.sourceType = "file-plugin";
406
+ resolved.filePluginSource = fileSource.name;
407
+ resolved.localPath = fileSource.fullPath;
408
+ }
409
+ }
384
410
  }
385
411
  return resolved;
386
412
  }
413
+ function detectFilePluginSource(filePath, config) {
414
+ if (!config?.file?.sources) {
415
+ return null;
416
+ }
417
+ for (const [sourceName, source] of Object.entries(config.file.sources)) {
418
+ const basePath = source.local?.base_path;
419
+ if (!basePath) {
420
+ continue;
421
+ }
422
+ const normalizedPath = filePath.replace(/^\.\//, "").replace(/^\//, "");
423
+ const normalizedBasePath = basePath.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
424
+ if (normalizedPath.startsWith(normalizedBasePath)) {
425
+ return {
426
+ name: sourceName,
427
+ fullPath: path3__namespace.default.join(basePath, normalizedPath.substring(normalizedBasePath.length).replace(/^\//, ""))
428
+ };
429
+ }
430
+ const sourceNameInPath = normalizedBasePath.split("/").pop();
431
+ if (sourceNameInPath && normalizedPath.startsWith(sourceNameInPath + "/")) {
432
+ const pathWithoutSource = normalizedPath.substring(sourceNameInPath.length + 1);
433
+ return {
434
+ name: sourceName,
435
+ fullPath: path3__namespace.default.join(basePath, pathWithoutSource)
436
+ };
437
+ }
438
+ }
439
+ return null;
440
+ }
387
441
  function resolveReferences(uris, options = {}) {
388
442
  return uris.map((uri) => resolveReference(uri, options)).filter((r) => r !== null);
389
443
  }
@@ -397,9 +451,9 @@ function getRelativeCachePath(uri) {
397
451
  return null;
398
452
  }
399
453
  if (parsed.path) {
400
- return path3__default.default.join(parsed.org, parsed.project, parsed.path);
454
+ return path3__namespace.default.join(parsed.org, parsed.project, parsed.path);
401
455
  }
402
- return path3__default.default.join(parsed.org, parsed.project);
456
+ return path3__namespace.default.join(parsed.org, parsed.project);
403
457
  }
404
458
 
405
459
  // src/types/built-in.ts
@@ -854,8 +908,40 @@ var ArchiveProjectConfigSchema = zod.z.object({
854
908
  var ArchiveConfigSchema = zod.z.object({
855
909
  projects: zod.z.record(ArchiveProjectConfigSchema)
856
910
  });
911
+ var GitHubAuthConfigSchema = zod.z.object({
912
+ /** Default token environment variable name (default: GITHUB_TOKEN) */
913
+ default_token_env: zod.z.string().optional(),
914
+ /** Fallback to public access if authentication fails */
915
+ fallback_to_public: zod.z.boolean().optional()
916
+ });
917
+ var AuthConfigSchema = zod.z.object({
918
+ /** GitHub authentication configuration */
919
+ github: GitHubAuthConfigSchema.optional()
920
+ });
921
+ var SourceConfigSchema = zod.z.object({
922
+ /** Source type */
923
+ type: zod.z.enum(["github", "s3", "http", "local"]),
924
+ /** Environment variable containing the authentication token */
925
+ token_env: zod.z.string().optional(),
926
+ /** Direct token value (not recommended, use token_env instead) */
927
+ token: zod.z.string().optional(),
928
+ /** Branch to fetch from (for GitHub sources) */
929
+ branch: zod.z.string().optional(),
930
+ /** Base URL (for HTTP sources) */
931
+ base_url: zod.z.string().optional(),
932
+ /** Bucket name (for S3 sources) */
933
+ bucket: zod.z.string().optional(),
934
+ /** Prefix/path within bucket (for S3 sources) */
935
+ prefix: zod.z.string().optional()
936
+ });
937
+ var DependencyConfigSchema = zod.z.object({
938
+ /** Sources within this dependency */
939
+ sources: zod.z.record(SourceConfigSchema)
940
+ });
857
941
  var CodexConfigSchema = zod.z.object({
858
942
  organizationSlug: zod.z.string(),
943
+ /** Project name (optional) */
944
+ project: zod.z.string().optional(),
859
945
  directories: zod.z.object({
860
946
  source: zod.z.string().optional(),
861
947
  target: zod.z.string().optional(),
@@ -865,8 +951,47 @@ var CodexConfigSchema = zod.z.object({
865
951
  // Directional sync configuration
866
952
  sync: DirectionalSyncSchema.optional(),
867
953
  // Archive configuration
868
- archive: ArchiveConfigSchema.optional()
954
+ archive: ArchiveConfigSchema.optional(),
955
+ // Authentication configuration
956
+ auth: AuthConfigSchema.optional(),
957
+ // Dependencies configuration (external projects)
958
+ dependencies: zod.z.record(DependencyConfigSchema).optional()
869
959
  }).strict();
960
+ var FileSourceSchema = zod.z.object({
961
+ type: zod.z.enum(["s3", "r2", "gcs", "local"]),
962
+ bucket: zod.z.string().optional(),
963
+ prefix: zod.z.string().optional(),
964
+ region: zod.z.string().optional(),
965
+ local: zod.z.object({
966
+ base_path: zod.z.string()
967
+ }),
968
+ push: zod.z.object({
969
+ compress: zod.z.boolean().optional(),
970
+ keep_local: zod.z.boolean().optional()
971
+ }).optional(),
972
+ auth: zod.z.object({
973
+ profile: zod.z.string().optional()
974
+ }).optional()
975
+ }).refine(
976
+ (data) => {
977
+ if (data.type !== "local" && !data.bucket) {
978
+ return false;
979
+ }
980
+ return true;
981
+ },
982
+ {
983
+ message: "Bucket is required for s3, r2, and gcs storage types",
984
+ path: ["bucket"]
985
+ }
986
+ );
987
+ var FileConfigSchema = zod.z.object({
988
+ schema_version: zod.z.string(),
989
+ sources: zod.z.record(FileSourceSchema)
990
+ });
991
+ zod.z.object({
992
+ file: FileConfigSchema.optional(),
993
+ codex: CodexConfigSchema.optional()
994
+ });
870
995
  function parseMetadata(content, options = {}) {
871
996
  const { strict = true, normalize = true } = options;
872
997
  const normalizedContent = normalize ? content.replace(/\r\n/g, "\n") : content;
@@ -1196,18 +1321,18 @@ function parseCustomDestination(value) {
1196
1321
  );
1197
1322
  }
1198
1323
  const repo = value.substring(0, colonIndex).trim();
1199
- const path6 = value.substring(colonIndex + 1).trim();
1324
+ const path7 = value.substring(colonIndex + 1).trim();
1200
1325
  if (!repo) {
1201
1326
  throw new ValidationError(
1202
1327
  `Invalid custom destination: repository name cannot be empty in "${value}"`
1203
1328
  );
1204
1329
  }
1205
- if (!path6) {
1330
+ if (!path7) {
1206
1331
  throw new ValidationError(
1207
1332
  `Invalid custom destination: path cannot be empty in "${value}"`
1208
1333
  );
1209
1334
  }
1210
- return { repo, path: path6 };
1335
+ return { repo, path: path7 };
1211
1336
  }
1212
1337
  function getCustomSyncDestinations(metadata) {
1213
1338
  const customDestinations = metadata.codex_sync_custom;
@@ -1243,8 +1368,8 @@ function mergeFetchOptions(options) {
1243
1368
  ...options
1244
1369
  };
1245
1370
  }
1246
- function detectContentType(path6) {
1247
- const ext = path6.split(".").pop()?.toLowerCase();
1371
+ function detectContentType(path7) {
1372
+ const ext = path7.split(".").pop()?.toLowerCase();
1248
1373
  const mimeTypes = {
1249
1374
  md: "text/markdown",
1250
1375
  markdown: "text/markdown",
@@ -1304,19 +1429,19 @@ var LocalStorage = class {
1304
1429
  if (!reference.localPath) {
1305
1430
  throw new Error(`No local path for reference: ${reference.uri}`);
1306
1431
  }
1307
- const fullPath = path3__default.default.isAbsolute(reference.localPath) ? reference.localPath : path3__default.default.join(this.baseDir, reference.localPath);
1432
+ const fullPath = path3__namespace.default.isAbsolute(reference.localPath) ? reference.localPath : path3__namespace.default.join(this.baseDir, reference.localPath);
1308
1433
  try {
1309
- await fs2__default.default.access(fullPath);
1434
+ await fs3__namespace.default.access(fullPath);
1310
1435
  } catch {
1311
1436
  throw new Error(`File not found: ${fullPath}`);
1312
1437
  }
1313
- const stats = await fs2__default.default.stat(fullPath);
1438
+ const stats = await fs3__namespace.default.stat(fullPath);
1314
1439
  if (stats.size > opts.maxSize) {
1315
1440
  throw new Error(
1316
1441
  `File too large: ${stats.size} bytes (max: ${opts.maxSize} bytes)`
1317
1442
  );
1318
1443
  }
1319
- const content = await fs2__default.default.readFile(fullPath);
1444
+ const content = await fs3__namespace.default.readFile(fullPath);
1320
1445
  return {
1321
1446
  content,
1322
1447
  contentType: detectContentType(reference.localPath),
@@ -1335,9 +1460,9 @@ var LocalStorage = class {
1335
1460
  if (!reference.localPath) {
1336
1461
  return false;
1337
1462
  }
1338
- const fullPath = path3__default.default.isAbsolute(reference.localPath) ? reference.localPath : path3__default.default.join(this.baseDir, reference.localPath);
1463
+ const fullPath = path3__namespace.default.isAbsolute(reference.localPath) ? reference.localPath : path3__namespace.default.join(this.baseDir, reference.localPath);
1339
1464
  try {
1340
- await fs2__default.default.access(fullPath);
1465
+ await fs3__namespace.default.access(fullPath);
1341
1466
  return true;
1342
1467
  } catch {
1343
1468
  return false;
@@ -1347,24 +1472,24 @@ var LocalStorage = class {
1347
1472
  * Read file content as string
1348
1473
  */
1349
1474
  async readText(filePath) {
1350
- const fullPath = path3__default.default.isAbsolute(filePath) ? filePath : path3__default.default.join(this.baseDir, filePath);
1351
- return fs2__default.default.readFile(fullPath, "utf-8");
1475
+ const fullPath = path3__namespace.default.isAbsolute(filePath) ? filePath : path3__namespace.default.join(this.baseDir, filePath);
1476
+ return fs3__namespace.default.readFile(fullPath, "utf-8");
1352
1477
  }
1353
1478
  /**
1354
1479
  * Write content to file
1355
1480
  */
1356
1481
  async write(filePath, content) {
1357
- const fullPath = path3__default.default.isAbsolute(filePath) ? filePath : path3__default.default.join(this.baseDir, filePath);
1358
- await fs2__default.default.mkdir(path3__default.default.dirname(fullPath), { recursive: true });
1359
- await fs2__default.default.writeFile(fullPath, content);
1482
+ const fullPath = path3__namespace.default.isAbsolute(filePath) ? filePath : path3__namespace.default.join(this.baseDir, filePath);
1483
+ await fs3__namespace.default.mkdir(path3__namespace.default.dirname(fullPath), { recursive: true });
1484
+ await fs3__namespace.default.writeFile(fullPath, content);
1360
1485
  }
1361
1486
  /**
1362
1487
  * Delete a file
1363
1488
  */
1364
1489
  async delete(filePath) {
1365
- const fullPath = path3__default.default.isAbsolute(filePath) ? filePath : path3__default.default.join(this.baseDir, filePath);
1490
+ const fullPath = path3__namespace.default.isAbsolute(filePath) ? filePath : path3__namespace.default.join(this.baseDir, filePath);
1366
1491
  try {
1367
- await fs2__default.default.unlink(fullPath);
1492
+ await fs3__namespace.default.unlink(fullPath);
1368
1493
  return true;
1369
1494
  } catch {
1370
1495
  return false;
@@ -1374,10 +1499,10 @@ var LocalStorage = class {
1374
1499
  * List files in a directory
1375
1500
  */
1376
1501
  async list(dirPath) {
1377
- const fullPath = path3__default.default.isAbsolute(dirPath) ? dirPath : path3__default.default.join(this.baseDir, dirPath);
1502
+ const fullPath = path3__namespace.default.isAbsolute(dirPath) ? dirPath : path3__namespace.default.join(this.baseDir, dirPath);
1378
1503
  try {
1379
- const entries = await fs2__default.default.readdir(fullPath, { withFileTypes: true });
1380
- return entries.filter((e) => e.isFile()).map((e) => path3__default.default.join(dirPath, e.name));
1504
+ const entries = await fs3__namespace.default.readdir(fullPath, { withFileTypes: true });
1505
+ return entries.filter((e) => e.isFile()).map((e) => path3__namespace.default.join(dirPath, e.name));
1381
1506
  } catch {
1382
1507
  return [];
1383
1508
  }
@@ -1722,6 +1847,300 @@ var HttpStorage = class {
1722
1847
  function createHttpStorage(options) {
1723
1848
  return new HttpStorage(options);
1724
1849
  }
1850
+
1851
+ // src/file-integration/source-resolver.ts
1852
+ var FileSourceResolver = class {
1853
+ constructor(config) {
1854
+ this.config = config;
1855
+ this.initializeSources();
1856
+ }
1857
+ sources = /* @__PURE__ */ new Map();
1858
+ /**
1859
+ * Initialize sources from config
1860
+ */
1861
+ initializeSources() {
1862
+ if (!this.config.file?.sources) {
1863
+ return;
1864
+ }
1865
+ for (const [name, sourceConfig] of Object.entries(this.config.file.sources)) {
1866
+ const resolved = {
1867
+ name,
1868
+ type: "file-plugin",
1869
+ localPath: sourceConfig.local.base_path,
1870
+ isCurrentProject: true,
1871
+ config: sourceConfig
1872
+ };
1873
+ if (sourceConfig.bucket && sourceConfig.type !== "local") {
1874
+ resolved.bucket = sourceConfig.bucket;
1875
+ resolved.prefix = sourceConfig.prefix;
1876
+ resolved.remotePath = this.buildRemotePath(sourceConfig);
1877
+ }
1878
+ this.sources.set(name, resolved);
1879
+ }
1880
+ }
1881
+ /**
1882
+ * Build remote path from source config
1883
+ */
1884
+ buildRemotePath(source) {
1885
+ const protocol = source.type === "s3" ? "s3://" : source.type === "r2" ? "r2://" : "gcs://";
1886
+ const bucket = source.bucket;
1887
+ const prefix = source.prefix ? `/${source.prefix}` : "";
1888
+ return `${protocol}${bucket}${prefix}`;
1889
+ }
1890
+ /**
1891
+ * Get all available file plugin sources
1892
+ *
1893
+ * @returns Array of resolved file sources
1894
+ */
1895
+ getAvailableSources() {
1896
+ return Array.from(this.sources.values());
1897
+ }
1898
+ /**
1899
+ * Resolve a source by name
1900
+ *
1901
+ * @param name - Source name (e.g., "specs", "logs")
1902
+ * @returns Resolved source or null if not found
1903
+ */
1904
+ resolveSource(name) {
1905
+ return this.sources.get(name) || null;
1906
+ }
1907
+ /**
1908
+ * Check if a path belongs to any file plugin source
1909
+ *
1910
+ * @param path - File path to check
1911
+ * @returns True if path matches any source's base_path
1912
+ */
1913
+ isFilePluginPath(path7) {
1914
+ return this.getSourceForPath(path7) !== null;
1915
+ }
1916
+ /**
1917
+ * Get the source for a given path
1918
+ *
1919
+ * Matches path against all source base_paths and returns the matching source.
1920
+ * Uses longest-match strategy if multiple sources match.
1921
+ *
1922
+ * @param path - File path to match
1923
+ * @returns Matching source or null
1924
+ */
1925
+ getSourceForPath(path7) {
1926
+ let bestMatch = null;
1927
+ let bestMatchLength = 0;
1928
+ for (const source of this.sources.values()) {
1929
+ const normalizedPath = this.normalizePath(path7);
1930
+ const normalizedBasePath = this.normalizePath(source.localPath);
1931
+ if (normalizedPath.startsWith(normalizedBasePath)) {
1932
+ const matchLength = normalizedBasePath.length;
1933
+ if (matchLength > bestMatchLength) {
1934
+ bestMatch = source;
1935
+ bestMatchLength = matchLength;
1936
+ }
1937
+ }
1938
+ }
1939
+ return bestMatch;
1940
+ }
1941
+ /**
1942
+ * Normalize path for comparison
1943
+ * - Remove leading "./" or "/"
1944
+ * - Remove trailing "/"
1945
+ * - Convert to lowercase for case-insensitive comparison
1946
+ */
1947
+ normalizePath(path7) {
1948
+ return path7.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "").toLowerCase();
1949
+ }
1950
+ /**
1951
+ * Get source names
1952
+ *
1953
+ * @returns Array of source names
1954
+ */
1955
+ getSourceNames() {
1956
+ return Array.from(this.sources.keys());
1957
+ }
1958
+ /**
1959
+ * Check if sources are configured
1960
+ *
1961
+ * @returns True if any file plugin sources are configured
1962
+ */
1963
+ hasSources() {
1964
+ return this.sources.size > 0;
1965
+ }
1966
+ };
1967
+
1968
+ // src/storage/errors.ts
1969
+ var FilePluginFileNotFoundError = class _FilePluginFileNotFoundError extends Error {
1970
+ constructor(filePath, sourceName, options) {
1971
+ const includeCloudSuggestions = options?.includeCloudSuggestions !== false;
1972
+ const storageType = options?.storageType;
1973
+ let message = `File not found: ${filePath}
1974
+
1975
+ `;
1976
+ if (includeCloudSuggestions) {
1977
+ if (storageType) {
1978
+ message += `This file may be in cloud storage (${storageType}).
1979
+
1980
+ `;
1981
+ } else {
1982
+ message += `This file may not have been synced from remote storage yet.
1983
+ `;
1984
+ }
1985
+ message += `To fetch from cloud storage, run:
1986
+ `;
1987
+ message += ` file pull ${sourceName}
1988
+
1989
+ `;
1990
+ message += `Or sync all sources:
1991
+ `;
1992
+ message += ` file sync`;
1993
+ } else {
1994
+ message += `Please ensure the file exists locally or pull it from cloud storage.`;
1995
+ }
1996
+ super(message);
1997
+ this.filePath = filePath;
1998
+ this.sourceName = sourceName;
1999
+ this.name = "FilePluginFileNotFoundError";
2000
+ if (Error.captureStackTrace) {
2001
+ Error.captureStackTrace(this, _FilePluginFileNotFoundError);
2002
+ }
2003
+ }
2004
+ };
2005
+
2006
+ // src/storage/file-plugin.ts
2007
+ var FilePluginStorage = class {
2008
+ constructor(options) {
2009
+ this.options = options;
2010
+ this.sourceResolver = new FileSourceResolver(options.config);
2011
+ this.baseDir = options.baseDir || process.cwd();
2012
+ }
2013
+ name = "file-plugin";
2014
+ type = "local";
2015
+ // Reuse local type for compatibility
2016
+ sourceResolver;
2017
+ baseDir;
2018
+ /**
2019
+ * Check if this provider can handle the reference
2020
+ *
2021
+ * Only handles:
2022
+ * - Current project references
2023
+ * - With sourceType === 'file-plugin'
2024
+ *
2025
+ * @param reference - Resolved reference
2026
+ * @returns True if this provider can handle the reference
2027
+ */
2028
+ canHandle(reference) {
2029
+ return reference.isCurrentProject && reference.sourceType === "file-plugin";
2030
+ }
2031
+ /**
2032
+ * Fetch content for a reference
2033
+ *
2034
+ * Reads from local filesystem based on the resolved local path.
2035
+ * If file not found and S3 fallback is enabled, throws helpful error.
2036
+ *
2037
+ * @param reference - Resolved reference
2038
+ * @param options - Fetch options (unused for local reads)
2039
+ * @returns Fetch result with content
2040
+ */
2041
+ async fetch(reference, _options) {
2042
+ if (!reference.localPath) {
2043
+ throw new Error(`File plugin reference missing localPath: ${reference.uri}`);
2044
+ }
2045
+ if (!reference.filePluginSource) {
2046
+ throw new Error(`File plugin reference missing source name: ${reference.uri}`);
2047
+ }
2048
+ const absolutePath = path3__namespace.isAbsolute(reference.localPath) ? path3__namespace.resolve(reference.localPath) : path3__namespace.resolve(this.baseDir, reference.localPath);
2049
+ const source = this.sourceResolver.resolveSource(reference.filePluginSource);
2050
+ if (source) {
2051
+ const allowedDir = path3__namespace.resolve(this.baseDir, source.localPath);
2052
+ if (!absolutePath.startsWith(allowedDir + path3__namespace.sep) && absolutePath !== allowedDir) {
2053
+ throw new Error(
2054
+ `Path traversal detected: ${reference.localPath} resolves outside allowed directory ${source.localPath}`
2055
+ );
2056
+ }
2057
+ }
2058
+ try {
2059
+ const content = await fs3__namespace.readFile(absolutePath);
2060
+ const contentType = this.detectContentType(absolutePath);
2061
+ return {
2062
+ content,
2063
+ contentType,
2064
+ size: content.length,
2065
+ source: "file-plugin",
2066
+ metadata: {
2067
+ filePluginSource: reference.filePluginSource,
2068
+ localPath: absolutePath
2069
+ }
2070
+ };
2071
+ } catch (error) {
2072
+ if (error.code === "ENOENT") {
2073
+ const source2 = this.sourceResolver.resolveSource(reference.filePluginSource);
2074
+ throw this.createFileNotFoundError(reference, source2);
2075
+ }
2076
+ throw error;
2077
+ }
2078
+ }
2079
+ /**
2080
+ * Check if a reference exists
2081
+ *
2082
+ * @param reference - Resolved reference
2083
+ * @param options - Fetch options (unused)
2084
+ * @returns True if file exists
2085
+ */
2086
+ async exists(reference, _options) {
2087
+ if (!reference.localPath) {
2088
+ return false;
2089
+ }
2090
+ const absolutePath = path3__namespace.isAbsolute(reference.localPath) ? path3__namespace.resolve(reference.localPath) : path3__namespace.resolve(this.baseDir, reference.localPath);
2091
+ if (reference.filePluginSource) {
2092
+ const source = this.sourceResolver.resolveSource(reference.filePluginSource);
2093
+ if (source) {
2094
+ const allowedDir = path3__namespace.resolve(this.baseDir, source.localPath);
2095
+ if (!absolutePath.startsWith(allowedDir + path3__namespace.sep) && absolutePath !== allowedDir) {
2096
+ return false;
2097
+ }
2098
+ }
2099
+ }
2100
+ try {
2101
+ await fs3__namespace.access(absolutePath);
2102
+ return true;
2103
+ } catch {
2104
+ return false;
2105
+ }
2106
+ }
2107
+ /**
2108
+ * Detect content type from file extension
2109
+ */
2110
+ detectContentType(filePath) {
2111
+ const ext = path3__namespace.extname(filePath).toLowerCase();
2112
+ const mimeTypes = {
2113
+ ".md": "text/markdown",
2114
+ ".txt": "text/plain",
2115
+ ".json": "application/json",
2116
+ ".yaml": "text/yaml",
2117
+ ".yml": "text/yaml",
2118
+ ".html": "text/html",
2119
+ ".xml": "application/xml",
2120
+ ".log": "text/plain",
2121
+ ".js": "application/javascript",
2122
+ ".ts": "application/typescript",
2123
+ ".py": "text/x-python",
2124
+ ".sh": "application/x-sh"
2125
+ };
2126
+ return mimeTypes[ext] || "application/octet-stream";
2127
+ }
2128
+ /**
2129
+ * Create a helpful error message when file is not found
2130
+ */
2131
+ createFileNotFoundError(reference, source) {
2132
+ const includeCloudSuggestions = this.options.enableS3Fallback !== false;
2133
+ const storageType = source?.config.type;
2134
+ return new FilePluginFileNotFoundError(
2135
+ reference.localPath || reference.path || "",
2136
+ reference.filePluginSource || "",
2137
+ {
2138
+ includeCloudSuggestions,
2139
+ storageType
2140
+ }
2141
+ );
2142
+ }
2143
+ };
1725
2144
  var execFileAsync = util.promisify(child_process.execFile);
1726
2145
  async function execFileNoThrow(command, args = [], options) {
1727
2146
  try {
@@ -1871,10 +2290,10 @@ var S3ArchiveStorage = class {
1871
2290
  *
1872
2291
  * Used to organize archives by type
1873
2292
  */
1874
- detectType(path6) {
1875
- if (path6.startsWith("specs/")) return "specs";
1876
- if (path6.startsWith("docs/")) return "docs";
1877
- if (path6.includes("/logs/")) return "logs";
2293
+ detectType(path7) {
2294
+ if (path7.startsWith("specs/")) return "specs";
2295
+ if (path7.startsWith("docs/")) return "docs";
2296
+ if (path7.includes("/logs/")) return "logs";
1878
2297
  return "misc";
1879
2298
  }
1880
2299
  /**
@@ -1885,9 +2304,9 @@ var S3ArchiveStorage = class {
1885
2304
  * - *.md (all markdown files)
1886
2305
  * - docs/*.md (markdown files in docs/)
1887
2306
  */
1888
- matchesPatterns(path6, patterns) {
2307
+ matchesPatterns(path7, patterns) {
1889
2308
  for (const pattern of patterns) {
1890
- if (this.matchesPattern(path6, pattern)) {
2309
+ if (this.matchesPattern(path7, pattern)) {
1891
2310
  return true;
1892
2311
  }
1893
2312
  }
@@ -1896,14 +2315,14 @@ var S3ArchiveStorage = class {
1896
2315
  /**
1897
2316
  * Check if path matches a single pattern
1898
2317
  */
1899
- matchesPattern(path6, pattern) {
2318
+ matchesPattern(path7, pattern) {
1900
2319
  const DOUBLE_STAR = "\0DOUBLE_STAR\0";
1901
2320
  let regexPattern = pattern.replace(/\*\*/g, DOUBLE_STAR);
1902
2321
  regexPattern = regexPattern.replace(/[.[\](){}+^$|\\]/g, "\\$&");
1903
2322
  regexPattern = regexPattern.replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
1904
2323
  regexPattern = regexPattern.replace(new RegExp(DOUBLE_STAR, "g"), ".*");
1905
2324
  const regex = new RegExp(`^${regexPattern}$`);
1906
- return regex.test(path6);
2325
+ return regex.test(path7);
1907
2326
  }
1908
2327
  };
1909
2328
 
@@ -1911,14 +2330,61 @@ var S3ArchiveStorage = class {
1911
2330
  var StorageManager = class {
1912
2331
  providers = /* @__PURE__ */ new Map();
1913
2332
  priority;
2333
+ codexConfig;
1914
2334
  constructor(config = {}) {
2335
+ this.codexConfig = config.codexConfig;
1915
2336
  this.providers.set("local", new LocalStorage(config.local));
1916
2337
  this.providers.set("github", new GitHubStorage(config.github));
1917
2338
  this.providers.set("http", new HttpStorage(config.http));
1918
2339
  if (config.s3Archive) {
1919
2340
  this.providers.set("s3-archive", new S3ArchiveStorage(config.s3Archive));
1920
2341
  }
1921
- this.priority = config.priority || (config.s3Archive ? ["local", "s3-archive", "github", "http"] : ["local", "github", "http"]);
2342
+ if (config.filePlugin) {
2343
+ this.providers.set("file-plugin", new FilePluginStorage(config.filePlugin));
2344
+ }
2345
+ 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"]);
2346
+ }
2347
+ /**
2348
+ * Resolve authentication token for a reference
2349
+ *
2350
+ * Looks up dependency-specific authentication or falls back to default
2351
+ */
2352
+ resolveToken(reference) {
2353
+ if (!this.codexConfig) {
2354
+ return void 0;
2355
+ }
2356
+ const dependencyKey = `${reference.org}/${reference.project}`;
2357
+ if (this.codexConfig.dependencies?.[dependencyKey]) {
2358
+ const dependency = this.codexConfig.dependencies[dependencyKey];
2359
+ for (const [, sourceConfig] of Object.entries(dependency.sources)) {
2360
+ if (sourceConfig.type === "github") {
2361
+ if (sourceConfig.token_env) {
2362
+ const token = process.env[sourceConfig.token_env];
2363
+ if (token) {
2364
+ return token;
2365
+ }
2366
+ }
2367
+ if (sourceConfig.token) {
2368
+ return sourceConfig.token;
2369
+ }
2370
+ }
2371
+ }
2372
+ }
2373
+ const defaultTokenEnv = this.codexConfig.auth?.github?.default_token_env || "GITHUB_TOKEN";
2374
+ return process.env[defaultTokenEnv];
2375
+ }
2376
+ /**
2377
+ * Resolve fetch options with authentication
2378
+ *
2379
+ * Merges reference-specific authentication with provided options
2380
+ */
2381
+ resolveFetchOptions(reference, options) {
2382
+ const token = this.resolveToken(reference);
2383
+ return {
2384
+ ...options,
2385
+ token: options?.token || token
2386
+ // Explicit option overrides resolved token
2387
+ };
1922
2388
  }
1923
2389
  /**
1924
2390
  * Register a custom storage provider
@@ -1960,8 +2426,10 @@ var StorageManager = class {
1960
2426
  * Fetch content for a reference
1961
2427
  *
1962
2428
  * Tries providers in priority order until one succeeds.
2429
+ * Automatically resolves authentication based on dependency configuration.
1963
2430
  */
1964
2431
  async fetch(reference, options) {
2432
+ const resolvedOptions = this.resolveFetchOptions(reference, options);
1965
2433
  const errors = [];
1966
2434
  for (const type of this.priority) {
1967
2435
  const provider = this.providers.get(type);
@@ -1969,7 +2437,7 @@ var StorageManager = class {
1969
2437
  continue;
1970
2438
  }
1971
2439
  try {
1972
- return await provider.fetch(reference, options);
2440
+ return await provider.fetch(reference, resolvedOptions);
1973
2441
  } catch (error) {
1974
2442
  errors.push(error instanceof Error ? error : new Error(String(error)));
1975
2443
  }
@@ -1990,15 +2458,17 @@ var StorageManager = class {
1990
2458
  * Check if content exists for a reference
1991
2459
  *
1992
2460
  * Returns true if any provider reports the content exists.
2461
+ * Automatically resolves authentication based on dependency configuration.
1993
2462
  */
1994
2463
  async exists(reference, options) {
2464
+ const resolvedOptions = this.resolveFetchOptions(reference, options);
1995
2465
  for (const type of this.priority) {
1996
2466
  const provider = this.providers.get(type);
1997
2467
  if (!provider || !provider.canHandle(reference)) {
1998
2468
  continue;
1999
2469
  }
2000
2470
  try {
2001
- if (await provider.exists(reference, options)) {
2471
+ if (await provider.exists(reference, resolvedOptions)) {
2002
2472
  return true;
2003
2473
  }
2004
2474
  } catch {
@@ -2018,6 +2488,8 @@ var StorageManager = class {
2018
2488
  }
2019
2489
  /**
2020
2490
  * Fetch multiple references in parallel
2491
+ *
2492
+ * Automatically resolves authentication for each reference based on dependency configuration.
2021
2493
  */
2022
2494
  async fetchMany(references, options) {
2023
2495
  const results = /* @__PURE__ */ new Map();
@@ -2158,7 +2630,7 @@ var CachePersistence = class {
2158
2630
  }
2159
2631
  const [, org, project, filePath] = match;
2160
2632
  const relativePath = filePath || "index";
2161
- return path3__default.default.join(this.cacheDir, org, project, relativePath + this.extension);
2633
+ return path3__namespace.default.join(this.cacheDir, org, project, relativePath + this.extension);
2162
2634
  }
2163
2635
  /**
2164
2636
  * Get the metadata file path for a URI
@@ -2174,8 +2646,8 @@ var CachePersistence = class {
2174
2646
  const metadataPath = this.getMetadataPath(uri);
2175
2647
  try {
2176
2648
  const [metadataJson, content] = await Promise.all([
2177
- fs2__default.default.readFile(metadataPath, "utf-8"),
2178
- fs2__default.default.readFile(cachePath)
2649
+ fs3__namespace.default.readFile(metadataPath, "utf-8"),
2650
+ fs3__namespace.default.readFile(cachePath)
2179
2651
  ]);
2180
2652
  const metadata = JSON.parse(metadataJson);
2181
2653
  return {
@@ -2195,32 +2667,32 @@ var CachePersistence = class {
2195
2667
  async write(entry) {
2196
2668
  const cachePath = this.getCachePath(entry.metadata.uri);
2197
2669
  const metadataPath = this.getMetadataPath(entry.metadata.uri);
2198
- await fs2__default.default.mkdir(path3__default.default.dirname(cachePath), { recursive: true });
2670
+ await fs3__namespace.default.mkdir(path3__namespace.default.dirname(cachePath), { recursive: true });
2199
2671
  if (this.atomicWrites) {
2200
2672
  const tempCachePath = cachePath + ".tmp";
2201
2673
  const tempMetadataPath = metadataPath + ".tmp";
2202
2674
  try {
2203
2675
  await Promise.all([
2204
- fs2__default.default.writeFile(tempCachePath, entry.content),
2205
- fs2__default.default.writeFile(tempMetadataPath, JSON.stringify(entry.metadata, null, 2))
2676
+ fs3__namespace.default.writeFile(tempCachePath, entry.content),
2677
+ fs3__namespace.default.writeFile(tempMetadataPath, JSON.stringify(entry.metadata, null, 2))
2206
2678
  ]);
2207
2679
  await Promise.all([
2208
- fs2__default.default.rename(tempCachePath, cachePath),
2209
- fs2__default.default.rename(tempMetadataPath, metadataPath)
2680
+ fs3__namespace.default.rename(tempCachePath, cachePath),
2681
+ fs3__namespace.default.rename(tempMetadataPath, metadataPath)
2210
2682
  ]);
2211
2683
  } catch (error) {
2212
2684
  await Promise.all([
2213
- fs2__default.default.unlink(tempCachePath).catch(() => {
2685
+ fs3__namespace.default.unlink(tempCachePath).catch(() => {
2214
2686
  }),
2215
- fs2__default.default.unlink(tempMetadataPath).catch(() => {
2687
+ fs3__namespace.default.unlink(tempMetadataPath).catch(() => {
2216
2688
  })
2217
2689
  ]);
2218
2690
  throw error;
2219
2691
  }
2220
2692
  } else {
2221
2693
  await Promise.all([
2222
- fs2__default.default.writeFile(cachePath, entry.content),
2223
- fs2__default.default.writeFile(metadataPath, JSON.stringify(entry.metadata, null, 2))
2694
+ fs3__namespace.default.writeFile(cachePath, entry.content),
2695
+ fs3__namespace.default.writeFile(metadataPath, JSON.stringify(entry.metadata, null, 2))
2224
2696
  ]);
2225
2697
  }
2226
2698
  }
@@ -2231,7 +2703,7 @@ var CachePersistence = class {
2231
2703
  const cachePath = this.getCachePath(uri);
2232
2704
  const metadataPath = this.getMetadataPath(uri);
2233
2705
  try {
2234
- await Promise.all([fs2__default.default.unlink(cachePath), fs2__default.default.unlink(metadataPath)]);
2706
+ await Promise.all([fs3__namespace.default.unlink(cachePath), fs3__namespace.default.unlink(metadataPath)]);
2235
2707
  return true;
2236
2708
  } catch (error) {
2237
2709
  if (error.code === "ENOENT") {
@@ -2246,7 +2718,7 @@ var CachePersistence = class {
2246
2718
  async exists(uri) {
2247
2719
  const cachePath = this.getCachePath(uri);
2248
2720
  try {
2249
- await fs2__default.default.access(cachePath);
2721
+ await fs3__namespace.default.access(cachePath);
2250
2722
  return true;
2251
2723
  } catch {
2252
2724
  return false;
@@ -2258,20 +2730,20 @@ var CachePersistence = class {
2258
2730
  async list() {
2259
2731
  const uris = [];
2260
2732
  try {
2261
- const orgs = await fs2__default.default.readdir(this.cacheDir);
2733
+ const orgs = await fs3__namespace.default.readdir(this.cacheDir);
2262
2734
  for (const org of orgs) {
2263
- const orgPath = path3__default.default.join(this.cacheDir, org);
2264
- const orgStat = await fs2__default.default.stat(orgPath);
2735
+ const orgPath = path3__namespace.default.join(this.cacheDir, org);
2736
+ const orgStat = await fs3__namespace.default.stat(orgPath);
2265
2737
  if (!orgStat.isDirectory()) continue;
2266
- const projects = await fs2__default.default.readdir(orgPath);
2738
+ const projects = await fs3__namespace.default.readdir(orgPath);
2267
2739
  for (const project of projects) {
2268
- const projectPath = path3__default.default.join(orgPath, project);
2269
- const projectStat = await fs2__default.default.stat(projectPath);
2740
+ const projectPath = path3__namespace.default.join(orgPath, project);
2741
+ const projectStat = await fs3__namespace.default.stat(projectPath);
2270
2742
  if (!projectStat.isDirectory()) continue;
2271
2743
  const files = await this.listFilesRecursive(projectPath);
2272
2744
  for (const file of files) {
2273
2745
  if (file.endsWith(this.extension)) {
2274
- const relativePath = path3__default.default.relative(projectPath, file);
2746
+ const relativePath = path3__namespace.default.relative(projectPath, file);
2275
2747
  const filePath = relativePath.slice(0, -this.extension.length);
2276
2748
  uris.push(`codex://${org}/${project}/${filePath}`);
2277
2749
  }
@@ -2291,9 +2763,9 @@ var CachePersistence = class {
2291
2763
  */
2292
2764
  async listFilesRecursive(dir) {
2293
2765
  const files = [];
2294
- const entries = await fs2__default.default.readdir(dir, { withFileTypes: true });
2766
+ const entries = await fs3__namespace.default.readdir(dir, { withFileTypes: true });
2295
2767
  for (const entry of entries) {
2296
- const fullPath = path3__default.default.join(dir, entry.name);
2768
+ const fullPath = path3__namespace.default.join(dir, entry.name);
2297
2769
  if (entry.isDirectory()) {
2298
2770
  files.push(...await this.listFilesRecursive(fullPath));
2299
2771
  } else if (entry.isFile()) {
@@ -2308,12 +2780,12 @@ var CachePersistence = class {
2308
2780
  async clear() {
2309
2781
  let count = 0;
2310
2782
  try {
2311
- const orgs = await fs2__default.default.readdir(this.cacheDir);
2783
+ const orgs = await fs3__namespace.default.readdir(this.cacheDir);
2312
2784
  for (const org of orgs) {
2313
- const orgPath = path3__default.default.join(this.cacheDir, org);
2314
- const stat = await fs2__default.default.stat(orgPath);
2785
+ const orgPath = path3__namespace.default.join(this.cacheDir, org);
2786
+ const stat = await fs3__namespace.default.stat(orgPath);
2315
2787
  if (stat.isDirectory()) {
2316
- await fs2__default.default.rm(orgPath, { recursive: true });
2788
+ await fs3__namespace.default.rm(orgPath, { recursive: true });
2317
2789
  count++;
2318
2790
  }
2319
2791
  }
@@ -2374,7 +2846,7 @@ var CachePersistence = class {
2374
2846
  * Ensure cache directory exists
2375
2847
  */
2376
2848
  async ensureDir() {
2377
- await fs2__default.default.mkdir(this.cacheDir, { recursive: true });
2849
+ await fs3__namespace.default.mkdir(this.cacheDir, { recursive: true });
2378
2850
  }
2379
2851
  /**
2380
2852
  * Get cache directory path
@@ -2422,8 +2894,17 @@ var CacheManager = class {
2422
2894
  * Get content for a reference
2423
2895
  *
2424
2896
  * Implements cache-first strategy with stale-while-revalidate.
2897
+ *
2898
+ * EXCEPTION: File plugin sources (current project files) bypass cache entirely.
2899
+ * They are always read fresh from disk for optimal development experience.
2425
2900
  */
2426
2901
  async get(reference, options) {
2902
+ if (reference.isCurrentProject && reference.sourceType === "file-plugin") {
2903
+ if (!this.storage) {
2904
+ throw new Error("Storage manager not set");
2905
+ }
2906
+ return await this.storage.fetch(reference, options);
2907
+ }
2427
2908
  const ttl = options?.ttl ?? this.config.defaultTtl;
2428
2909
  let entry = this.memoryCache.get(reference.uri);
2429
2910
  if (!entry && this.persistence) {
@@ -2629,13 +3110,18 @@ var CacheManager = class {
2629
3110
  }
2630
3111
  /**
2631
3112
  * Fetch content and store in cache
3113
+ *
3114
+ * EXCEPTION: File plugin sources are not cached (should not reach here,
3115
+ * but added as safety check).
2632
3116
  */
2633
3117
  async fetchAndCache(reference, ttl, options) {
2634
3118
  if (!this.storage) {
2635
3119
  throw new Error("Storage manager not set");
2636
3120
  }
2637
3121
  const result = await this.storage.fetch(reference, options);
2638
- await this.set(reference.uri, result, ttl);
3122
+ if (!(reference.isCurrentProject && reference.sourceType === "file-plugin")) {
3123
+ await this.set(reference.uri, result, ttl);
3124
+ }
2639
3125
  return result;
2640
3126
  }
2641
3127
  /**
@@ -2754,11 +3240,11 @@ var DEFAULT_SYNC_CONFIG = {
2754
3240
  deleteOrphans: false,
2755
3241
  conflictStrategy: "newest"
2756
3242
  };
2757
- function evaluatePath(path6, rules, direction, defaultExcludes = []) {
3243
+ function evaluatePath(path7, rules, direction, defaultExcludes = []) {
2758
3244
  for (const pattern of defaultExcludes) {
2759
- if (micromatch3__default.default.isMatch(path6, pattern)) {
3245
+ if (micromatch3__default.default.isMatch(path7, pattern)) {
2760
3246
  return {
2761
- path: path6,
3247
+ path: path7,
2762
3248
  shouldSync: false,
2763
3249
  reason: `Excluded by default pattern: ${pattern}`
2764
3250
  };
@@ -2769,9 +3255,9 @@ function evaluatePath(path6, rules, direction, defaultExcludes = []) {
2769
3255
  if (rule.direction && rule.direction !== direction) {
2770
3256
  continue;
2771
3257
  }
2772
- if (micromatch3__default.default.isMatch(path6, rule.pattern)) {
3258
+ if (micromatch3__default.default.isMatch(path7, rule.pattern)) {
2773
3259
  return {
2774
- path: path6,
3260
+ path: path7,
2775
3261
  shouldSync: rule.include,
2776
3262
  matchedRule: rule,
2777
3263
  reason: rule.include ? `Included by rule: ${rule.pattern}` : `Excluded by rule: ${rule.pattern}`
@@ -2779,21 +3265,21 @@ function evaluatePath(path6, rules, direction, defaultExcludes = []) {
2779
3265
  }
2780
3266
  }
2781
3267
  return {
2782
- path: path6,
3268
+ path: path7,
2783
3269
  shouldSync: true,
2784
3270
  reason: "No matching rule, included by default"
2785
3271
  };
2786
3272
  }
2787
3273
  function evaluatePaths(paths, rules, direction, defaultExcludes = []) {
2788
3274
  const results = /* @__PURE__ */ new Map();
2789
- for (const path6 of paths) {
2790
- results.set(path6, evaluatePath(path6, rules, direction, defaultExcludes));
3275
+ for (const path7 of paths) {
3276
+ results.set(path7, evaluatePath(path7, rules, direction, defaultExcludes));
2791
3277
  }
2792
3278
  return results;
2793
3279
  }
2794
3280
  function filterSyncablePaths(paths, rules, direction, defaultExcludes = []) {
2795
3281
  return paths.filter(
2796
- (path6) => evaluatePath(path6, rules, direction, defaultExcludes).shouldSync
3282
+ (path7) => evaluatePath(path7, rules, direction, defaultExcludes).shouldSync
2797
3283
  );
2798
3284
  }
2799
3285
  function createRulesFromPatterns(include = [], exclude = []) {
@@ -3059,8 +3545,8 @@ async function scanCodexWithRouting(options) {
3059
3545
  for (const filePath of allFiles) {
3060
3546
  totalScanned++;
3061
3547
  try {
3062
- const fullPath = path3__default.default.join(codexDir, filePath);
3063
- const stats = await fs2__default.default.stat(fullPath);
3548
+ const fullPath = path3__namespace.default.join(codexDir, filePath);
3549
+ const stats = await fs3__namespace.default.stat(fullPath);
3064
3550
  if (stats.size > maxFileSize) {
3065
3551
  totalSkipped++;
3066
3552
  errors.push({
@@ -3156,10 +3642,10 @@ async function listAllFilesRecursive(dirPath) {
3156
3642
  const files = [];
3157
3643
  async function scanDirectory(currentPath, relativePath = "") {
3158
3644
  try {
3159
- const entries = await fs2__default.default.readdir(currentPath, { withFileTypes: true });
3645
+ const entries = await fs3__namespace.default.readdir(currentPath, { withFileTypes: true });
3160
3646
  for (const entry of entries) {
3161
- const entryPath = path3__default.default.join(currentPath, entry.name);
3162
- const entryRelativePath = relativePath ? path3__default.default.join(relativePath, entry.name) : entry.name;
3647
+ const entryPath = path3__namespace.default.join(currentPath, entry.name);
3648
+ const entryRelativePath = relativePath ? path3__namespace.default.join(relativePath, entry.name) : entry.name;
3163
3649
  if (entry.isDirectory()) {
3164
3650
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") {
3165
3651
  continue;
@@ -3398,17 +3884,17 @@ var SyncManager = class {
3398
3884
  if (file.operation === "create" || file.operation === "update") {
3399
3885
  const sourcePath = `${plan.source}/${file.path}`;
3400
3886
  const targetPath = `${plan.target}/${file.path}`;
3401
- const fs4 = await import('fs/promises');
3402
- const path6 = await import('path');
3403
- const targetDir = path6.dirname(targetPath);
3404
- await fs4.mkdir(targetDir, { recursive: true });
3405
- await fs4.copyFile(sourcePath, targetPath);
3887
+ const fs5 = await import('fs/promises');
3888
+ const path7 = await import('path');
3889
+ const targetDir = path7.dirname(targetPath);
3890
+ await fs5.mkdir(targetDir, { recursive: true });
3891
+ await fs5.copyFile(sourcePath, targetPath);
3406
3892
  synced++;
3407
3893
  } else if (file.operation === "delete") {
3408
3894
  const targetPath = `${plan.target}/${file.path}`;
3409
- const fs4 = await import('fs/promises');
3895
+ const fs5 = await import('fs/promises');
3410
3896
  try {
3411
- await fs4.unlink(targetPath);
3897
+ await fs5.unlink(targetPath);
3412
3898
  synced++;
3413
3899
  } catch (error) {
3414
3900
  if (error.code !== "ENOENT") {
@@ -3463,15 +3949,15 @@ var SyncManager = class {
3463
3949
  /**
3464
3950
  * Get sync status for a file
3465
3951
  */
3466
- async getFileStatus(path6) {
3952
+ async getFileStatus(path7) {
3467
3953
  const manifest = await this.loadManifest();
3468
- return manifest?.entries[path6] ?? null;
3954
+ return manifest?.entries[path7] ?? null;
3469
3955
  }
3470
3956
  /**
3471
3957
  * Check if a file is synced
3472
3958
  */
3473
- async isFileSynced(path6) {
3474
- const status = await this.getFileStatus(path6);
3959
+ async isFileSynced(path7) {
3960
+ const status = await this.getFileStatus(path7);
3475
3961
  return status !== null;
3476
3962
  }
3477
3963
  /**
@@ -3555,19 +4041,19 @@ function ruleMatchesContext(rule, context) {
3555
4041
  }
3556
4042
  return true;
3557
4043
  }
3558
- function ruleMatchesPath(rule, path6) {
3559
- return micromatch3__default.default.isMatch(path6, rule.pattern);
4044
+ function ruleMatchesPath(rule, path7) {
4045
+ return micromatch3__default.default.isMatch(path7, rule.pattern);
3560
4046
  }
3561
4047
  function ruleMatchesAction(rule, action) {
3562
4048
  return rule.actions.includes(action);
3563
4049
  }
3564
- function evaluatePermission(path6, action, context, config) {
4050
+ function evaluatePermission(path7, action, context, config) {
3565
4051
  const sortedRules = [...config.rules].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
3566
4052
  for (const rule of sortedRules) {
3567
4053
  if (!ruleMatchesContext(rule, context)) {
3568
4054
  continue;
3569
4055
  }
3570
- if (!ruleMatchesPath(rule, path6)) {
4056
+ if (!ruleMatchesPath(rule, path7)) {
3571
4057
  continue;
3572
4058
  }
3573
4059
  if (!ruleMatchesAction(rule, action)) {
@@ -3586,24 +4072,24 @@ function evaluatePermission(path6, action, context, config) {
3586
4072
  reason: config.defaultAllow ? "Allowed by default" : "Denied by default"
3587
4073
  };
3588
4074
  }
3589
- function isAllowed(path6, action, context, config) {
3590
- const result = evaluatePermission(path6, action, context, config);
4075
+ function isAllowed(path7, action, context, config) {
4076
+ const result = evaluatePermission(path7, action, context, config);
3591
4077
  return result.allowed;
3592
4078
  }
3593
- function hasPermission(path6, action, requiredLevel, context, config) {
3594
- const result = evaluatePermission(path6, action, context, config);
4079
+ function hasPermission(path7, action, requiredLevel, context, config) {
4080
+ const result = evaluatePermission(path7, action, context, config);
3595
4081
  return levelGrants(result.level, requiredLevel);
3596
4082
  }
3597
4083
  function evaluatePermissions(paths, action, context, config) {
3598
4084
  const results = /* @__PURE__ */ new Map();
3599
- for (const path6 of paths) {
3600
- results.set(path6, evaluatePermission(path6, action, context, config));
4085
+ for (const path7 of paths) {
4086
+ results.set(path7, evaluatePermission(path7, action, context, config));
3601
4087
  }
3602
4088
  return results;
3603
4089
  }
3604
4090
  function filterByPermission(paths, action, context, config, requiredLevel = "read") {
3605
- return paths.filter((path6) => {
3606
- const result = evaluatePermission(path6, action, context, config);
4091
+ return paths.filter((path7) => {
4092
+ const result = evaluatePermission(path7, action, context, config);
3607
4093
  return levelGrants(result.level, requiredLevel);
3608
4094
  });
3609
4095
  }
@@ -3694,21 +4180,21 @@ var PermissionManager = class {
3694
4180
  /**
3695
4181
  * Check if an action is allowed for a path
3696
4182
  */
3697
- isAllowed(path6, action, context) {
3698
- const result = this.evaluate(path6, action, context);
4183
+ isAllowed(path7, action, context) {
4184
+ const result = this.evaluate(path7, action, context);
3699
4185
  return result.allowed;
3700
4186
  }
3701
4187
  /**
3702
4188
  * Check if a permission level is granted
3703
4189
  */
3704
- hasPermission(path6, action, requiredLevel, context) {
3705
- const result = this.evaluate(path6, action, context);
4190
+ hasPermission(path7, action, requiredLevel, context) {
4191
+ const result = this.evaluate(path7, action, context);
3706
4192
  return levelGrants(result.level, requiredLevel);
3707
4193
  }
3708
4194
  /**
3709
4195
  * Evaluate permission for a path and action
3710
4196
  */
3711
- evaluate(path6, action, context) {
4197
+ evaluate(path7, action, context) {
3712
4198
  const mergedContext = { ...this.defaultContext, ...context };
3713
4199
  if (!this.config.enforced) {
3714
4200
  return {
@@ -3717,7 +4203,7 @@ var PermissionManager = class {
3717
4203
  reason: "Permissions not enforced"
3718
4204
  };
3719
4205
  }
3720
- return evaluatePermission(path6, action, mergedContext, this.config);
4206
+ return evaluatePermission(path7, action, mergedContext, this.config);
3721
4207
  }
3722
4208
  /**
3723
4209
  * Filter paths by permission
@@ -3820,12 +4306,12 @@ var PermissionManager = class {
3820
4306
  /**
3821
4307
  * Assert permission (throws if denied)
3822
4308
  */
3823
- assertPermission(path6, action, requiredLevel = "read", context) {
3824
- const result = this.evaluate(path6, action, context);
4309
+ assertPermission(path7, action, requiredLevel = "read", context) {
4310
+ const result = this.evaluate(path7, action, context);
3825
4311
  if (!levelGrants(result.level, requiredLevel)) {
3826
4312
  throw new PermissionDeniedError(
3827
- `Permission denied for ${action} on ${path6}: ${result.reason}`,
3828
- path6,
4313
+ `Permission denied for ${action} on ${path7}: ${result.reason}`,
4314
+ path7,
3829
4315
  action,
3830
4316
  result
3831
4317
  );
@@ -3833,9 +4319,9 @@ var PermissionManager = class {
3833
4319
  }
3834
4320
  };
3835
4321
  var PermissionDeniedError = class extends Error {
3836
- constructor(message, path6, action, result) {
4322
+ constructor(message, path7, action, result) {
3837
4323
  super(message);
3838
- this.path = path6;
4324
+ this.path = path7;
3839
4325
  this.action = action;
3840
4326
  this.result = result;
3841
4327
  this.name = "PermissionDeniedError";
@@ -4332,8 +4818,8 @@ function convertToUri(reference, options = {}) {
4332
4818
  const parts = parseReference2(trimmed);
4333
4819
  const org = parts.org || options.defaultOrg || "_";
4334
4820
  const project = parts.project || options.defaultProject || "_";
4335
- const path6 = parts.path;
4336
- return `codex://${org}/${project}/${path6}`;
4821
+ const path7 = parts.path;
4822
+ return `codex://${org}/${project}/${path7}`;
4337
4823
  }
4338
4824
  function parseReference2(reference) {
4339
4825
  const trimmed = reference.trim();
@@ -4429,6 +4915,8 @@ exports.DEFAULT_MIGRATION_OPTIONS = DEFAULT_MIGRATION_OPTIONS;
4429
4915
  exports.DEFAULT_PERMISSION_CONFIG = DEFAULT_PERMISSION_CONFIG;
4430
4916
  exports.DEFAULT_SYNC_CONFIG = DEFAULT_SYNC_CONFIG;
4431
4917
  exports.DEFAULT_TYPE = DEFAULT_TYPE;
4918
+ exports.FilePluginFileNotFoundError = FilePluginFileNotFoundError;
4919
+ exports.FilePluginStorage = FilePluginStorage;
4432
4920
  exports.GitHubStorage = GitHubStorage;
4433
4921
  exports.HttpStorage = HttpStorage;
4434
4922
  exports.LEGACY_PATTERNS = LEGACY_PATTERNS;