@fluid-app/fluid-cli-theme-dev 0.1.28 → 0.1.30
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.mjs +64 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -52,6 +52,12 @@ function getRequestIdFromJsonBody(body) {
|
|
|
52
52
|
return getStringRequestId(record.request_id) ?? getStringRequestId(record.requestId) ?? (meta && typeof meta === "object" && !Array.isArray(meta) ? getStringRequestId(meta.request_id) ?? getStringRequestId(meta.requestId) : void 0);
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
+
* Type guard for ApiError
|
|
56
|
+
*/
|
|
57
|
+
function isApiError(error) {
|
|
58
|
+
return error instanceof ApiError;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
55
61
|
* Creates a configured fetch client instance
|
|
56
62
|
*/
|
|
57
63
|
function createFetchClient(config) {
|
|
@@ -1258,6 +1264,45 @@ async function deleteThemeResource(client, application_theme_id, body) {
|
|
|
1258
1264
|
return client.delete(`/api/application_themes/${application_theme_id}/resources`, { body });
|
|
1259
1265
|
}
|
|
1260
1266
|
//#endregion
|
|
1267
|
+
//#region src/theme/stylesheet-keys.ts
|
|
1268
|
+
const STYLESHEET_KEY_PATTERN = /^(styles\.css|global_styles\.css|[^/]+\/[^/]+\/styles\.css)$/;
|
|
1269
|
+
function isStylesheetKey(key) {
|
|
1270
|
+
return STYLESHEET_KEY_PATTERN.test(key);
|
|
1271
|
+
}
|
|
1272
|
+
//#endregion
|
|
1273
|
+
//#region src/theme/format-error.ts
|
|
1274
|
+
const STYLESHEET_MIGRATION_SKILL = "template-stylesheet-to-asset-migration";
|
|
1275
|
+
function isStylesheetKeyRejection(error) {
|
|
1276
|
+
if (error.status !== 422) return false;
|
|
1277
|
+
if (/stylesheet.*no longer accepted/i.test(error.message)) return true;
|
|
1278
|
+
if (error.data && typeof error.data === "object") {
|
|
1279
|
+
const resourceErrors = error.data.application_theme_resource;
|
|
1280
|
+
if (resourceErrors && typeof resourceErrors.key === "string" && /stylesheet.*no longer accepted/i.test(resourceErrors.key)) return true;
|
|
1281
|
+
}
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Extract a human-readable message from a caught error. ApiError's default
|
|
1286
|
+
* `toString` prefixes with `ApiError:` and the class name; this returns the
|
|
1287
|
+
* API's own `error_message` verbatim so the CLI surfaces messages like
|
|
1288
|
+
* "This stylesheet key is no longer accepted. Upload stylesheets as
|
|
1289
|
+
* theme-level assets." directly.
|
|
1290
|
+
*
|
|
1291
|
+
* When the error is the legacy stylesheet key rejection specifically,
|
|
1292
|
+
* a hint is appended pointing to the bundled migration skill — the
|
|
1293
|
+
* caller needs to move the per-template `styles.css` bytes to `assets/`
|
|
1294
|
+
* and stop pushing the deprecated column key.
|
|
1295
|
+
*/
|
|
1296
|
+
function formatError(e) {
|
|
1297
|
+
if (isApiError(e)) {
|
|
1298
|
+
const status = e.status ? ` [${e.status}]` : "";
|
|
1299
|
+
const hint = isStylesheetKeyRejection(e) ? `\n ↳ Run \`fluid theme skills install\` — the bundled \`${STYLESHEET_MIGRATION_SKILL}\` skill can help you migrate this to a theme-level asset.` : "";
|
|
1300
|
+
return `${e.message}${status}${hint}`;
|
|
1301
|
+
}
|
|
1302
|
+
if (e instanceof Error) return e.message;
|
|
1303
|
+
return String(e);
|
|
1304
|
+
}
|
|
1305
|
+
//#endregion
|
|
1261
1306
|
//#region src/theme/syncer.ts
|
|
1262
1307
|
var Syncer = class {
|
|
1263
1308
|
checksums = /* @__PURE__ */ new Map();
|
|
@@ -1394,7 +1439,7 @@ var Syncer = class {
|
|
|
1394
1439
|
await this.uploadFile(file);
|
|
1395
1440
|
result.uploaded++;
|
|
1396
1441
|
} catch (e) {
|
|
1397
|
-
result.errors.push(`Upload ${file.relativePath}: ${e}`);
|
|
1442
|
+
result.errors.push(`Upload ${file.relativePath}: ${formatError(e)}`);
|
|
1398
1443
|
}
|
|
1399
1444
|
opts.onProgress?.(++done, toUpload.length);
|
|
1400
1445
|
}
|
|
@@ -1405,7 +1450,7 @@ var Syncer = class {
|
|
|
1405
1450
|
await this.deleteRemoteFile(key);
|
|
1406
1451
|
result.deleted++;
|
|
1407
1452
|
} catch (e) {
|
|
1408
|
-
result.errors.push(`Delete ${key}: ${e}`);
|
|
1453
|
+
result.errors.push(`Delete ${key}: ${formatError(e)}`);
|
|
1409
1454
|
}
|
|
1410
1455
|
}
|
|
1411
1456
|
return result;
|
|
@@ -1443,17 +1488,21 @@ var Syncer = class {
|
|
|
1443
1488
|
}
|
|
1444
1489
|
result.downloaded++;
|
|
1445
1490
|
} catch (e) {
|
|
1446
|
-
result.errors.push(`Download ${resource.key}: ${e}`);
|
|
1491
|
+
result.errors.push(`Download ${resource.key}: ${formatError(e)}`);
|
|
1447
1492
|
}
|
|
1448
1493
|
opts.onProgress?.(++done, resources.length);
|
|
1449
1494
|
}
|
|
1450
1495
|
if (opts.delete) {
|
|
1451
1496
|
const remoteKeys = new Set(resources.map((r) => r.key));
|
|
1452
|
-
for (const file of this.themeRoot.files())
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1497
|
+
for (const file of this.themeRoot.files()) {
|
|
1498
|
+
if (remoteKeys.has(file.relativePath)) continue;
|
|
1499
|
+
if (isStylesheetKey(file.relativePath)) continue;
|
|
1500
|
+
try {
|
|
1501
|
+
const { unlinkSync } = await import("node:fs");
|
|
1502
|
+
unlinkSync(file.absolutePath);
|
|
1503
|
+
result.deleted++;
|
|
1504
|
+
} catch {}
|
|
1505
|
+
}
|
|
1457
1506
|
}
|
|
1458
1507
|
return result;
|
|
1459
1508
|
}
|
|
@@ -2490,6 +2539,12 @@ function resolveBundledSkillsDir() {
|
|
|
2490
2539
|
}
|
|
2491
2540
|
throw new Error("Could not locate the bundled theme skills — this is a packaging bug.");
|
|
2492
2541
|
}
|
|
2542
|
+
function skillsWouldShipWithTheme(cwd, targetRoot) {
|
|
2543
|
+
if (!new ThemeRoot(cwd).isValid()) return false;
|
|
2544
|
+
const rel = relative(cwd, targetRoot);
|
|
2545
|
+
if (rel.startsWith("..")) return false;
|
|
2546
|
+
return !rel.split(sep).some((segment) => segment.startsWith("."));
|
|
2547
|
+
}
|
|
2493
2548
|
function createSkillsCommand() {
|
|
2494
2549
|
const skills = new Command("skills").description("Manage the bundled Fluid theme AI skills");
|
|
2495
2550
|
skills.command("install").description("Copy the bundled theme skills into the current directory (default: .agents/skills/)").option("-d, --dir <path>", "Directory to install into", DEFAULT_TARGET_DIR).option("-f, --force", "Overwrite existing skills without prompting").action(async (opts) => {
|
|
@@ -2499,6 +2554,7 @@ function createSkillsCommand() {
|
|
|
2499
2554
|
process.exit(1);
|
|
2500
2555
|
}
|
|
2501
2556
|
const targetRoot = resolve(process.cwd(), opts.dir);
|
|
2557
|
+
if (skillsWouldShipWithTheme(process.cwd(), targetRoot)) console.log(`${chalk.yellow("⚠")} ${chalk.bold(opts.dir)} is not a hidden directory — 'fluid theme push' will upload these skills as theme files. Install into a dot-directory like ${chalk.cyan(DEFAULT_TARGET_DIR)} to keep them local.`);
|
|
2502
2558
|
const { installed, skipped } = await installSkills({
|
|
2503
2559
|
sourceDir,
|
|
2504
2560
|
targetRoot,
|