@fractary/codex 0.8.0 → 0.9.0
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 +524 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +502 -48
- package/dist/index.d.ts +502 -48
- package/dist/index.js +506 -122
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import micromatch3 from 'micromatch';
|
|
2
|
-
import path3 from 'path';
|
|
2
|
+
import * as path3 from 'path';
|
|
3
|
+
import path3__default from 'path';
|
|
3
4
|
import { execFile, execSync } from 'child_process';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import yaml from 'js-yaml';
|
|
6
|
-
import
|
|
7
|
+
import * as fs3 from 'fs/promises';
|
|
8
|
+
import fs3__default from 'fs/promises';
|
|
7
9
|
import { promisify } from 'util';
|
|
8
10
|
|
|
9
11
|
var __defProp = Object.defineProperty;
|
|
@@ -161,7 +163,7 @@ function validatePath(filePath) {
|
|
|
161
163
|
return false;
|
|
162
164
|
}
|
|
163
165
|
}
|
|
164
|
-
const normalized =
|
|
166
|
+
const normalized = path3__default.normalize(filePath);
|
|
165
167
|
if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("\\..\\")) {
|
|
166
168
|
return false;
|
|
167
169
|
}
|
|
@@ -179,7 +181,7 @@ function sanitizePath(filePath) {
|
|
|
179
181
|
}
|
|
180
182
|
sanitized = sanitized.replace(/^\/+/, "");
|
|
181
183
|
sanitized = sanitized.replace(/^~\//, "");
|
|
182
|
-
sanitized =
|
|
184
|
+
sanitized = path3__default.normalize(sanitized);
|
|
183
185
|
sanitized = sanitized.replace(/\.\.\//g, "").replace(/\.\./g, "");
|
|
184
186
|
sanitized = sanitized.replace(/^\.\//, "");
|
|
185
187
|
sanitized = sanitized.replace(/\0/g, "");
|
|
@@ -280,10 +282,10 @@ function parseReference(uri, options = {}) {
|
|
|
280
282
|
path: filePath
|
|
281
283
|
};
|
|
282
284
|
}
|
|
283
|
-
function buildUri(org, project,
|
|
285
|
+
function buildUri(org, project, path7) {
|
|
284
286
|
const base = `${CODEX_URI_PREFIX}${org}/${project}`;
|
|
285
|
-
if (
|
|
286
|
-
const cleanPath =
|
|
287
|
+
if (path7) {
|
|
288
|
+
const cleanPath = path7.startsWith("/") ? path7.slice(1) : path7;
|
|
287
289
|
return `${base}/${cleanPath}`;
|
|
288
290
|
}
|
|
289
291
|
return base;
|
|
@@ -354,7 +356,7 @@ function getCurrentContext(options = {}) {
|
|
|
354
356
|
return detectCurrentProject(options.cwd);
|
|
355
357
|
}
|
|
356
358
|
function calculateCachePath(org, project, filePath, cacheDir) {
|
|
357
|
-
return
|
|
359
|
+
return path3__default.join(cacheDir, org, project, filePath);
|
|
358
360
|
}
|
|
359
361
|
function resolveReference(uri, options = {}) {
|
|
360
362
|
const parsed = parseReference(uri);
|
|
@@ -363,7 +365,7 @@ function resolveReference(uri, options = {}) {
|
|
|
363
365
|
}
|
|
364
366
|
const cacheDir = options.cacheDir || DEFAULT_CACHE_DIR;
|
|
365
367
|
const currentContext = getCurrentContext(options);
|
|
366
|
-
const cachePath = parsed.path ? calculateCachePath(parsed.org, parsed.project, parsed.path, cacheDir) :
|
|
368
|
+
const cachePath = parsed.path ? calculateCachePath(parsed.org, parsed.project, parsed.path, cacheDir) : path3__default.join(cacheDir, parsed.org, parsed.project);
|
|
367
369
|
const isCurrentProject = currentContext.org === parsed.org && currentContext.project === parsed.project;
|
|
368
370
|
const resolved = {
|
|
369
371
|
...parsed,
|
|
@@ -372,9 +374,45 @@ function resolveReference(uri, options = {}) {
|
|
|
372
374
|
};
|
|
373
375
|
if (isCurrentProject && parsed.path) {
|
|
374
376
|
resolved.localPath = parsed.path;
|
|
377
|
+
if (options.config) {
|
|
378
|
+
const fileSource = detectFilePluginSource(parsed.path, options.config);
|
|
379
|
+
if (fileSource) {
|
|
380
|
+
resolved.sourceType = "file-plugin";
|
|
381
|
+
resolved.filePluginSource = fileSource.name;
|
|
382
|
+
resolved.localPath = fileSource.fullPath;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
375
385
|
}
|
|
376
386
|
return resolved;
|
|
377
387
|
}
|
|
388
|
+
function detectFilePluginSource(filePath, config) {
|
|
389
|
+
if (!config?.file?.sources) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
for (const [sourceName, source] of Object.entries(config.file.sources)) {
|
|
393
|
+
const basePath = source.local?.base_path;
|
|
394
|
+
if (!basePath) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const normalizedPath = filePath.replace(/^\.\//, "").replace(/^\//, "");
|
|
398
|
+
const normalizedBasePath = basePath.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
|
|
399
|
+
if (normalizedPath.startsWith(normalizedBasePath)) {
|
|
400
|
+
return {
|
|
401
|
+
name: sourceName,
|
|
402
|
+
fullPath: path3__default.join(basePath, normalizedPath.substring(normalizedBasePath.length).replace(/^\//, ""))
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const sourceNameInPath = normalizedBasePath.split("/").pop();
|
|
406
|
+
if (sourceNameInPath && normalizedPath.startsWith(sourceNameInPath + "/")) {
|
|
407
|
+
const pathWithoutSource = normalizedPath.substring(sourceNameInPath.length + 1);
|
|
408
|
+
return {
|
|
409
|
+
name: sourceName,
|
|
410
|
+
fullPath: path3__default.join(basePath, pathWithoutSource)
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
378
416
|
function resolveReferences(uris, options = {}) {
|
|
379
417
|
return uris.map((uri) => resolveReference(uri, options)).filter((r) => r !== null);
|
|
380
418
|
}
|
|
@@ -388,9 +426,9 @@ function getRelativeCachePath(uri) {
|
|
|
388
426
|
return null;
|
|
389
427
|
}
|
|
390
428
|
if (parsed.path) {
|
|
391
|
-
return
|
|
429
|
+
return path3__default.join(parsed.org, parsed.project, parsed.path);
|
|
392
430
|
}
|
|
393
|
-
return
|
|
431
|
+
return path3__default.join(parsed.org, parsed.project);
|
|
394
432
|
}
|
|
395
433
|
|
|
396
434
|
// src/types/built-in.ts
|
|
@@ -858,6 +896,41 @@ var CodexConfigSchema = z.object({
|
|
|
858
896
|
// Archive configuration
|
|
859
897
|
archive: ArchiveConfigSchema.optional()
|
|
860
898
|
}).strict();
|
|
899
|
+
var FileSourceSchema = z.object({
|
|
900
|
+
type: z.enum(["s3", "r2", "gcs", "local"]),
|
|
901
|
+
bucket: z.string().optional(),
|
|
902
|
+
prefix: z.string().optional(),
|
|
903
|
+
region: z.string().optional(),
|
|
904
|
+
local: z.object({
|
|
905
|
+
base_path: z.string()
|
|
906
|
+
}),
|
|
907
|
+
push: z.object({
|
|
908
|
+
compress: z.boolean().optional(),
|
|
909
|
+
keep_local: z.boolean().optional()
|
|
910
|
+
}).optional(),
|
|
911
|
+
auth: z.object({
|
|
912
|
+
profile: z.string().optional()
|
|
913
|
+
}).optional()
|
|
914
|
+
}).refine(
|
|
915
|
+
(data) => {
|
|
916
|
+
if (data.type !== "local" && !data.bucket) {
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
return true;
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
message: "Bucket is required for s3, r2, and gcs storage types",
|
|
923
|
+
path: ["bucket"]
|
|
924
|
+
}
|
|
925
|
+
);
|
|
926
|
+
var FileConfigSchema = z.object({
|
|
927
|
+
schema_version: z.string(),
|
|
928
|
+
sources: z.record(FileSourceSchema)
|
|
929
|
+
});
|
|
930
|
+
z.object({
|
|
931
|
+
file: FileConfigSchema.optional(),
|
|
932
|
+
codex: CodexConfigSchema.optional()
|
|
933
|
+
});
|
|
861
934
|
function parseMetadata(content, options = {}) {
|
|
862
935
|
const { strict = true, normalize = true } = options;
|
|
863
936
|
const normalizedContent = normalize ? content.replace(/\r\n/g, "\n") : content;
|
|
@@ -1187,18 +1260,18 @@ function parseCustomDestination(value) {
|
|
|
1187
1260
|
);
|
|
1188
1261
|
}
|
|
1189
1262
|
const repo = value.substring(0, colonIndex).trim();
|
|
1190
|
-
const
|
|
1263
|
+
const path7 = value.substring(colonIndex + 1).trim();
|
|
1191
1264
|
if (!repo) {
|
|
1192
1265
|
throw new ValidationError(
|
|
1193
1266
|
`Invalid custom destination: repository name cannot be empty in "${value}"`
|
|
1194
1267
|
);
|
|
1195
1268
|
}
|
|
1196
|
-
if (!
|
|
1269
|
+
if (!path7) {
|
|
1197
1270
|
throw new ValidationError(
|
|
1198
1271
|
`Invalid custom destination: path cannot be empty in "${value}"`
|
|
1199
1272
|
);
|
|
1200
1273
|
}
|
|
1201
|
-
return { repo, path:
|
|
1274
|
+
return { repo, path: path7 };
|
|
1202
1275
|
}
|
|
1203
1276
|
function getCustomSyncDestinations(metadata) {
|
|
1204
1277
|
const customDestinations = metadata.codex_sync_custom;
|
|
@@ -1234,8 +1307,8 @@ function mergeFetchOptions(options) {
|
|
|
1234
1307
|
...options
|
|
1235
1308
|
};
|
|
1236
1309
|
}
|
|
1237
|
-
function detectContentType(
|
|
1238
|
-
const ext =
|
|
1310
|
+
function detectContentType(path7) {
|
|
1311
|
+
const ext = path7.split(".").pop()?.toLowerCase();
|
|
1239
1312
|
const mimeTypes = {
|
|
1240
1313
|
md: "text/markdown",
|
|
1241
1314
|
markdown: "text/markdown",
|
|
@@ -1295,19 +1368,19 @@ var LocalStorage = class {
|
|
|
1295
1368
|
if (!reference.localPath) {
|
|
1296
1369
|
throw new Error(`No local path for reference: ${reference.uri}`);
|
|
1297
1370
|
}
|
|
1298
|
-
const fullPath =
|
|
1371
|
+
const fullPath = path3__default.isAbsolute(reference.localPath) ? reference.localPath : path3__default.join(this.baseDir, reference.localPath);
|
|
1299
1372
|
try {
|
|
1300
|
-
await
|
|
1373
|
+
await fs3__default.access(fullPath);
|
|
1301
1374
|
} catch {
|
|
1302
1375
|
throw new Error(`File not found: ${fullPath}`);
|
|
1303
1376
|
}
|
|
1304
|
-
const stats = await
|
|
1377
|
+
const stats = await fs3__default.stat(fullPath);
|
|
1305
1378
|
if (stats.size > opts.maxSize) {
|
|
1306
1379
|
throw new Error(
|
|
1307
1380
|
`File too large: ${stats.size} bytes (max: ${opts.maxSize} bytes)`
|
|
1308
1381
|
);
|
|
1309
1382
|
}
|
|
1310
|
-
const content = await
|
|
1383
|
+
const content = await fs3__default.readFile(fullPath);
|
|
1311
1384
|
return {
|
|
1312
1385
|
content,
|
|
1313
1386
|
contentType: detectContentType(reference.localPath),
|
|
@@ -1326,9 +1399,9 @@ var LocalStorage = class {
|
|
|
1326
1399
|
if (!reference.localPath) {
|
|
1327
1400
|
return false;
|
|
1328
1401
|
}
|
|
1329
|
-
const fullPath =
|
|
1402
|
+
const fullPath = path3__default.isAbsolute(reference.localPath) ? reference.localPath : path3__default.join(this.baseDir, reference.localPath);
|
|
1330
1403
|
try {
|
|
1331
|
-
await
|
|
1404
|
+
await fs3__default.access(fullPath);
|
|
1332
1405
|
return true;
|
|
1333
1406
|
} catch {
|
|
1334
1407
|
return false;
|
|
@@ -1338,24 +1411,24 @@ var LocalStorage = class {
|
|
|
1338
1411
|
* Read file content as string
|
|
1339
1412
|
*/
|
|
1340
1413
|
async readText(filePath) {
|
|
1341
|
-
const fullPath =
|
|
1342
|
-
return
|
|
1414
|
+
const fullPath = path3__default.isAbsolute(filePath) ? filePath : path3__default.join(this.baseDir, filePath);
|
|
1415
|
+
return fs3__default.readFile(fullPath, "utf-8");
|
|
1343
1416
|
}
|
|
1344
1417
|
/**
|
|
1345
1418
|
* Write content to file
|
|
1346
1419
|
*/
|
|
1347
1420
|
async write(filePath, content) {
|
|
1348
|
-
const fullPath =
|
|
1349
|
-
await
|
|
1350
|
-
await
|
|
1421
|
+
const fullPath = path3__default.isAbsolute(filePath) ? filePath : path3__default.join(this.baseDir, filePath);
|
|
1422
|
+
await fs3__default.mkdir(path3__default.dirname(fullPath), { recursive: true });
|
|
1423
|
+
await fs3__default.writeFile(fullPath, content);
|
|
1351
1424
|
}
|
|
1352
1425
|
/**
|
|
1353
1426
|
* Delete a file
|
|
1354
1427
|
*/
|
|
1355
1428
|
async delete(filePath) {
|
|
1356
|
-
const fullPath =
|
|
1429
|
+
const fullPath = path3__default.isAbsolute(filePath) ? filePath : path3__default.join(this.baseDir, filePath);
|
|
1357
1430
|
try {
|
|
1358
|
-
await
|
|
1431
|
+
await fs3__default.unlink(fullPath);
|
|
1359
1432
|
return true;
|
|
1360
1433
|
} catch {
|
|
1361
1434
|
return false;
|
|
@@ -1365,10 +1438,10 @@ var LocalStorage = class {
|
|
|
1365
1438
|
* List files in a directory
|
|
1366
1439
|
*/
|
|
1367
1440
|
async list(dirPath) {
|
|
1368
|
-
const fullPath =
|
|
1441
|
+
const fullPath = path3__default.isAbsolute(dirPath) ? dirPath : path3__default.join(this.baseDir, dirPath);
|
|
1369
1442
|
try {
|
|
1370
|
-
const entries = await
|
|
1371
|
-
return entries.filter((e) => e.isFile()).map((e) =>
|
|
1443
|
+
const entries = await fs3__default.readdir(fullPath, { withFileTypes: true });
|
|
1444
|
+
return entries.filter((e) => e.isFile()).map((e) => path3__default.join(dirPath, e.name));
|
|
1372
1445
|
} catch {
|
|
1373
1446
|
return [];
|
|
1374
1447
|
}
|
|
@@ -1713,6 +1786,300 @@ var HttpStorage = class {
|
|
|
1713
1786
|
function createHttpStorage(options) {
|
|
1714
1787
|
return new HttpStorage(options);
|
|
1715
1788
|
}
|
|
1789
|
+
|
|
1790
|
+
// src/file-integration/source-resolver.ts
|
|
1791
|
+
var FileSourceResolver = class {
|
|
1792
|
+
constructor(config) {
|
|
1793
|
+
this.config = config;
|
|
1794
|
+
this.initializeSources();
|
|
1795
|
+
}
|
|
1796
|
+
sources = /* @__PURE__ */ new Map();
|
|
1797
|
+
/**
|
|
1798
|
+
* Initialize sources from config
|
|
1799
|
+
*/
|
|
1800
|
+
initializeSources() {
|
|
1801
|
+
if (!this.config.file?.sources) {
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
for (const [name, sourceConfig] of Object.entries(this.config.file.sources)) {
|
|
1805
|
+
const resolved = {
|
|
1806
|
+
name,
|
|
1807
|
+
type: "file-plugin",
|
|
1808
|
+
localPath: sourceConfig.local.base_path,
|
|
1809
|
+
isCurrentProject: true,
|
|
1810
|
+
config: sourceConfig
|
|
1811
|
+
};
|
|
1812
|
+
if (sourceConfig.bucket && sourceConfig.type !== "local") {
|
|
1813
|
+
resolved.bucket = sourceConfig.bucket;
|
|
1814
|
+
resolved.prefix = sourceConfig.prefix;
|
|
1815
|
+
resolved.remotePath = this.buildRemotePath(sourceConfig);
|
|
1816
|
+
}
|
|
1817
|
+
this.sources.set(name, resolved);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Build remote path from source config
|
|
1822
|
+
*/
|
|
1823
|
+
buildRemotePath(source) {
|
|
1824
|
+
const protocol = source.type === "s3" ? "s3://" : source.type === "r2" ? "r2://" : "gcs://";
|
|
1825
|
+
const bucket = source.bucket;
|
|
1826
|
+
const prefix = source.prefix ? `/${source.prefix}` : "";
|
|
1827
|
+
return `${protocol}${bucket}${prefix}`;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Get all available file plugin sources
|
|
1831
|
+
*
|
|
1832
|
+
* @returns Array of resolved file sources
|
|
1833
|
+
*/
|
|
1834
|
+
getAvailableSources() {
|
|
1835
|
+
return Array.from(this.sources.values());
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Resolve a source by name
|
|
1839
|
+
*
|
|
1840
|
+
* @param name - Source name (e.g., "specs", "logs")
|
|
1841
|
+
* @returns Resolved source or null if not found
|
|
1842
|
+
*/
|
|
1843
|
+
resolveSource(name) {
|
|
1844
|
+
return this.sources.get(name) || null;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Check if a path belongs to any file plugin source
|
|
1848
|
+
*
|
|
1849
|
+
* @param path - File path to check
|
|
1850
|
+
* @returns True if path matches any source's base_path
|
|
1851
|
+
*/
|
|
1852
|
+
isFilePluginPath(path7) {
|
|
1853
|
+
return this.getSourceForPath(path7) !== null;
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Get the source for a given path
|
|
1857
|
+
*
|
|
1858
|
+
* Matches path against all source base_paths and returns the matching source.
|
|
1859
|
+
* Uses longest-match strategy if multiple sources match.
|
|
1860
|
+
*
|
|
1861
|
+
* @param path - File path to match
|
|
1862
|
+
* @returns Matching source or null
|
|
1863
|
+
*/
|
|
1864
|
+
getSourceForPath(path7) {
|
|
1865
|
+
let bestMatch = null;
|
|
1866
|
+
let bestMatchLength = 0;
|
|
1867
|
+
for (const source of this.sources.values()) {
|
|
1868
|
+
const normalizedPath = this.normalizePath(path7);
|
|
1869
|
+
const normalizedBasePath = this.normalizePath(source.localPath);
|
|
1870
|
+
if (normalizedPath.startsWith(normalizedBasePath)) {
|
|
1871
|
+
const matchLength = normalizedBasePath.length;
|
|
1872
|
+
if (matchLength > bestMatchLength) {
|
|
1873
|
+
bestMatch = source;
|
|
1874
|
+
bestMatchLength = matchLength;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
return bestMatch;
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Normalize path for comparison
|
|
1882
|
+
* - Remove leading "./" or "/"
|
|
1883
|
+
* - Remove trailing "/"
|
|
1884
|
+
* - Convert to lowercase for case-insensitive comparison
|
|
1885
|
+
*/
|
|
1886
|
+
normalizePath(path7) {
|
|
1887
|
+
return path7.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "").toLowerCase();
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Get source names
|
|
1891
|
+
*
|
|
1892
|
+
* @returns Array of source names
|
|
1893
|
+
*/
|
|
1894
|
+
getSourceNames() {
|
|
1895
|
+
return Array.from(this.sources.keys());
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Check if sources are configured
|
|
1899
|
+
*
|
|
1900
|
+
* @returns True if any file plugin sources are configured
|
|
1901
|
+
*/
|
|
1902
|
+
hasSources() {
|
|
1903
|
+
return this.sources.size > 0;
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
// src/storage/errors.ts
|
|
1908
|
+
var FilePluginFileNotFoundError = class _FilePluginFileNotFoundError extends Error {
|
|
1909
|
+
constructor(filePath, sourceName, options) {
|
|
1910
|
+
const includeCloudSuggestions = options?.includeCloudSuggestions !== false;
|
|
1911
|
+
const storageType = options?.storageType;
|
|
1912
|
+
let message = `File not found: ${filePath}
|
|
1913
|
+
|
|
1914
|
+
`;
|
|
1915
|
+
if (includeCloudSuggestions) {
|
|
1916
|
+
if (storageType) {
|
|
1917
|
+
message += `This file may be in cloud storage (${storageType}).
|
|
1918
|
+
|
|
1919
|
+
`;
|
|
1920
|
+
} else {
|
|
1921
|
+
message += `This file may not have been synced from remote storage yet.
|
|
1922
|
+
`;
|
|
1923
|
+
}
|
|
1924
|
+
message += `To fetch from cloud storage, run:
|
|
1925
|
+
`;
|
|
1926
|
+
message += ` file pull ${sourceName}
|
|
1927
|
+
|
|
1928
|
+
`;
|
|
1929
|
+
message += `Or sync all sources:
|
|
1930
|
+
`;
|
|
1931
|
+
message += ` file sync`;
|
|
1932
|
+
} else {
|
|
1933
|
+
message += `Please ensure the file exists locally or pull it from cloud storage.`;
|
|
1934
|
+
}
|
|
1935
|
+
super(message);
|
|
1936
|
+
this.filePath = filePath;
|
|
1937
|
+
this.sourceName = sourceName;
|
|
1938
|
+
this.name = "FilePluginFileNotFoundError";
|
|
1939
|
+
if (Error.captureStackTrace) {
|
|
1940
|
+
Error.captureStackTrace(this, _FilePluginFileNotFoundError);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
|
|
1945
|
+
// src/storage/file-plugin.ts
|
|
1946
|
+
var FilePluginStorage = class {
|
|
1947
|
+
constructor(options) {
|
|
1948
|
+
this.options = options;
|
|
1949
|
+
this.sourceResolver = new FileSourceResolver(options.config);
|
|
1950
|
+
this.baseDir = options.baseDir || process.cwd();
|
|
1951
|
+
}
|
|
1952
|
+
name = "file-plugin";
|
|
1953
|
+
type = "local";
|
|
1954
|
+
// Reuse local type for compatibility
|
|
1955
|
+
sourceResolver;
|
|
1956
|
+
baseDir;
|
|
1957
|
+
/**
|
|
1958
|
+
* Check if this provider can handle the reference
|
|
1959
|
+
*
|
|
1960
|
+
* Only handles:
|
|
1961
|
+
* - Current project references
|
|
1962
|
+
* - With sourceType === 'file-plugin'
|
|
1963
|
+
*
|
|
1964
|
+
* @param reference - Resolved reference
|
|
1965
|
+
* @returns True if this provider can handle the reference
|
|
1966
|
+
*/
|
|
1967
|
+
canHandle(reference) {
|
|
1968
|
+
return reference.isCurrentProject && reference.sourceType === "file-plugin";
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Fetch content for a reference
|
|
1972
|
+
*
|
|
1973
|
+
* Reads from local filesystem based on the resolved local path.
|
|
1974
|
+
* If file not found and S3 fallback is enabled, throws helpful error.
|
|
1975
|
+
*
|
|
1976
|
+
* @param reference - Resolved reference
|
|
1977
|
+
* @param options - Fetch options (unused for local reads)
|
|
1978
|
+
* @returns Fetch result with content
|
|
1979
|
+
*/
|
|
1980
|
+
async fetch(reference, _options) {
|
|
1981
|
+
if (!reference.localPath) {
|
|
1982
|
+
throw new Error(`File plugin reference missing localPath: ${reference.uri}`);
|
|
1983
|
+
}
|
|
1984
|
+
if (!reference.filePluginSource) {
|
|
1985
|
+
throw new Error(`File plugin reference missing source name: ${reference.uri}`);
|
|
1986
|
+
}
|
|
1987
|
+
const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
|
|
1988
|
+
const source = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
1989
|
+
if (source) {
|
|
1990
|
+
const allowedDir = path3.resolve(this.baseDir, source.localPath);
|
|
1991
|
+
if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
|
|
1992
|
+
throw new Error(
|
|
1993
|
+
`Path traversal detected: ${reference.localPath} resolves outside allowed directory ${source.localPath}`
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
try {
|
|
1998
|
+
const content = await fs3.readFile(absolutePath);
|
|
1999
|
+
const contentType = this.detectContentType(absolutePath);
|
|
2000
|
+
return {
|
|
2001
|
+
content,
|
|
2002
|
+
contentType,
|
|
2003
|
+
size: content.length,
|
|
2004
|
+
source: "file-plugin",
|
|
2005
|
+
metadata: {
|
|
2006
|
+
filePluginSource: reference.filePluginSource,
|
|
2007
|
+
localPath: absolutePath
|
|
2008
|
+
}
|
|
2009
|
+
};
|
|
2010
|
+
} catch (error) {
|
|
2011
|
+
if (error.code === "ENOENT") {
|
|
2012
|
+
const source2 = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
2013
|
+
throw this.createFileNotFoundError(reference, source2);
|
|
2014
|
+
}
|
|
2015
|
+
throw error;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Check if a reference exists
|
|
2020
|
+
*
|
|
2021
|
+
* @param reference - Resolved reference
|
|
2022
|
+
* @param options - Fetch options (unused)
|
|
2023
|
+
* @returns True if file exists
|
|
2024
|
+
*/
|
|
2025
|
+
async exists(reference, _options) {
|
|
2026
|
+
if (!reference.localPath) {
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
|
|
2030
|
+
if (reference.filePluginSource) {
|
|
2031
|
+
const source = this.sourceResolver.resolveSource(reference.filePluginSource);
|
|
2032
|
+
if (source) {
|
|
2033
|
+
const allowedDir = path3.resolve(this.baseDir, source.localPath);
|
|
2034
|
+
if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
try {
|
|
2040
|
+
await fs3.access(absolutePath);
|
|
2041
|
+
return true;
|
|
2042
|
+
} catch {
|
|
2043
|
+
return false;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Detect content type from file extension
|
|
2048
|
+
*/
|
|
2049
|
+
detectContentType(filePath) {
|
|
2050
|
+
const ext = path3.extname(filePath).toLowerCase();
|
|
2051
|
+
const mimeTypes = {
|
|
2052
|
+
".md": "text/markdown",
|
|
2053
|
+
".txt": "text/plain",
|
|
2054
|
+
".json": "application/json",
|
|
2055
|
+
".yaml": "text/yaml",
|
|
2056
|
+
".yml": "text/yaml",
|
|
2057
|
+
".html": "text/html",
|
|
2058
|
+
".xml": "application/xml",
|
|
2059
|
+
".log": "text/plain",
|
|
2060
|
+
".js": "application/javascript",
|
|
2061
|
+
".ts": "application/typescript",
|
|
2062
|
+
".py": "text/x-python",
|
|
2063
|
+
".sh": "application/x-sh"
|
|
2064
|
+
};
|
|
2065
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Create a helpful error message when file is not found
|
|
2069
|
+
*/
|
|
2070
|
+
createFileNotFoundError(reference, source) {
|
|
2071
|
+
const includeCloudSuggestions = this.options.enableS3Fallback !== false;
|
|
2072
|
+
const storageType = source?.config.type;
|
|
2073
|
+
return new FilePluginFileNotFoundError(
|
|
2074
|
+
reference.localPath || reference.path || "",
|
|
2075
|
+
reference.filePluginSource || "",
|
|
2076
|
+
{
|
|
2077
|
+
includeCloudSuggestions,
|
|
2078
|
+
storageType
|
|
2079
|
+
}
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
1716
2083
|
var execFileAsync = promisify(execFile);
|
|
1717
2084
|
async function execFileNoThrow(command, args = [], options) {
|
|
1718
2085
|
try {
|
|
@@ -1862,10 +2229,10 @@ var S3ArchiveStorage = class {
|
|
|
1862
2229
|
*
|
|
1863
2230
|
* Used to organize archives by type
|
|
1864
2231
|
*/
|
|
1865
|
-
detectType(
|
|
1866
|
-
if (
|
|
1867
|
-
if (
|
|
1868
|
-
if (
|
|
2232
|
+
detectType(path7) {
|
|
2233
|
+
if (path7.startsWith("specs/")) return "specs";
|
|
2234
|
+
if (path7.startsWith("docs/")) return "docs";
|
|
2235
|
+
if (path7.includes("/logs/")) return "logs";
|
|
1869
2236
|
return "misc";
|
|
1870
2237
|
}
|
|
1871
2238
|
/**
|
|
@@ -1876,9 +2243,9 @@ var S3ArchiveStorage = class {
|
|
|
1876
2243
|
* - *.md (all markdown files)
|
|
1877
2244
|
* - docs/*.md (markdown files in docs/)
|
|
1878
2245
|
*/
|
|
1879
|
-
matchesPatterns(
|
|
2246
|
+
matchesPatterns(path7, patterns) {
|
|
1880
2247
|
for (const pattern of patterns) {
|
|
1881
|
-
if (this.matchesPattern(
|
|
2248
|
+
if (this.matchesPattern(path7, pattern)) {
|
|
1882
2249
|
return true;
|
|
1883
2250
|
}
|
|
1884
2251
|
}
|
|
@@ -1887,14 +2254,14 @@ var S3ArchiveStorage = class {
|
|
|
1887
2254
|
/**
|
|
1888
2255
|
* Check if path matches a single pattern
|
|
1889
2256
|
*/
|
|
1890
|
-
matchesPattern(
|
|
2257
|
+
matchesPattern(path7, pattern) {
|
|
1891
2258
|
const DOUBLE_STAR = "\0DOUBLE_STAR\0";
|
|
1892
2259
|
let regexPattern = pattern.replace(/\*\*/g, DOUBLE_STAR);
|
|
1893
2260
|
regexPattern = regexPattern.replace(/[.[\](){}+^$|\\]/g, "\\$&");
|
|
1894
2261
|
regexPattern = regexPattern.replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
|
|
1895
2262
|
regexPattern = regexPattern.replace(new RegExp(DOUBLE_STAR, "g"), ".*");
|
|
1896
2263
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
1897
|
-
return regex.test(
|
|
2264
|
+
return regex.test(path7);
|
|
1898
2265
|
}
|
|
1899
2266
|
};
|
|
1900
2267
|
|
|
@@ -1909,7 +2276,10 @@ var StorageManager = class {
|
|
|
1909
2276
|
if (config.s3Archive) {
|
|
1910
2277
|
this.providers.set("s3-archive", new S3ArchiveStorage(config.s3Archive));
|
|
1911
2278
|
}
|
|
1912
|
-
|
|
2279
|
+
if (config.filePlugin) {
|
|
2280
|
+
this.providers.set("file-plugin", new FilePluginStorage(config.filePlugin));
|
|
2281
|
+
}
|
|
2282
|
+
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"]);
|
|
1913
2283
|
}
|
|
1914
2284
|
/**
|
|
1915
2285
|
* Register a custom storage provider
|
|
@@ -2149,7 +2519,7 @@ var CachePersistence = class {
|
|
|
2149
2519
|
}
|
|
2150
2520
|
const [, org, project, filePath] = match;
|
|
2151
2521
|
const relativePath = filePath || "index";
|
|
2152
|
-
return
|
|
2522
|
+
return path3__default.join(this.cacheDir, org, project, relativePath + this.extension);
|
|
2153
2523
|
}
|
|
2154
2524
|
/**
|
|
2155
2525
|
* Get the metadata file path for a URI
|
|
@@ -2165,8 +2535,8 @@ var CachePersistence = class {
|
|
|
2165
2535
|
const metadataPath = this.getMetadataPath(uri);
|
|
2166
2536
|
try {
|
|
2167
2537
|
const [metadataJson, content] = await Promise.all([
|
|
2168
|
-
|
|
2169
|
-
|
|
2538
|
+
fs3__default.readFile(metadataPath, "utf-8"),
|
|
2539
|
+
fs3__default.readFile(cachePath)
|
|
2170
2540
|
]);
|
|
2171
2541
|
const metadata = JSON.parse(metadataJson);
|
|
2172
2542
|
return {
|
|
@@ -2186,32 +2556,32 @@ var CachePersistence = class {
|
|
|
2186
2556
|
async write(entry) {
|
|
2187
2557
|
const cachePath = this.getCachePath(entry.metadata.uri);
|
|
2188
2558
|
const metadataPath = this.getMetadataPath(entry.metadata.uri);
|
|
2189
|
-
await
|
|
2559
|
+
await fs3__default.mkdir(path3__default.dirname(cachePath), { recursive: true });
|
|
2190
2560
|
if (this.atomicWrites) {
|
|
2191
2561
|
const tempCachePath = cachePath + ".tmp";
|
|
2192
2562
|
const tempMetadataPath = metadataPath + ".tmp";
|
|
2193
2563
|
try {
|
|
2194
2564
|
await Promise.all([
|
|
2195
|
-
|
|
2196
|
-
|
|
2565
|
+
fs3__default.writeFile(tempCachePath, entry.content),
|
|
2566
|
+
fs3__default.writeFile(tempMetadataPath, JSON.stringify(entry.metadata, null, 2))
|
|
2197
2567
|
]);
|
|
2198
2568
|
await Promise.all([
|
|
2199
|
-
|
|
2200
|
-
|
|
2569
|
+
fs3__default.rename(tempCachePath, cachePath),
|
|
2570
|
+
fs3__default.rename(tempMetadataPath, metadataPath)
|
|
2201
2571
|
]);
|
|
2202
2572
|
} catch (error) {
|
|
2203
2573
|
await Promise.all([
|
|
2204
|
-
|
|
2574
|
+
fs3__default.unlink(tempCachePath).catch(() => {
|
|
2205
2575
|
}),
|
|
2206
|
-
|
|
2576
|
+
fs3__default.unlink(tempMetadataPath).catch(() => {
|
|
2207
2577
|
})
|
|
2208
2578
|
]);
|
|
2209
2579
|
throw error;
|
|
2210
2580
|
}
|
|
2211
2581
|
} else {
|
|
2212
2582
|
await Promise.all([
|
|
2213
|
-
|
|
2214
|
-
|
|
2583
|
+
fs3__default.writeFile(cachePath, entry.content),
|
|
2584
|
+
fs3__default.writeFile(metadataPath, JSON.stringify(entry.metadata, null, 2))
|
|
2215
2585
|
]);
|
|
2216
2586
|
}
|
|
2217
2587
|
}
|
|
@@ -2222,7 +2592,7 @@ var CachePersistence = class {
|
|
|
2222
2592
|
const cachePath = this.getCachePath(uri);
|
|
2223
2593
|
const metadataPath = this.getMetadataPath(uri);
|
|
2224
2594
|
try {
|
|
2225
|
-
await Promise.all([
|
|
2595
|
+
await Promise.all([fs3__default.unlink(cachePath), fs3__default.unlink(metadataPath)]);
|
|
2226
2596
|
return true;
|
|
2227
2597
|
} catch (error) {
|
|
2228
2598
|
if (error.code === "ENOENT") {
|
|
@@ -2237,7 +2607,7 @@ var CachePersistence = class {
|
|
|
2237
2607
|
async exists(uri) {
|
|
2238
2608
|
const cachePath = this.getCachePath(uri);
|
|
2239
2609
|
try {
|
|
2240
|
-
await
|
|
2610
|
+
await fs3__default.access(cachePath);
|
|
2241
2611
|
return true;
|
|
2242
2612
|
} catch {
|
|
2243
2613
|
return false;
|
|
@@ -2249,20 +2619,20 @@ var CachePersistence = class {
|
|
|
2249
2619
|
async list() {
|
|
2250
2620
|
const uris = [];
|
|
2251
2621
|
try {
|
|
2252
|
-
const orgs = await
|
|
2622
|
+
const orgs = await fs3__default.readdir(this.cacheDir);
|
|
2253
2623
|
for (const org of orgs) {
|
|
2254
|
-
const orgPath =
|
|
2255
|
-
const orgStat = await
|
|
2624
|
+
const orgPath = path3__default.join(this.cacheDir, org);
|
|
2625
|
+
const orgStat = await fs3__default.stat(orgPath);
|
|
2256
2626
|
if (!orgStat.isDirectory()) continue;
|
|
2257
|
-
const projects = await
|
|
2627
|
+
const projects = await fs3__default.readdir(orgPath);
|
|
2258
2628
|
for (const project of projects) {
|
|
2259
|
-
const projectPath =
|
|
2260
|
-
const projectStat = await
|
|
2629
|
+
const projectPath = path3__default.join(orgPath, project);
|
|
2630
|
+
const projectStat = await fs3__default.stat(projectPath);
|
|
2261
2631
|
if (!projectStat.isDirectory()) continue;
|
|
2262
2632
|
const files = await this.listFilesRecursive(projectPath);
|
|
2263
2633
|
for (const file of files) {
|
|
2264
2634
|
if (file.endsWith(this.extension)) {
|
|
2265
|
-
const relativePath =
|
|
2635
|
+
const relativePath = path3__default.relative(projectPath, file);
|
|
2266
2636
|
const filePath = relativePath.slice(0, -this.extension.length);
|
|
2267
2637
|
uris.push(`codex://${org}/${project}/${filePath}`);
|
|
2268
2638
|
}
|
|
@@ -2282,9 +2652,9 @@ var CachePersistence = class {
|
|
|
2282
2652
|
*/
|
|
2283
2653
|
async listFilesRecursive(dir) {
|
|
2284
2654
|
const files = [];
|
|
2285
|
-
const entries = await
|
|
2655
|
+
const entries = await fs3__default.readdir(dir, { withFileTypes: true });
|
|
2286
2656
|
for (const entry of entries) {
|
|
2287
|
-
const fullPath =
|
|
2657
|
+
const fullPath = path3__default.join(dir, entry.name);
|
|
2288
2658
|
if (entry.isDirectory()) {
|
|
2289
2659
|
files.push(...await this.listFilesRecursive(fullPath));
|
|
2290
2660
|
} else if (entry.isFile()) {
|
|
@@ -2299,12 +2669,12 @@ var CachePersistence = class {
|
|
|
2299
2669
|
async clear() {
|
|
2300
2670
|
let count = 0;
|
|
2301
2671
|
try {
|
|
2302
|
-
const orgs = await
|
|
2672
|
+
const orgs = await fs3__default.readdir(this.cacheDir);
|
|
2303
2673
|
for (const org of orgs) {
|
|
2304
|
-
const orgPath =
|
|
2305
|
-
const stat = await
|
|
2674
|
+
const orgPath = path3__default.join(this.cacheDir, org);
|
|
2675
|
+
const stat = await fs3__default.stat(orgPath);
|
|
2306
2676
|
if (stat.isDirectory()) {
|
|
2307
|
-
await
|
|
2677
|
+
await fs3__default.rm(orgPath, { recursive: true });
|
|
2308
2678
|
count++;
|
|
2309
2679
|
}
|
|
2310
2680
|
}
|
|
@@ -2365,7 +2735,7 @@ var CachePersistence = class {
|
|
|
2365
2735
|
* Ensure cache directory exists
|
|
2366
2736
|
*/
|
|
2367
2737
|
async ensureDir() {
|
|
2368
|
-
await
|
|
2738
|
+
await fs3__default.mkdir(this.cacheDir, { recursive: true });
|
|
2369
2739
|
}
|
|
2370
2740
|
/**
|
|
2371
2741
|
* Get cache directory path
|
|
@@ -2413,8 +2783,17 @@ var CacheManager = class {
|
|
|
2413
2783
|
* Get content for a reference
|
|
2414
2784
|
*
|
|
2415
2785
|
* Implements cache-first strategy with stale-while-revalidate.
|
|
2786
|
+
*
|
|
2787
|
+
* EXCEPTION: File plugin sources (current project files) bypass cache entirely.
|
|
2788
|
+
* They are always read fresh from disk for optimal development experience.
|
|
2416
2789
|
*/
|
|
2417
2790
|
async get(reference, options) {
|
|
2791
|
+
if (reference.isCurrentProject && reference.sourceType === "file-plugin") {
|
|
2792
|
+
if (!this.storage) {
|
|
2793
|
+
throw new Error("Storage manager not set");
|
|
2794
|
+
}
|
|
2795
|
+
return await this.storage.fetch(reference, options);
|
|
2796
|
+
}
|
|
2418
2797
|
const ttl = options?.ttl ?? this.config.defaultTtl;
|
|
2419
2798
|
let entry = this.memoryCache.get(reference.uri);
|
|
2420
2799
|
if (!entry && this.persistence) {
|
|
@@ -2620,13 +2999,18 @@ var CacheManager = class {
|
|
|
2620
2999
|
}
|
|
2621
3000
|
/**
|
|
2622
3001
|
* Fetch content and store in cache
|
|
3002
|
+
*
|
|
3003
|
+
* EXCEPTION: File plugin sources are not cached (should not reach here,
|
|
3004
|
+
* but added as safety check).
|
|
2623
3005
|
*/
|
|
2624
3006
|
async fetchAndCache(reference, ttl, options) {
|
|
2625
3007
|
if (!this.storage) {
|
|
2626
3008
|
throw new Error("Storage manager not set");
|
|
2627
3009
|
}
|
|
2628
3010
|
const result = await this.storage.fetch(reference, options);
|
|
2629
|
-
|
|
3011
|
+
if (!(reference.isCurrentProject && reference.sourceType === "file-plugin")) {
|
|
3012
|
+
await this.set(reference.uri, result, ttl);
|
|
3013
|
+
}
|
|
2630
3014
|
return result;
|
|
2631
3015
|
}
|
|
2632
3016
|
/**
|
|
@@ -2745,11 +3129,11 @@ var DEFAULT_SYNC_CONFIG = {
|
|
|
2745
3129
|
deleteOrphans: false,
|
|
2746
3130
|
conflictStrategy: "newest"
|
|
2747
3131
|
};
|
|
2748
|
-
function evaluatePath(
|
|
3132
|
+
function evaluatePath(path7, rules, direction, defaultExcludes = []) {
|
|
2749
3133
|
for (const pattern of defaultExcludes) {
|
|
2750
|
-
if (micromatch3.isMatch(
|
|
3134
|
+
if (micromatch3.isMatch(path7, pattern)) {
|
|
2751
3135
|
return {
|
|
2752
|
-
path:
|
|
3136
|
+
path: path7,
|
|
2753
3137
|
shouldSync: false,
|
|
2754
3138
|
reason: `Excluded by default pattern: ${pattern}`
|
|
2755
3139
|
};
|
|
@@ -2760,9 +3144,9 @@ function evaluatePath(path6, rules, direction, defaultExcludes = []) {
|
|
|
2760
3144
|
if (rule.direction && rule.direction !== direction) {
|
|
2761
3145
|
continue;
|
|
2762
3146
|
}
|
|
2763
|
-
if (micromatch3.isMatch(
|
|
3147
|
+
if (micromatch3.isMatch(path7, rule.pattern)) {
|
|
2764
3148
|
return {
|
|
2765
|
-
path:
|
|
3149
|
+
path: path7,
|
|
2766
3150
|
shouldSync: rule.include,
|
|
2767
3151
|
matchedRule: rule,
|
|
2768
3152
|
reason: rule.include ? `Included by rule: ${rule.pattern}` : `Excluded by rule: ${rule.pattern}`
|
|
@@ -2770,21 +3154,21 @@ function evaluatePath(path6, rules, direction, defaultExcludes = []) {
|
|
|
2770
3154
|
}
|
|
2771
3155
|
}
|
|
2772
3156
|
return {
|
|
2773
|
-
path:
|
|
3157
|
+
path: path7,
|
|
2774
3158
|
shouldSync: true,
|
|
2775
3159
|
reason: "No matching rule, included by default"
|
|
2776
3160
|
};
|
|
2777
3161
|
}
|
|
2778
3162
|
function evaluatePaths(paths, rules, direction, defaultExcludes = []) {
|
|
2779
3163
|
const results = /* @__PURE__ */ new Map();
|
|
2780
|
-
for (const
|
|
2781
|
-
results.set(
|
|
3164
|
+
for (const path7 of paths) {
|
|
3165
|
+
results.set(path7, evaluatePath(path7, rules, direction, defaultExcludes));
|
|
2782
3166
|
}
|
|
2783
3167
|
return results;
|
|
2784
3168
|
}
|
|
2785
3169
|
function filterSyncablePaths(paths, rules, direction, defaultExcludes = []) {
|
|
2786
3170
|
return paths.filter(
|
|
2787
|
-
(
|
|
3171
|
+
(path7) => evaluatePath(path7, rules, direction, defaultExcludes).shouldSync
|
|
2788
3172
|
);
|
|
2789
3173
|
}
|
|
2790
3174
|
function createRulesFromPatterns(include = [], exclude = []) {
|
|
@@ -3050,8 +3434,8 @@ async function scanCodexWithRouting(options) {
|
|
|
3050
3434
|
for (const filePath of allFiles) {
|
|
3051
3435
|
totalScanned++;
|
|
3052
3436
|
try {
|
|
3053
|
-
const fullPath =
|
|
3054
|
-
const stats = await
|
|
3437
|
+
const fullPath = path3__default.join(codexDir, filePath);
|
|
3438
|
+
const stats = await fs3__default.stat(fullPath);
|
|
3055
3439
|
if (stats.size > maxFileSize) {
|
|
3056
3440
|
totalSkipped++;
|
|
3057
3441
|
errors.push({
|
|
@@ -3147,10 +3531,10 @@ async function listAllFilesRecursive(dirPath) {
|
|
|
3147
3531
|
const files = [];
|
|
3148
3532
|
async function scanDirectory(currentPath, relativePath = "") {
|
|
3149
3533
|
try {
|
|
3150
|
-
const entries = await
|
|
3534
|
+
const entries = await fs3__default.readdir(currentPath, { withFileTypes: true });
|
|
3151
3535
|
for (const entry of entries) {
|
|
3152
|
-
const entryPath =
|
|
3153
|
-
const entryRelativePath = relativePath ?
|
|
3536
|
+
const entryPath = path3__default.join(currentPath, entry.name);
|
|
3537
|
+
const entryRelativePath = relativePath ? path3__default.join(relativePath, entry.name) : entry.name;
|
|
3154
3538
|
if (entry.isDirectory()) {
|
|
3155
3539
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") {
|
|
3156
3540
|
continue;
|
|
@@ -3389,17 +3773,17 @@ var SyncManager = class {
|
|
|
3389
3773
|
if (file.operation === "create" || file.operation === "update") {
|
|
3390
3774
|
const sourcePath = `${plan.source}/${file.path}`;
|
|
3391
3775
|
const targetPath = `${plan.target}/${file.path}`;
|
|
3392
|
-
const
|
|
3393
|
-
const
|
|
3394
|
-
const targetDir =
|
|
3395
|
-
await
|
|
3396
|
-
await
|
|
3776
|
+
const fs5 = await import('fs/promises');
|
|
3777
|
+
const path7 = await import('path');
|
|
3778
|
+
const targetDir = path7.dirname(targetPath);
|
|
3779
|
+
await fs5.mkdir(targetDir, { recursive: true });
|
|
3780
|
+
await fs5.copyFile(sourcePath, targetPath);
|
|
3397
3781
|
synced++;
|
|
3398
3782
|
} else if (file.operation === "delete") {
|
|
3399
3783
|
const targetPath = `${plan.target}/${file.path}`;
|
|
3400
|
-
const
|
|
3784
|
+
const fs5 = await import('fs/promises');
|
|
3401
3785
|
try {
|
|
3402
|
-
await
|
|
3786
|
+
await fs5.unlink(targetPath);
|
|
3403
3787
|
synced++;
|
|
3404
3788
|
} catch (error) {
|
|
3405
3789
|
if (error.code !== "ENOENT") {
|
|
@@ -3454,15 +3838,15 @@ var SyncManager = class {
|
|
|
3454
3838
|
/**
|
|
3455
3839
|
* Get sync status for a file
|
|
3456
3840
|
*/
|
|
3457
|
-
async getFileStatus(
|
|
3841
|
+
async getFileStatus(path7) {
|
|
3458
3842
|
const manifest = await this.loadManifest();
|
|
3459
|
-
return manifest?.entries[
|
|
3843
|
+
return manifest?.entries[path7] ?? null;
|
|
3460
3844
|
}
|
|
3461
3845
|
/**
|
|
3462
3846
|
* Check if a file is synced
|
|
3463
3847
|
*/
|
|
3464
|
-
async isFileSynced(
|
|
3465
|
-
const status = await this.getFileStatus(
|
|
3848
|
+
async isFileSynced(path7) {
|
|
3849
|
+
const status = await this.getFileStatus(path7);
|
|
3466
3850
|
return status !== null;
|
|
3467
3851
|
}
|
|
3468
3852
|
/**
|
|
@@ -3546,19 +3930,19 @@ function ruleMatchesContext(rule, context) {
|
|
|
3546
3930
|
}
|
|
3547
3931
|
return true;
|
|
3548
3932
|
}
|
|
3549
|
-
function ruleMatchesPath(rule,
|
|
3550
|
-
return micromatch3.isMatch(
|
|
3933
|
+
function ruleMatchesPath(rule, path7) {
|
|
3934
|
+
return micromatch3.isMatch(path7, rule.pattern);
|
|
3551
3935
|
}
|
|
3552
3936
|
function ruleMatchesAction(rule, action) {
|
|
3553
3937
|
return rule.actions.includes(action);
|
|
3554
3938
|
}
|
|
3555
|
-
function evaluatePermission(
|
|
3939
|
+
function evaluatePermission(path7, action, context, config) {
|
|
3556
3940
|
const sortedRules = [...config.rules].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
3557
3941
|
for (const rule of sortedRules) {
|
|
3558
3942
|
if (!ruleMatchesContext(rule, context)) {
|
|
3559
3943
|
continue;
|
|
3560
3944
|
}
|
|
3561
|
-
if (!ruleMatchesPath(rule,
|
|
3945
|
+
if (!ruleMatchesPath(rule, path7)) {
|
|
3562
3946
|
continue;
|
|
3563
3947
|
}
|
|
3564
3948
|
if (!ruleMatchesAction(rule, action)) {
|
|
@@ -3577,24 +3961,24 @@ function evaluatePermission(path6, action, context, config) {
|
|
|
3577
3961
|
reason: config.defaultAllow ? "Allowed by default" : "Denied by default"
|
|
3578
3962
|
};
|
|
3579
3963
|
}
|
|
3580
|
-
function isAllowed(
|
|
3581
|
-
const result = evaluatePermission(
|
|
3964
|
+
function isAllowed(path7, action, context, config) {
|
|
3965
|
+
const result = evaluatePermission(path7, action, context, config);
|
|
3582
3966
|
return result.allowed;
|
|
3583
3967
|
}
|
|
3584
|
-
function hasPermission(
|
|
3585
|
-
const result = evaluatePermission(
|
|
3968
|
+
function hasPermission(path7, action, requiredLevel, context, config) {
|
|
3969
|
+
const result = evaluatePermission(path7, action, context, config);
|
|
3586
3970
|
return levelGrants(result.level, requiredLevel);
|
|
3587
3971
|
}
|
|
3588
3972
|
function evaluatePermissions(paths, action, context, config) {
|
|
3589
3973
|
const results = /* @__PURE__ */ new Map();
|
|
3590
|
-
for (const
|
|
3591
|
-
results.set(
|
|
3974
|
+
for (const path7 of paths) {
|
|
3975
|
+
results.set(path7, evaluatePermission(path7, action, context, config));
|
|
3592
3976
|
}
|
|
3593
3977
|
return results;
|
|
3594
3978
|
}
|
|
3595
3979
|
function filterByPermission(paths, action, context, config, requiredLevel = "read") {
|
|
3596
|
-
return paths.filter((
|
|
3597
|
-
const result = evaluatePermission(
|
|
3980
|
+
return paths.filter((path7) => {
|
|
3981
|
+
const result = evaluatePermission(path7, action, context, config);
|
|
3598
3982
|
return levelGrants(result.level, requiredLevel);
|
|
3599
3983
|
});
|
|
3600
3984
|
}
|
|
@@ -3685,21 +4069,21 @@ var PermissionManager = class {
|
|
|
3685
4069
|
/**
|
|
3686
4070
|
* Check if an action is allowed for a path
|
|
3687
4071
|
*/
|
|
3688
|
-
isAllowed(
|
|
3689
|
-
const result = this.evaluate(
|
|
4072
|
+
isAllowed(path7, action, context) {
|
|
4073
|
+
const result = this.evaluate(path7, action, context);
|
|
3690
4074
|
return result.allowed;
|
|
3691
4075
|
}
|
|
3692
4076
|
/**
|
|
3693
4077
|
* Check if a permission level is granted
|
|
3694
4078
|
*/
|
|
3695
|
-
hasPermission(
|
|
3696
|
-
const result = this.evaluate(
|
|
4079
|
+
hasPermission(path7, action, requiredLevel, context) {
|
|
4080
|
+
const result = this.evaluate(path7, action, context);
|
|
3697
4081
|
return levelGrants(result.level, requiredLevel);
|
|
3698
4082
|
}
|
|
3699
4083
|
/**
|
|
3700
4084
|
* Evaluate permission for a path and action
|
|
3701
4085
|
*/
|
|
3702
|
-
evaluate(
|
|
4086
|
+
evaluate(path7, action, context) {
|
|
3703
4087
|
const mergedContext = { ...this.defaultContext, ...context };
|
|
3704
4088
|
if (!this.config.enforced) {
|
|
3705
4089
|
return {
|
|
@@ -3708,7 +4092,7 @@ var PermissionManager = class {
|
|
|
3708
4092
|
reason: "Permissions not enforced"
|
|
3709
4093
|
};
|
|
3710
4094
|
}
|
|
3711
|
-
return evaluatePermission(
|
|
4095
|
+
return evaluatePermission(path7, action, mergedContext, this.config);
|
|
3712
4096
|
}
|
|
3713
4097
|
/**
|
|
3714
4098
|
* Filter paths by permission
|
|
@@ -3811,12 +4195,12 @@ var PermissionManager = class {
|
|
|
3811
4195
|
/**
|
|
3812
4196
|
* Assert permission (throws if denied)
|
|
3813
4197
|
*/
|
|
3814
|
-
assertPermission(
|
|
3815
|
-
const result = this.evaluate(
|
|
4198
|
+
assertPermission(path7, action, requiredLevel = "read", context) {
|
|
4199
|
+
const result = this.evaluate(path7, action, context);
|
|
3816
4200
|
if (!levelGrants(result.level, requiredLevel)) {
|
|
3817
4201
|
throw new PermissionDeniedError(
|
|
3818
|
-
`Permission denied for ${action} on ${
|
|
3819
|
-
|
|
4202
|
+
`Permission denied for ${action} on ${path7}: ${result.reason}`,
|
|
4203
|
+
path7,
|
|
3820
4204
|
action,
|
|
3821
4205
|
result
|
|
3822
4206
|
);
|
|
@@ -3824,9 +4208,9 @@ var PermissionManager = class {
|
|
|
3824
4208
|
}
|
|
3825
4209
|
};
|
|
3826
4210
|
var PermissionDeniedError = class extends Error {
|
|
3827
|
-
constructor(message,
|
|
4211
|
+
constructor(message, path7, action, result) {
|
|
3828
4212
|
super(message);
|
|
3829
|
-
this.path =
|
|
4213
|
+
this.path = path7;
|
|
3830
4214
|
this.action = action;
|
|
3831
4215
|
this.result = result;
|
|
3832
4216
|
this.name = "PermissionDeniedError";
|
|
@@ -4323,8 +4707,8 @@ function convertToUri(reference, options = {}) {
|
|
|
4323
4707
|
const parts = parseReference2(trimmed);
|
|
4324
4708
|
const org = parts.org || options.defaultOrg || "_";
|
|
4325
4709
|
const project = parts.project || options.defaultProject || "_";
|
|
4326
|
-
const
|
|
4327
|
-
return `codex://${org}/${project}/${
|
|
4710
|
+
const path7 = parts.path;
|
|
4711
|
+
return `codex://${org}/${project}/${path7}`;
|
|
4328
4712
|
}
|
|
4329
4713
|
function parseReference2(reference) {
|
|
4330
4714
|
const trimmed = reference.trim();
|
|
@@ -4404,6 +4788,6 @@ function generateReferenceMigrationSummary(results) {
|
|
|
4404
4788
|
return lines.join("\n");
|
|
4405
4789
|
}
|
|
4406
4790
|
|
|
4407
|
-
export { AutoSyncPatternSchema, BUILT_IN_TYPES, CODEX_URI_PREFIX, CacheManager, CachePersistence, CodexConfigSchema, CodexError, CommonRules, ConfigurationError, CustomTypeSchema, DEFAULT_CACHE_DIR, DEFAULT_FETCH_OPTIONS, DEFAULT_MIGRATION_OPTIONS, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYNC_CONFIG, DEFAULT_TYPE, GitHubStorage, HttpStorage, LEGACY_PATTERNS, LEGACY_REF_PREFIX, LocalStorage, MetadataSchema, PERMISSION_LEVEL_ORDER, PermissionDeniedError, PermissionManager, StorageManager, SyncManager, SyncRulesSchema, TTL, TypeRegistry, TypesConfigSchema, ValidationError, buildUri, calculateCachePath, calculateContentHash, convertLegacyReference, convertLegacyReferences, convertToUri, createCacheEntry, createCacheManager, createCachePersistence, createDefaultRegistry, createEmptyModernConfig, createEmptySyncPlan, createGitHubStorage, createHttpStorage, createLocalStorage, createPermissionManager, createRule, createRulesFromPatterns, createStorageManager, createSyncManager, createSyncPlan, deserializeCacheEntry, detectContentType, detectCurrentProject, detectVersion, estimateSyncTime, evaluatePath, evaluatePaths, evaluatePatterns, evaluatePermission, evaluatePermissions, extendType, extractOrgFromRepoName, extractRawFrontmatter, filterByPatterns, filterByPermission, filterPlanOperations, filterSyncablePaths, findLegacyReferences, formatPlanSummary, generateMigrationReport, generateReferenceMigrationSummary, getBuiltInType, getBuiltInTypeNames, getCacheEntryAge, getCacheEntryStatus, getCurrentContext, getCustomSyncDestinations, getDefaultCacheManager, getDefaultConfig, getDefaultDirectories, getDefaultPermissionManager, getDefaultRules, getDefaultStorageManager, getDirectory, getExtension, getFilename, getMigrationRequirements, getPlanStats, getRelativeCachePath, getRemainingTtl, getTargetRepos, hasContentChanged, hasFrontmatter, hasLegacyReferences, hasPermission as hasPermissionLevel, isBuiltInType, isCacheEntryFresh, isCacheEntryValid, isCurrentProjectUri, isLegacyConfig, isLegacyReference, isModernConfig, isAllowed as isPermissionAllowed, isValidUri, levelGrants, loadConfig, loadCustomTypes, matchAnyPattern, matchPattern, maxLevel, mergeFetchOptions, mergeRules, mergeTypes, migrateConfig, migrateFileReferences, minLevel, needsMigration, parseCustomDestination, parseMetadata, parseReference, parseTtl, resolveOrganization, resolveReference, resolveReferences, ruleMatchesAction, ruleMatchesContext, ruleMatchesPath, sanitizePath, serializeCacheEntry, setDefaultCacheManager, setDefaultPermissionManager, setDefaultStorageManager, shouldSyncToRepo, summarizeEvaluations, touchCacheEntry, validateCustomTypes, validateMetadata, validateMigratedConfig, validateOrg, validatePath, validateRules2 as validatePermissionRules, validateProject, validateRules, validateUri };
|
|
4791
|
+
export { AutoSyncPatternSchema, BUILT_IN_TYPES, CODEX_URI_PREFIX, CacheManager, CachePersistence, CodexConfigSchema, CodexError, CommonRules, ConfigurationError, CustomTypeSchema, DEFAULT_CACHE_DIR, DEFAULT_FETCH_OPTIONS, DEFAULT_MIGRATION_OPTIONS, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYNC_CONFIG, DEFAULT_TYPE, FilePluginFileNotFoundError, FilePluginStorage, GitHubStorage, HttpStorage, LEGACY_PATTERNS, LEGACY_REF_PREFIX, LocalStorage, MetadataSchema, PERMISSION_LEVEL_ORDER, PermissionDeniedError, PermissionManager, StorageManager, SyncManager, SyncRulesSchema, TTL, TypeRegistry, TypesConfigSchema, ValidationError, buildUri, calculateCachePath, calculateContentHash, convertLegacyReference, convertLegacyReferences, convertToUri, createCacheEntry, createCacheManager, createCachePersistence, createDefaultRegistry, createEmptyModernConfig, createEmptySyncPlan, createGitHubStorage, createHttpStorage, createLocalStorage, createPermissionManager, createRule, createRulesFromPatterns, createStorageManager, createSyncManager, createSyncPlan, deserializeCacheEntry, detectContentType, detectCurrentProject, detectVersion, estimateSyncTime, evaluatePath, evaluatePaths, evaluatePatterns, evaluatePermission, evaluatePermissions, extendType, extractOrgFromRepoName, extractRawFrontmatter, filterByPatterns, filterByPermission, filterPlanOperations, filterSyncablePaths, findLegacyReferences, formatPlanSummary, generateMigrationReport, generateReferenceMigrationSummary, getBuiltInType, getBuiltInTypeNames, getCacheEntryAge, getCacheEntryStatus, getCurrentContext, getCustomSyncDestinations, getDefaultCacheManager, getDefaultConfig, getDefaultDirectories, getDefaultPermissionManager, getDefaultRules, getDefaultStorageManager, getDirectory, getExtension, getFilename, getMigrationRequirements, getPlanStats, getRelativeCachePath, getRemainingTtl, getTargetRepos, hasContentChanged, hasFrontmatter, hasLegacyReferences, hasPermission as hasPermissionLevel, isBuiltInType, isCacheEntryFresh, isCacheEntryValid, isCurrentProjectUri, isLegacyConfig, isLegacyReference, isModernConfig, isAllowed as isPermissionAllowed, isValidUri, levelGrants, loadConfig, loadCustomTypes, matchAnyPattern, matchPattern, maxLevel, mergeFetchOptions, mergeRules, mergeTypes, migrateConfig, migrateFileReferences, minLevel, needsMigration, parseCustomDestination, parseMetadata, parseReference, parseTtl, resolveOrganization, resolveReference, resolveReferences, ruleMatchesAction, ruleMatchesContext, ruleMatchesPath, sanitizePath, serializeCacheEntry, setDefaultCacheManager, setDefaultPermissionManager, setDefaultStorageManager, shouldSyncToRepo, summarizeEvaluations, touchCacheEntry, validateCustomTypes, validateMetadata, validateMigratedConfig, validateOrg, validatePath, validateRules2 as validatePermissionRules, validateProject, validateRules, validateUri };
|
|
4408
4792
|
//# sourceMappingURL=index.js.map
|
|
4409
4793
|
//# sourceMappingURL=index.js.map
|