@shard-for-obsidian/cli 0.2.0 → 0.2.4
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.js +226 -49
- package/dist/index.js.map +3 -3
- package/package.json +15 -13
package/dist/index.js
CHANGED
|
@@ -989,7 +989,7 @@ async function pushCommand(opts) {
|
|
|
989
989
|
digest: mainJsResult.digest,
|
|
990
990
|
size: mainJsResult.size,
|
|
991
991
|
annotations: {
|
|
992
|
-
"
|
|
992
|
+
"vnd.obsidianmd.layer.filename": "main.js"
|
|
993
993
|
}
|
|
994
994
|
});
|
|
995
995
|
logger.log(
|
|
@@ -1005,7 +1005,7 @@ async function pushCommand(opts) {
|
|
|
1005
1005
|
digest: stylesCssResult.digest,
|
|
1006
1006
|
size: stylesCssResult.size,
|
|
1007
1007
|
annotations: {
|
|
1008
|
-
"
|
|
1008
|
+
"vnd.obsidianmd.layer.filename": "styles.css"
|
|
1009
1009
|
}
|
|
1010
1010
|
});
|
|
1011
1011
|
logger.log(
|
|
@@ -1015,14 +1015,19 @@ async function pushCommand(opts) {
|
|
|
1015
1015
|
const githubUrl = deriveGitHubUrl(ref.remoteName);
|
|
1016
1016
|
const manifest = plugin.manifest.parsed;
|
|
1017
1017
|
const annotations = {
|
|
1018
|
-
"
|
|
1019
|
-
"
|
|
1020
|
-
"
|
|
1021
|
-
"
|
|
1022
|
-
"
|
|
1018
|
+
"vnd.obsidianmd.plugin.id": manifest.id,
|
|
1019
|
+
"vnd.obsidianmd.plugin.name": manifest.name,
|
|
1020
|
+
"vnd.obsidianmd.plugin.version": manifest.version,
|
|
1021
|
+
"vnd.obsidianmd.plugin.description": manifest.description,
|
|
1022
|
+
"vnd.obsidianmd.plugin.author": manifest.author,
|
|
1023
|
+
"vnd.obsidianmd.plugin.repo": githubUrl,
|
|
1024
|
+
"vnd.obsidianmd.plugin.published-at": (/* @__PURE__ */ new Date()).toISOString()
|
|
1023
1025
|
};
|
|
1024
1026
|
if (manifest.authorUrl) {
|
|
1025
|
-
annotations["
|
|
1027
|
+
annotations["vnd.obsidianmd.plugin.author-url"] = manifest.authorUrl;
|
|
1028
|
+
}
|
|
1029
|
+
if (manifest.minAppVersion) {
|
|
1030
|
+
annotations["vnd.obsidianmd.plugin.min-app-version"] = manifest.minAppVersion;
|
|
1026
1031
|
}
|
|
1027
1032
|
logger.log("Pushing plugin manifest...");
|
|
1028
1033
|
const manifestPushResult = await client.pushPluginManifest({
|
|
@@ -1076,7 +1081,7 @@ async function pullCommand(opts) {
|
|
|
1076
1081
|
logger.log(`Wrote manifest.json (${manifestJson.length} bytes)`);
|
|
1077
1082
|
const files = ["manifest.json"];
|
|
1078
1083
|
for (const layer of manifest.layers) {
|
|
1079
|
-
const filename = layer.annotations?.["
|
|
1084
|
+
const filename = layer.annotations?.["vnd.obsidianmd.layer.filename"];
|
|
1080
1085
|
if (!filename) {
|
|
1081
1086
|
throw new Error(
|
|
1082
1087
|
`Layer ${layer.digest} missing required filename annotation`
|
|
@@ -1297,7 +1302,7 @@ var PluginConverter = class {
|
|
|
1297
1302
|
digest: mainJsResult.digest,
|
|
1298
1303
|
size: mainJsResult.size,
|
|
1299
1304
|
annotations: {
|
|
1300
|
-
"
|
|
1305
|
+
"vnd.obsidianmd.layer.filename": "main.js"
|
|
1301
1306
|
}
|
|
1302
1307
|
});
|
|
1303
1308
|
if (pluginData.stylesCss) {
|
|
@@ -1309,19 +1314,26 @@ var PluginConverter = class {
|
|
|
1309
1314
|
digest: stylesCssResult.digest,
|
|
1310
1315
|
size: stylesCssResult.size,
|
|
1311
1316
|
annotations: {
|
|
1312
|
-
"
|
|
1317
|
+
"vnd.obsidianmd.layer.filename": "styles.css"
|
|
1313
1318
|
}
|
|
1314
1319
|
});
|
|
1315
1320
|
}
|
|
1316
1321
|
const annotations = {
|
|
1317
|
-
"
|
|
1318
|
-
"
|
|
1319
|
-
"
|
|
1320
|
-
"
|
|
1321
|
-
"
|
|
1322
|
+
"vnd.obsidianmd.plugin.id": pluginData.manifest.id,
|
|
1323
|
+
"vnd.obsidianmd.plugin.name": pluginData.manifest.name,
|
|
1324
|
+
"vnd.obsidianmd.plugin.version": pluginData.manifest.version,
|
|
1325
|
+
"vnd.obsidianmd.plugin.description": pluginData.manifest.description,
|
|
1326
|
+
"vnd.obsidianmd.plugin.author": pluginData.manifest.author,
|
|
1327
|
+
"vnd.obsidianmd.plugin.repo": githubRepo,
|
|
1328
|
+
"vnd.obsidianmd.plugin.published-at": (/* @__PURE__ */ new Date()).toISOString(),
|
|
1329
|
+
"vnd.obsidianmd.plugin.converted": "true",
|
|
1330
|
+
"vnd.obsidianmd.plugin.original-repo": "obsidianmd/obsidian-releases"
|
|
1322
1331
|
};
|
|
1323
1332
|
if (pluginData.manifest.authorUrl) {
|
|
1324
|
-
annotations["
|
|
1333
|
+
annotations["vnd.obsidianmd.plugin.author-url"] = pluginData.manifest.authorUrl;
|
|
1334
|
+
}
|
|
1335
|
+
if (pluginData.manifest.minAppVersion) {
|
|
1336
|
+
annotations["vnd.obsidianmd.plugin.min-app-version"] = pluginData.manifest.minAppVersion;
|
|
1325
1337
|
}
|
|
1326
1338
|
const manifestPushResult = await client.pushPluginManifest({
|
|
1327
1339
|
ref: ref.tag || pluginData.manifest.version,
|
|
@@ -1434,11 +1446,112 @@ var MarketplaceClient = class {
|
|
|
1434
1446
|
}
|
|
1435
1447
|
};
|
|
1436
1448
|
|
|
1449
|
+
// src/lib/oci-tags.ts
|
|
1450
|
+
function parseAuthChallenge(header) {
|
|
1451
|
+
const realmMatch = /realm="([^"]+)"/.exec(header);
|
|
1452
|
+
const serviceMatch = /service="([^"]+)"/.exec(header);
|
|
1453
|
+
const scopeMatch = /scope="([^"]+)"/.exec(header);
|
|
1454
|
+
if (!realmMatch || !serviceMatch || !scopeMatch) {
|
|
1455
|
+
return null;
|
|
1456
|
+
}
|
|
1457
|
+
return {
|
|
1458
|
+
realm: realmMatch[1],
|
|
1459
|
+
service: serviceMatch[1],
|
|
1460
|
+
scope: scopeMatch[1]
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
async function getRegistryToken(challenge, adapter, githubToken) {
|
|
1464
|
+
const url = `${challenge.realm}?service=${challenge.service}&scope=${challenge.scope}`;
|
|
1465
|
+
const headers = {
|
|
1466
|
+
Accept: "application/json"
|
|
1467
|
+
};
|
|
1468
|
+
if (githubToken) {
|
|
1469
|
+
const basicAuth = Buffer.from(`token:${githubToken}`).toString("base64");
|
|
1470
|
+
headers.Authorization = `Basic ${basicAuth}`;
|
|
1471
|
+
}
|
|
1472
|
+
try {
|
|
1473
|
+
const response = await adapter.fetch(url, { headers });
|
|
1474
|
+
if (!response.ok) {
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
const data = await response.json();
|
|
1478
|
+
return data.token || null;
|
|
1479
|
+
} catch {
|
|
1480
|
+
return null;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
async function queryOciTags(opts) {
|
|
1484
|
+
const { registryUrl, adapter, token } = opts;
|
|
1485
|
+
const ref = parseRepoAndRef(registryUrl);
|
|
1486
|
+
const url = `https://${ref.index.name}/v2/${ref.remoteName}/tags/list`;
|
|
1487
|
+
const headers = {
|
|
1488
|
+
Accept: "application/json"
|
|
1489
|
+
};
|
|
1490
|
+
let response = await adapter.fetch(url, { headers });
|
|
1491
|
+
if (response.status === 401 && token) {
|
|
1492
|
+
const wwwAuth = response.headers.get("www-authenticate");
|
|
1493
|
+
if (wwwAuth) {
|
|
1494
|
+
const challenge = parseAuthChallenge(wwwAuth);
|
|
1495
|
+
if (challenge) {
|
|
1496
|
+
const registryToken = await getRegistryToken(challenge, adapter, token);
|
|
1497
|
+
if (registryToken) {
|
|
1498
|
+
headers.Authorization = `Bearer ${registryToken}`;
|
|
1499
|
+
response = await adapter.fetch(url, { headers });
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (!response.ok) {
|
|
1505
|
+
throw new Error(
|
|
1506
|
+
`Failed to query OCI tags: ${response.status} ${response.statusText}`
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
const data = await response.json();
|
|
1510
|
+
return data.tags || [];
|
|
1511
|
+
}
|
|
1512
|
+
async function queryTagMetadata(opts) {
|
|
1513
|
+
const { registryUrl, tag, adapter, token } = opts;
|
|
1514
|
+
const ref = parseRepoAndRef(registryUrl);
|
|
1515
|
+
const url = `https://${ref.index.name}/v2/${ref.remoteName}/manifests/${tag}`;
|
|
1516
|
+
const headers = {
|
|
1517
|
+
Accept: "application/vnd.oci.image.manifest.v1+json"
|
|
1518
|
+
};
|
|
1519
|
+
let response = await adapter.fetch(url, { headers });
|
|
1520
|
+
if (response.status === 401 && token) {
|
|
1521
|
+
const wwwAuth = response.headers.get("www-authenticate");
|
|
1522
|
+
if (wwwAuth) {
|
|
1523
|
+
const challenge = parseAuthChallenge(wwwAuth);
|
|
1524
|
+
if (challenge) {
|
|
1525
|
+
const registryToken = await getRegistryToken(challenge, adapter, token);
|
|
1526
|
+
if (registryToken) {
|
|
1527
|
+
headers.Authorization = `Bearer ${registryToken}`;
|
|
1528
|
+
response = await adapter.fetch(url, { headers });
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (!response.ok) {
|
|
1534
|
+
throw new Error(
|
|
1535
|
+
`Failed to query tag metadata: ${response.status} ${response.statusText}`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
const manifest = await response.json();
|
|
1539
|
+
const publishedAt = manifest.created || (/* @__PURE__ */ new Date()).toISOString();
|
|
1540
|
+
const layerSizes = manifest.layers?.map((l) => l.size) || [];
|
|
1541
|
+
const size = layerSizes.reduce((sum, s) => sum + s, 0);
|
|
1542
|
+
const annotations = manifest.annotations || {};
|
|
1543
|
+
return {
|
|
1544
|
+
publishedAt,
|
|
1545
|
+
size,
|
|
1546
|
+
annotations
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1437
1550
|
// src/commands/marketplace.ts
|
|
1438
1551
|
import * as fs3 from "node:fs/promises";
|
|
1439
1552
|
import * as path3 from "node:path";
|
|
1440
1553
|
async function marketplaceRegisterCommand(opts) {
|
|
1441
|
-
const { repository, token, logger, adapter } = opts;
|
|
1554
|
+
const { repository, token, introduction, logger, adapter } = opts;
|
|
1442
1555
|
logger.log(`Fetching plugin metadata from ${repository}...`);
|
|
1443
1556
|
const ref = parseRepoAndRef(repository);
|
|
1444
1557
|
const client = new OciRegistryClient({
|
|
@@ -1457,57 +1570,56 @@ async function marketplaceRegisterCommand(opts) {
|
|
|
1457
1570
|
const name = pluginManifest.name;
|
|
1458
1571
|
const author = pluginManifest.author;
|
|
1459
1572
|
const description = pluginManifest.description || "";
|
|
1460
|
-
const version = pluginManifest.version;
|
|
1461
1573
|
const minObsidianVersion = pluginManifest.minAppVersion;
|
|
1462
1574
|
const authorUrl = pluginManifest.authorUrl;
|
|
1463
1575
|
let gitHubRepoUrl;
|
|
1464
|
-
if (ociManifest.annotations && ociManifest.annotations["
|
|
1465
|
-
|
|
1466
|
-
if (source.startsWith("http")) {
|
|
1467
|
-
gitHubRepoUrl = source;
|
|
1468
|
-
} else {
|
|
1469
|
-
gitHubRepoUrl = `https://github.com/${source}`;
|
|
1470
|
-
}
|
|
1576
|
+
if (ociManifest.annotations && ociManifest.annotations["vnd.obsidianmd.plugin.repo"]) {
|
|
1577
|
+
gitHubRepoUrl = ociManifest.annotations["vnd.obsidianmd.plugin.repo"];
|
|
1471
1578
|
}
|
|
1472
1579
|
const registryUrl = ref.canonicalName;
|
|
1473
1580
|
logger.log(`Plugin ID: ${pluginId}`);
|
|
1474
1581
|
logger.log(`Name: ${name}`);
|
|
1475
1582
|
logger.log(`Author: ${author}`);
|
|
1476
|
-
logger.log(`Version: ${version}`);
|
|
1477
1583
|
logger.log(`Registry URL: ${registryUrl}`);
|
|
1478
1584
|
if (gitHubRepoUrl) {
|
|
1479
1585
|
logger.log(`Repository: ${gitHubRepoUrl}`);
|
|
1480
1586
|
}
|
|
1481
|
-
let
|
|
1587
|
+
let frontmatter = `---
|
|
1588
|
+
id: ${pluginId}
|
|
1482
1589
|
registryUrl: ${registryUrl}
|
|
1483
1590
|
name: ${name}
|
|
1484
1591
|
author: ${author}
|
|
1485
1592
|
description: ${description}
|
|
1486
|
-
version: ${version}
|
|
1487
1593
|
`;
|
|
1488
1594
|
if (gitHubRepoUrl) {
|
|
1489
|
-
|
|
1595
|
+
frontmatter += `repository: ${gitHubRepoUrl}
|
|
1490
1596
|
`;
|
|
1491
1597
|
}
|
|
1492
1598
|
if (minObsidianVersion) {
|
|
1493
|
-
|
|
1599
|
+
frontmatter += `minObsidianVersion: ${minObsidianVersion}
|
|
1494
1600
|
`;
|
|
1495
1601
|
}
|
|
1496
1602
|
if (authorUrl) {
|
|
1497
|
-
|
|
1603
|
+
frontmatter += `authorUrl: ${authorUrl}
|
|
1498
1604
|
`;
|
|
1499
1605
|
}
|
|
1500
|
-
|
|
1606
|
+
frontmatter += `---
|
|
1607
|
+
`;
|
|
1608
|
+
let markdownContent = frontmatter;
|
|
1609
|
+
if (introduction) {
|
|
1610
|
+
markdownContent += `
|
|
1611
|
+
${introduction}
|
|
1501
1612
|
`;
|
|
1613
|
+
}
|
|
1502
1614
|
const marketplacePath = await findMarketplaceDir();
|
|
1503
1615
|
const pluginsDir = path3.join(marketplacePath, "plugins");
|
|
1504
|
-
const
|
|
1616
|
+
const mdPath = path3.join(pluginsDir, `${pluginId}.md`);
|
|
1505
1617
|
await fs3.mkdir(pluginsDir, { recursive: true });
|
|
1506
|
-
await fs3.writeFile(
|
|
1507
|
-
logger.success(`Successfully registered plugin to ${
|
|
1618
|
+
await fs3.writeFile(mdPath, markdownContent, "utf-8");
|
|
1619
|
+
logger.success(`Successfully registered plugin to ${mdPath}`);
|
|
1508
1620
|
logger.log(`
|
|
1509
1621
|
Next steps:`);
|
|
1510
|
-
logger.log(`1. Review the generated
|
|
1622
|
+
logger.log(`1. Review the generated markdown file`);
|
|
1511
1623
|
logger.log(`2. Commit and push to the marketplace repository`);
|
|
1512
1624
|
logger.log(`3. Submit a pull request to add your plugin to the marketplace`);
|
|
1513
1625
|
return {
|
|
@@ -1515,12 +1627,11 @@ Next steps:`);
|
|
|
1515
1627
|
name,
|
|
1516
1628
|
author,
|
|
1517
1629
|
description,
|
|
1518
|
-
version,
|
|
1519
1630
|
registryUrl,
|
|
1520
1631
|
repository: gitHubRepoUrl,
|
|
1521
1632
|
minObsidianVersion,
|
|
1522
1633
|
authorUrl,
|
|
1523
|
-
|
|
1634
|
+
mdPath
|
|
1524
1635
|
};
|
|
1525
1636
|
}
|
|
1526
1637
|
async function findMarketplaceDir() {
|
|
@@ -1552,7 +1663,9 @@ Found ${plugins.length} plugins:
|
|
|
1552
1663
|
for (const plugin of plugins) {
|
|
1553
1664
|
logger.log(`${plugin.name} (${plugin.id})`);
|
|
1554
1665
|
logger.log(` Author: ${plugin.author}`);
|
|
1555
|
-
|
|
1666
|
+
if (plugin.versions && plugin.versions.length > 0) {
|
|
1667
|
+
logger.log(` Latest Version: ${plugin.versions[0].tag}`);
|
|
1668
|
+
}
|
|
1556
1669
|
logger.log(` Registry: ${plugin.registryUrl}`);
|
|
1557
1670
|
if (plugin.description) {
|
|
1558
1671
|
logger.log(` Description: ${plugin.description}`);
|
|
@@ -1577,7 +1690,9 @@ Found ${plugins.length} matching plugin(s):
|
|
|
1577
1690
|
for (const plugin of plugins) {
|
|
1578
1691
|
logger.log(`${plugin.name} (${plugin.id})`);
|
|
1579
1692
|
logger.log(` Author: ${plugin.author}`);
|
|
1580
|
-
|
|
1693
|
+
if (plugin.versions && plugin.versions.length > 0) {
|
|
1694
|
+
logger.log(` Latest Version: ${plugin.versions[0].tag}`);
|
|
1695
|
+
}
|
|
1581
1696
|
logger.log(` Registry: ${plugin.registryUrl}`);
|
|
1582
1697
|
if (plugin.description) {
|
|
1583
1698
|
logger.log(` Description: ${plugin.description}`);
|
|
@@ -1599,7 +1714,6 @@ async function marketplaceInfoCommand(opts) {
|
|
|
1599
1714
|
logger.log(`Plugin: ${plugin.name}`);
|
|
1600
1715
|
logger.log("=".repeat(60) + "\n");
|
|
1601
1716
|
logger.log(`ID: ${plugin.id}`);
|
|
1602
|
-
logger.log(`Version: ${plugin.version}`);
|
|
1603
1717
|
logger.log(`Author: ${plugin.author}`);
|
|
1604
1718
|
if (plugin.authorUrl) {
|
|
1605
1719
|
logger.log(`Author URL: ${plugin.authorUrl}`);
|
|
@@ -1619,11 +1733,24 @@ Registry URL: ${plugin.registryUrl}`);
|
|
|
1619
1733
|
if (plugin.tags && plugin.tags.length > 0) {
|
|
1620
1734
|
logger.log(`Tags: ${plugin.tags.join(", ")}`);
|
|
1621
1735
|
}
|
|
1622
|
-
|
|
1736
|
+
if (plugin.versions && plugin.versions.length > 0) {
|
|
1737
|
+
logger.log(`
|
|
1738
|
+
Available Versions (${plugin.versions.length}):`);
|
|
1739
|
+
for (const version of plugin.versions.slice(0, 5)) {
|
|
1740
|
+
const date = new Date(version.publishedAt).toISOString().split("T")[0];
|
|
1741
|
+
logger.log(` - ${version.tag} (${date})`);
|
|
1742
|
+
}
|
|
1743
|
+
if (plugin.versions.length > 5) {
|
|
1744
|
+
logger.log(` ... and ${plugin.versions.length - 5} more`);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1623
1747
|
logger.log("\n" + "=".repeat(60));
|
|
1624
1748
|
logger.log("Installation:");
|
|
1625
1749
|
logger.log("=".repeat(60) + "\n");
|
|
1626
|
-
|
|
1750
|
+
const latestVersion = plugin.versions && plugin.versions.length > 0 ? plugin.versions[0].tag : "latest";
|
|
1751
|
+
logger.log(
|
|
1752
|
+
`shard pull ${plugin.registryUrl}:${latestVersion} --output <path>`
|
|
1753
|
+
);
|
|
1627
1754
|
logger.log(
|
|
1628
1755
|
`shard marketplace install ${plugin.id} # (coming soon)`
|
|
1629
1756
|
);
|
|
@@ -1637,8 +1764,9 @@ async function marketplaceInstallCommand(opts) {
|
|
|
1637
1764
|
if (!plugin) {
|
|
1638
1765
|
throw new Error(`Plugin "${pluginId}" not found in marketplace`);
|
|
1639
1766
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1767
|
+
const latestVersion = plugin.versions && plugin.versions.length > 0 ? plugin.versions[0].tag : "latest";
|
|
1768
|
+
logger.log(`Found: ${plugin.name} v${latestVersion} by ${plugin.author}`);
|
|
1769
|
+
const versionToInstall = version || latestVersion;
|
|
1642
1770
|
const repository = `${plugin.registryUrl}:${versionToInstall}`;
|
|
1643
1771
|
logger.log(`Installing from ${repository}...`);
|
|
1644
1772
|
const pullResult = await pullCommand({
|
|
@@ -1653,11 +1781,41 @@ async function marketplaceInstallCommand(opts) {
|
|
|
1653
1781
|
);
|
|
1654
1782
|
return { plugin, pullResult };
|
|
1655
1783
|
}
|
|
1784
|
+
async function marketplaceVersionsCommand(opts) {
|
|
1785
|
+
const { registryUrl, token, logger, adapter } = opts;
|
|
1786
|
+
logger.log(`Querying versions for ${registryUrl}...`);
|
|
1787
|
+
const tags = await queryOciTags({ registryUrl, token, adapter });
|
|
1788
|
+
if (tags.length === 0) {
|
|
1789
|
+
logger.log("No versions found");
|
|
1790
|
+
return [];
|
|
1791
|
+
}
|
|
1792
|
+
logger.log(`
|
|
1793
|
+
Found ${tags.length} version(s):
|
|
1794
|
+
`);
|
|
1795
|
+
const versions = [];
|
|
1796
|
+
for (const tag of tags) {
|
|
1797
|
+
const metadata = await queryTagMetadata({
|
|
1798
|
+
registryUrl,
|
|
1799
|
+
tag,
|
|
1800
|
+
token,
|
|
1801
|
+
adapter
|
|
1802
|
+
});
|
|
1803
|
+
versions.push({ tag, ...metadata });
|
|
1804
|
+
const sizeKB = (metadata.size / 1024).toFixed(0);
|
|
1805
|
+
const date = new Date(metadata.publishedAt).toISOString().split("T")[0];
|
|
1806
|
+
logger.log(`- ${tag} (published ${date}, ${sizeKB} KB)`);
|
|
1807
|
+
if (metadata.annotations["vnd.obsidianmd.plugin.commit"]) {
|
|
1808
|
+
const sha = metadata.annotations["vnd.obsidianmd.plugin.commit"];
|
|
1809
|
+
logger.log(` Commit: ${sha.substring(0, 7)}`);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
return versions;
|
|
1813
|
+
}
|
|
1656
1814
|
async function marketplaceUpdateCommand(opts) {
|
|
1657
1815
|
const { logger } = opts;
|
|
1658
1816
|
logger.log("Updating marketplace entry...");
|
|
1659
1817
|
logger.log(
|
|
1660
|
-
"Note: This will overwrite the existing
|
|
1818
|
+
"Note: This will overwrite the existing markdown file with fresh metadata from GHCR\n"
|
|
1661
1819
|
);
|
|
1662
1820
|
return marketplaceRegisterCommand(opts);
|
|
1663
1821
|
}
|
|
@@ -1738,6 +1896,7 @@ Marketplace Subcommands:
|
|
|
1738
1896
|
install <plugin-id> Install a plugin by ID
|
|
1739
1897
|
register <repository> Register plugin to marketplace
|
|
1740
1898
|
update <repository> Update marketplace entry
|
|
1899
|
+
versions <registryUrl> List all available versions for a plugin
|
|
1741
1900
|
|
|
1742
1901
|
Push Options:
|
|
1743
1902
|
<directory> Path to plugin build output (e.g., ./dist)
|
|
@@ -1783,6 +1942,7 @@ Examples:
|
|
|
1783
1942
|
shard marketplace install obsidian-git --output ./plugins/obsidian-git
|
|
1784
1943
|
shard marketplace register ghcr.io/user/my-plugin:1.0.0
|
|
1785
1944
|
shard marketplace update ghcr.io/user/my-plugin:1.0.1
|
|
1945
|
+
shard marketplace versions ghcr.io/user/my-plugin
|
|
1786
1946
|
`;
|
|
1787
1947
|
async function main() {
|
|
1788
1948
|
let args;
|
|
@@ -1877,7 +2037,7 @@ async function main() {
|
|
|
1877
2037
|
const subcommand = args.positionals[1];
|
|
1878
2038
|
if (!subcommand) {
|
|
1879
2039
|
throw new Error(
|
|
1880
|
-
"Marketplace command requires a subcommand. Available: list, search, info, install, register, update"
|
|
2040
|
+
"Marketplace command requires a subcommand. Available: list, search, info, install, register, update, versions"
|
|
1881
2041
|
);
|
|
1882
2042
|
}
|
|
1883
2043
|
const token = resolveAuthToken(args.values.token);
|
|
@@ -1970,9 +2130,26 @@ async function main() {
|
|
|
1970
2130
|
console.log(JSON.stringify(result, null, 2));
|
|
1971
2131
|
}
|
|
1972
2132
|
process.exit(0);
|
|
2133
|
+
} else if (subcommand === "versions") {
|
|
2134
|
+
if (args.positionals.length < 3) {
|
|
2135
|
+
throw new Error(
|
|
2136
|
+
"Marketplace versions command requires <registryUrl>"
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
const registryUrl = args.positionals[2];
|
|
2140
|
+
const result = await marketplaceVersionsCommand({
|
|
2141
|
+
registryUrl,
|
|
2142
|
+
token,
|
|
2143
|
+
logger,
|
|
2144
|
+
adapter
|
|
2145
|
+
});
|
|
2146
|
+
if (args.values.json) {
|
|
2147
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2148
|
+
}
|
|
2149
|
+
process.exit(0);
|
|
1973
2150
|
} else {
|
|
1974
2151
|
throw new Error(
|
|
1975
|
-
`Unknown marketplace subcommand: ${subcommand}. Available: list, search, info, install, register, update`
|
|
2152
|
+
`Unknown marketplace subcommand: ${subcommand}. Available: list, search, info, install, register, update, versions`
|
|
1976
2153
|
);
|
|
1977
2154
|
}
|
|
1978
2155
|
} else {
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../../shard-lib/dist/parsing/IndexParser.js", "../../shard-lib/dist/utils/ValidationUtils.js", "../../shard-lib/dist/parsing/RepoParser.js", "../../shard-lib/dist/utils/DigestUtils.js", "../../shard-lib/dist/types/ManifestTypes.js", "../../shard-lib/dist/ghcr/GhcrConstants.js", "../../shard-lib/dist/errors/RegistryErrors.js", "../../shard-lib/dist/parsing/LinkHeaderParser.js", "../../shard-lib/dist/client/OciRegistryClient.js", "../src/lib/plugin.ts", "../src/commands/push.ts", "../src/commands/pull.ts", "../src/lib/community-plugins.ts", "../src/lib/community-cache.ts", "../src/lib/github-release.ts", "../src/lib/converter.ts", "../src/commands/convert.ts", "../src/lib/marketplace-client.ts", "../src/commands/marketplace.ts", "../src/lib/auth.ts", "../src/lib/logger.ts", "../src/adapters/node-fetch-adapter.ts"],
|
|
4
|
-
"sourcesContent": ["import { parseArgs } from \"node:util\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { convertCommand } from \"./commands/convert.js\";\nimport {\n marketplaceRegisterCommand,\n marketplaceListCommand,\n marketplaceSearchCommand,\n marketplaceInfoCommand,\n marketplaceInstallCommand,\n marketplaceUpdateCommand,\n} from \"./commands/marketplace.js\";\nimport { resolveAuthToken } from \"./lib/auth.js\";\nimport { Logger } from \"./lib/logger.js\";\nimport { NodeFetchAdapter } from \"./adapters/node-fetch-adapter.js\";\n\nconst USAGE = `\nUsage: shard <command> [options]\n\nCommands:\n push <directory> <repository> Push a plugin to GHCR\n pull <repository> Pull a plugin from GHCR\n convert <plugin-id> <repository> Convert legacy plugin to OCI format\n marketplace <subcommand> Manage marketplace plugins\n\nMarketplace Subcommands:\n list List all marketplace plugins\n search <keyword> Search for plugins\n info <plugin-id> Show detailed plugin information\n install <plugin-id> Install a plugin by ID\n register <repository> Register plugin to marketplace\n update <repository> Update marketplace entry\n\nPush Options:\n <directory> Path to plugin build output (e.g., ./dist)\n <repository> GHCR repository (e.g., ghcr.io/user/plugin)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nPull Options:\n <repository> Full reference with tag (e.g., ghcr.io/user/plugin:1.0.0)\n --output <dir> Where to extract files (required)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nConvert Options:\n <plugin-id> Plugin ID from community list (e.g., obsidian-git)\n <repository> GHCR repository (e.g., ghcr.io/user/plugin)\n --version <version> Specific version to convert (defaults to latest)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nMarketplace Options:\n --output <dir> Output directory for install command\n --version <version> Specific version to install (defaults to latest)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nEnvironment Variables:\n GITHUB_TOKEN GitHub token (alternative to --token)\n GH_TOKEN GitHub token (gh CLI compatibility)\n\nExamples:\n shard push ./dist ghcr.io/user/my-plugin\n shard pull ghcr.io/user/my-plugin:1.0.0 --output ./plugin\n shard convert obsidian-git ghcr.io/user/obsidian-git\n shard convert calendar ghcr.io/user/calendar --version 1.5.3\n shard marketplace list\n shard marketplace search \"calendar\"\n shard marketplace info obsidian-git\n shard marketplace install obsidian-git --output ./plugins/obsidian-git\n shard marketplace register ghcr.io/user/my-plugin:1.0.0\n shard marketplace update ghcr.io/user/my-plugin:1.0.1\n`;\n\ninterface CliArgs {\n values: {\n token?: string;\n json?: boolean;\n help?: boolean;\n output?: string;\n version?: string;\n };\n positionals: string[];\n}\n\nasync function main() {\n // Parse command line arguments\n let args: CliArgs;\n try {\n args = parseArgs({\n options: {\n token: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n help: { type: \"boolean\", default: false },\n output: { type: \"string\" },\n version: { type: \"string\" },\n },\n allowPositionals: true,\n }) as CliArgs;\n } catch (err) {\n console.error(\n `Error parsing arguments: ${err instanceof Error ? err.message : String(err)}`,\n );\n console.error(USAGE);\n process.exit(1);\n }\n\n // Show help\n if (args.values.help || args.positionals.length === 0) {\n console.log(USAGE);\n process.exit(0);\n }\n\n // Get command\n const command = args.positionals[0];\n const logger = new Logger(args.values.json);\n const adapter = new NodeFetchAdapter();\n\n try {\n if (command === \"push\") {\n // Parse push arguments\n if (args.positionals.length < 3) {\n throw new Error(\"Push command requires <directory> and <repository>\");\n }\n\n const directory = args.positionals[1];\n const repository = args.positionals[2];\n const token = resolveAuthToken(args.values.token);\n\n // Execute push\n const result = await pushCommand({\n directory,\n repository,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"pull\") {\n // Parse pull arguments\n if (args.positionals.length < 2) {\n throw new Error(\"Pull command requires <repository>\");\n }\n\n if (!args.values.output) {\n throw new Error(\"Pull command requires --output flag\");\n }\n\n const repository = args.positionals[1];\n const output = args.values.output;\n const token = resolveAuthToken(args.values.token);\n\n // Execute pull\n const result = await pullCommand({\n repository,\n output,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"convert\") {\n // Parse convert arguments\n if (args.positionals.length < 3) {\n throw new Error(\n \"Convert command requires <plugin-id> and <repository>\",\n );\n }\n\n const pluginId = args.positionals[1];\n const repository = args.positionals[2];\n const version = args.values.version;\n const token = resolveAuthToken(args.values.token);\n\n // Execute convert\n const result = await convertCommand({\n pluginId,\n repository,\n version,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"marketplace\") {\n // Parse marketplace subcommand\n const subcommand = args.positionals[1];\n\n if (!subcommand) {\n throw new Error(\n \"Marketplace command requires a subcommand. Available: list, search, info, install, register, update\",\n );\n }\n\n const token = resolveAuthToken(args.values.token);\n\n if (subcommand === \"list\") {\n // List all plugins\n const result = await marketplaceListCommand({\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"search\") {\n // Search for plugins\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace search command requires <keyword>\");\n }\n\n const keyword = args.positionals[2];\n const result = await marketplaceSearchCommand({\n keyword,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"info\") {\n // Show plugin info\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace info command requires <plugin-id>\");\n }\n\n const pluginId = args.positionals[2];\n const result = await marketplaceInfoCommand({\n pluginId,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"install\") {\n // Install plugin by ID\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace install command requires <plugin-id>\");\n }\n\n if (!args.values.output) {\n throw new Error(\"Marketplace install command requires --output flag\");\n }\n\n const pluginId = args.positionals[2];\n const output = args.values.output;\n const version = args.values.version;\n\n const result = await marketplaceInstallCommand({\n pluginId,\n output,\n version,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"register\") {\n // Register plugin to marketplace\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace register command requires <repository>\");\n }\n\n const repository = args.positionals[2];\n const result = await marketplaceRegisterCommand({\n repository,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"update\") {\n // Update marketplace entry\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace update command requires <repository>\");\n }\n\n const repository = args.positionals[2];\n const result = await marketplaceUpdateCommand({\n repository,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else {\n throw new Error(\n `Unknown marketplace subcommand: ${subcommand}. Available: list, search, info, install, register, update`,\n );\n }\n } else {\n throw new Error(`Unknown command: ${command}`);\n }\n } catch (err) {\n logger.error(err instanceof Error ? err.message : String(err));\n if (args.values.json) {\n console.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2,\n ),\n );\n }\n process.exit(1);\n }\n}\n\nvoid main();\n", "// --- globals\n// See `INDEXNAME` in docker/docker.git:registry/config.go.\nexport const DEFAULT_INDEX_NAME = \"docker.io\";\nexport const DEFAULT_INDEX_URL = \"https://registry-1.docker.io\";\nexport const DEFAULT_LOGIN_SERVERNAME = \"https://index.docker.io/v1/\";\n/**\n * Parse a docker index name or index URL.\n *\n * Examples:\n * docker.io (no scheme implies 'https')\n * index.docker.io (normalized to docker.io)\n * https://docker.io\n * http://localhost:5000\n * https://index.docker.io/v1/ (special case)\n *\n * Special case: `docker` still refers to \"https://index.docker.io/v1/\"\n * when dealing with auth (including in its json file).\n *\n * @param {String} arg: Optional. Index name (optionally with leading scheme).\n */\nexport function parseIndex(arg) {\n if (!arg || arg === DEFAULT_LOGIN_SERVERNAME) {\n // Default index.\n return {\n scheme: \"https\",\n name: DEFAULT_INDEX_NAME,\n official: true,\n };\n }\n // Optional protocol/scheme.\n let indexName;\n let scheme = \"https\";\n const protoSepIdx = arg.indexOf(\"://\");\n if (protoSepIdx !== -1) {\n const foundScheme = arg.slice(0, protoSepIdx);\n if (foundScheme !== \"http\" && foundScheme !== \"https\") {\n throw new Error(\"invalid index scheme, must be \" + '\"http\" or \"https\": ' + arg);\n }\n scheme = foundScheme;\n indexName = arg.slice(protoSepIdx + 3);\n }\n else {\n scheme = isLocalhost(arg) ? \"http\" : \"https\";\n indexName = arg;\n }\n if (!indexName) {\n throw new Error(\"invalid index, empty host: \" + arg);\n }\n else if (indexName.indexOf(\".\") === -1 &&\n indexName.indexOf(\":\") === -1 &&\n indexName !== \"localhost\") {\n throw new Error(`invalid index, \"${indexName}\" does not look like a valid host: ${arg}`);\n }\n else {\n // Allow a trailing '/' as from some URL builder functions that\n // add a default '/' path to a URL, e.g. 'https://docker.io/'.\n if (indexName[indexName.length - 1] === \"/\") {\n indexName = indexName.slice(0, indexName.length - 1);\n }\n // Ensure no trailing repo.\n if (indexName.indexOf(\"/\") !== -1) {\n throw new Error(\"invalid index, trailing repo: \" + arg);\n }\n }\n // Per docker.git's `ValidateIndexName`.\n if (indexName === \"index.\" + DEFAULT_INDEX_NAME) {\n indexName = DEFAULT_INDEX_NAME;\n }\n const index = {\n name: indexName,\n official: indexName === DEFAULT_INDEX_NAME,\n scheme,\n };\n // Disallow official and 'http'.\n if (index.official && index.scheme === \"http\") {\n throw new Error(\"invalid index, plaintext HTTP to official index \" +\n \"is disallowed: \" +\n arg);\n }\n return index;\n}\n/**\n * Similar in spirit to docker.git:registry/endpoint.go#NewEndpoint().\n */\nexport function urlFromIndex(index, scheme) {\n if (index.official) {\n // v1\n if (scheme != null && scheme !== \"https\")\n throw new Error(`Unencrypted communication with docker.io is not allowed`);\n return DEFAULT_INDEX_URL;\n }\n else {\n if (scheme != null && scheme !== \"https\" && scheme !== \"http\")\n throw new Error(`Non-HTTP communication with docker registries is not allowed`);\n return `${scheme ?? index.scheme}://${index.name}`;\n }\n}\nexport function isLocalhost(host) {\n const lead = host.split(\":\")[0];\n if (lead === \"localhost\" || lead === \"127.0.0.1\" || host.includes(\"::1\")) {\n return true;\n }\n else {\n return false;\n }\n}\n", "export function splitIntoTwo(str, sep) {\n const slashIdx = str.indexOf(sep);\n return slashIdx == -1\n ? [str]\n : [str.slice(0, slashIdx), str.slice(slashIdx + 1)];\n}\n", "import { parseIndex } from \"./IndexParser.js\";\nimport { splitIntoTwo } from \"../utils/ValidationUtils.js\";\n// JSSTYLED\n// 'DEFAULTTAG' from https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/graph/tags.go#L25\nexport const DEFAULT_TAG = \"latest\";\nconst VALID_NS = /^[a-z0-9._-]*$/;\nconst VALID_REPO = /^[a-z0-9_/.-]*$/;\n/**\n * Parse a docker repo and tag string: [INDEX/]REPO[:TAG|@DIGEST]\n *\n * Examples:\n * busybox\n * google/python\n * docker.io/ubuntu\n * localhost:5000/blarg\n * http://localhost:5000/blarg\n *\n * Dev Notes:\n * - This is meant to mimic\n * docker.git:registry/config.go#ServiceConfig.NewRepositoryInfo\n * as much as reasonable -- with the addition that we maintain the\n * 'tag' field. Also, that we accept the scheme on the \"INDEX\" is\n * different than docker.git's parsing.\n * - TODO: what about the '@digest' digest alternative to a tag? See:\n * // JSSTYLED\n * https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L68\n *\n * @param arg {String} The docker repo string to parse. See examples above.\n * @param defaultIndex {Object|String} Optional. The default index to use\n * if not specified with `arg`. If not given the default is 'docker.io'.\n * If given it may either be a string, e.g. 'https://myreg.example.com',\n * or parsed index object, as from `parseIndex()`.\n */\nexport function parseRepo(arg, defaultIndex) {\n let index;\n // Strip off optional leading `INDEX/`, parse it to `info.index` and\n // leave the rest in `remoteName`.\n let remoteNameRaw;\n const protoSepIdx = arg.indexOf(\"://\");\n if (protoSepIdx !== -1) {\n // (A) repo with a protocol, e.g. 'https://host/repo'.\n const slashIdx = arg.indexOf(\"/\", protoSepIdx + 3);\n if (slashIdx === -1) {\n throw new Error('invalid repository name, no \"/REPO\" after ' + \"hostame: \" + arg);\n }\n const indexName = arg.slice(0, slashIdx);\n remoteNameRaw = arg.slice(slashIdx + 1);\n index = parseIndex(indexName);\n }\n else {\n const parts = splitIntoTwo(arg, \"/\");\n if (parts.length === 1 ||\n /* or if parts[0] doesn't look like a hostname or IP */\n (parts[0].indexOf(\".\") === -1 &&\n parts[0].indexOf(\":\") === -1 &&\n parts[0] !== \"localhost\")) {\n // (B) repo without leading 'INDEX/'.\n if (defaultIndex === undefined) {\n index = parseIndex();\n }\n else if (typeof defaultIndex === \"string\") {\n index = parseIndex(defaultIndex);\n }\n else {\n index = defaultIndex;\n }\n remoteNameRaw = arg;\n }\n else {\n // (C) repo with leading 'INDEX/' (without protocol).\n index = parseIndex(parts[0]);\n remoteNameRaw = parts[1];\n }\n }\n // Validate remoteName (docker `validateRemoteName`).\n const nameParts = splitIntoTwo(remoteNameRaw, \"/\");\n let ns = \"\", name;\n if (nameParts.length === 2) {\n name = nameParts[1];\n // Validate ns.\n ns = nameParts[0];\n if (ns.length < 2 || ns.length > 255) {\n throw new Error(\"invalid repository namespace, must be between \" +\n \"2 and 255 characters: \" +\n ns);\n }\n if (!VALID_NS.test(ns)) {\n throw new Error(\"invalid repository namespace, may only contain \" +\n \"[a-z0-9._-] characters: \" +\n ns);\n }\n if (ns[0] === \"-\" && ns[ns.length - 1] === \"-\") {\n throw new Error(\"invalid repository namespace, cannot start or \" +\n \"end with a hypen: \" +\n ns);\n }\n if (ns.indexOf(\"--\") !== -1) {\n throw new Error(\"invalid repository namespace, cannot contain \" +\n \"consecutive hyphens: \" +\n ns);\n }\n }\n else {\n name = remoteNameRaw;\n if (index.official) {\n ns = \"library\";\n }\n }\n // Validate name.\n if (!VALID_REPO.test(name)) {\n throw new Error(\"invalid repository name, may only contain \" +\n \"[a-z0-9_/.-] characters: \" +\n name);\n }\n const isLibrary = index.official && ns === \"library\";\n const remoteName = ns ? `${ns}/${name}` : name;\n const localName = index.official\n ? isLibrary\n ? name\n : remoteName\n : `${index.name}/${remoteName}`;\n const canonicalName = index.official\n ? `${parseIndex().name}/${localName}`\n : localName;\n return {\n index,\n official: isLibrary,\n remoteName,\n localName,\n canonicalName,\n };\n}\n/**\n * Parse a docker repo and tag/digest string: [INDEX/]REPO[:TAG|@DIGEST|:TAG@DIGEST]\n *\n * Examples:\n * busybox\n * busybox:latest\n * google/python:3.3\n * docker.io/ubuntu\n * localhost:5000/blarg\n * http://localhost:5000/blarg:latest\n * google/python:3.3@sha256:fb9f16730ac6316afa4d97caa51302199...\n * alpine@sha256:fb9f16730ac6316afa4d97caa5130219927bfcecf0b0...\n *\n * Dev Notes:\n * - TODO Validation on digest and tag would be nice.\n *\n * @param arg {String} The docker repo:tag string to parse. See examples above.\n * @param defaultIndex {Object|String} Optional. The default index to use\n * if not specified with `arg`. If not given the default is 'docker.io'.\n * If given it may either be a string, e.g. 'https://myreg.example.com',\n * or parsed index object, as from `parseIndex()`.\n */\nexport function parseRepoAndRef(arg, defaultIndex) {\n // Parse off the tag/digest per\n // https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L69\n let digest = null;\n let tag = null;\n const atIdx = arg.lastIndexOf(\"@\");\n if (atIdx !== -1) {\n digest = arg.slice(atIdx + 1);\n arg = arg.slice(0, atIdx);\n }\n else {\n tag = DEFAULT_TAG;\n }\n const colonIdx = arg.lastIndexOf(\":\");\n const slashIdx = arg.lastIndexOf(\"/\");\n if (colonIdx !== -1 && colonIdx > slashIdx) {\n tag = arg.slice(colonIdx + 1);\n arg = arg.slice(0, colonIdx);\n }\n const repo = parseRepo(arg, defaultIndex);\n return {\n ...repo,\n digest,\n tag,\n canonicalRef: [\n repo.canonicalName,\n tag ? `:${tag}` : \"\",\n digest ? `@${digest}` : \"\",\n ].join(\"\"),\n };\n}\nexport const parseRepoAndTag = parseRepoAndRef;\n", "export function encodeHex(data) {\n return [...new Uint8Array(data)]\n .map((x) => x.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n/**\n * Calculate the 'Docker-Content-Digest' header for the given manifest.\n *\n * @returns {Promise<String>} The docker digest string.\n * @throws {InvalidContentError} if there is a problem parsing the manifest.\n */\nexport async function digestFromManifestStr(manifestStr) {\n let manifest;\n try {\n manifest = JSON.parse(manifestStr);\n }\n catch (thrown) {\n const err = thrown;\n throw new Error(`could not parse manifest: ${err.message}\\n${manifestStr}`);\n }\n if (manifest.schemaVersion === 1) {\n throw new Error(`schemaVersion 1 is not supported by /x/docker_registry_client.`);\n }\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(manifestStr));\n return `sha256:${encodeHex(hash)}`;\n}\n", "// Media type constants for Docker and OCI manifests\nexport const MEDIATYPE_MANIFEST_V2 = \"application/vnd.docker.distribution.manifest.v2+json\";\nexport const MEDIATYPE_MANIFEST_LIST_V2 = \"application/vnd.docker.distribution.manifest.list.v2+json\";\nexport const MEDIATYPE_OCI_MANIFEST_V1 = \"application/vnd.oci.image.manifest.v1+json\";\nexport const MEDIATYPE_OCI_MANIFEST_INDEX_V1 = \"application/vnd.oci.image.index.v1+json\";\nexport const MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1 = \"application/vnd.obsidianmd.plugin-manifest.v1+json\";\nexport const DEFAULT_USERAGENT = `open-obsidian-plugin-spec/0.1.0`;\n", "export const REALM = \"https://ghcr.io/token\";\nexport const SERVICE = \"ghcr.io\";\nexport const SCOPE_REPO_PREFIX = \"repository:\";\nexport const SCOPE_PULL_SUFFIX = \":pull\";\n", "/*\n * Error classes that docker-registry-client may produce.\n */\n/** Base class for custom error classes. */\nexport class ApiError extends Error {\n constructor(message) {\n super(message);\n this.name = new.target.name;\n Error.captureStackTrace?.(this, new.target);\n }\n}\nexport class HttpError extends ApiError {\n resp;\n errors;\n name = \"HttpError\";\n constructor(resp, errors, message) {\n super(message);\n this.resp = resp;\n this.errors = errors;\n }\n}\nexport class BadDigestError extends ApiError {\n name = \"BadDigestError\";\n}\nexport class InvalidContentError extends ApiError {\n name = \"InvalidContentError\";\n}\nexport class InternalError extends ApiError {\n name = \"InternalError\";\n}\nexport class ManifestVerificationError extends ApiError {\n name = \"ManifestVerificationError\";\n}\nexport class InvalidManifestError extends ApiError {\n name = \"InvalidManifestError\";\n}\nexport class DownloadError extends ApiError {\n name = \"DownloadError\";\n}\nexport class UploadError extends ApiError {\n name = \"UploadError\";\n}\nexport class BlobReadError extends ApiError {\n name = \"BlobReadError\";\n}\n// export class UnauthorizedError extends HttpError {\n// readonly name = 'UnauthorizedError';\n// readonly statusCode = 401;\n// }\nexport class TooManyRedirectsError extends ApiError {\n name = \"TooManyRedirectsError\";\n}\n", "const linkRegex = /^<([^>]+)>(?:\\s*;\\s*(.+))?$/;\nexport function parseLinkHeader(rawHeader) {\n if (!rawHeader)\n return [];\n return rawHeader\n .split(\",\")\n .slice(0, 5) // Arbitrary limit to how many links we are willing to parse\n .flatMap((piece) => {\n const matches = piece.trim().match(linkRegex);\n if (!matches)\n return [];\n const { rel, ...params } = matches[2]\n ?.split(\";\")\n .map((param) => param.trim().split(\"=\"))\n .reduce((acc, [key, value]) => {\n if (!value)\n return acc;\n if (value.startsWith('\"') && value.endsWith('\"')) {\n value = value.slice(1, -1);\n }\n acc[key] = value;\n return acc;\n }, {}) ?? {};\n if (!rel)\n return [];\n return [\n {\n rel,\n url: matches[1],\n params,\n },\n ];\n });\n}\n", "import { urlFromIndex } from \"../parsing/IndexParser.js\";\nimport { parseRepo } from \"../parsing/RepoParser.js\";\nimport { splitIntoTwo } from \"../utils/ValidationUtils.js\";\nimport { encodeHex, digestFromManifestStr } from \"../utils/DigestUtils.js\";\nimport { MEDIATYPE_MANIFEST_V2, MEDIATYPE_MANIFEST_LIST_V2, MEDIATYPE_OCI_MANIFEST_V1, MEDIATYPE_OCI_MANIFEST_INDEX_V1, MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1, } from \"../types/ManifestTypes.js\";\nimport { REALM, SERVICE } from \"../ghcr/GhcrConstants.js\";\nimport * as e from \"../errors/RegistryErrors.js\";\nimport { parseLinkHeader } from \"../parsing/LinkHeaderParser.js\";\nconst DEFAULT_USERAGENT = `open-obsidian-plugin-spec/0.1.0`;\n// Use globalThis.crypto (available in browsers/Electron/Node 18+)\nconst getCrypto = () => {\n if (!globalThis.crypto) {\n throw new Error(\"crypto API not available. This library requires Node.js 18+ or a modern browser environment.\");\n }\n return globalThis.crypto;\n};\n/*\n * Set the \"Authorization\" HTTP header into the headers object from the given\n * auth info.\n * - Bearer auth if `token`.\n * - Else, Basic auth if `username`.\n * - Else, if the authorization key exists, then it is removed from headers.\n */\nfunction _setAuthHeaderFromAuthInfo(headers, authInfo) {\n if (authInfo?.type === \"Bearer\") {\n headers[\"authorization\"] = \"Bearer \" + authInfo.token;\n }\n else if (authInfo?.type === \"Basic\") {\n const credentials = `${authInfo.username ?? \"\"}:${authInfo.password ?? \"\"}`;\n headers[\"authorization\"] = \"Basic \" + btoa(credentials);\n }\n else {\n delete headers[\"authorization\"];\n }\n return headers;\n}\n/**\n * Special handling of errors from the registry server.\n *\n * Some registry errors will use a custom error format, so detect those\n * and convert these as necessary.\n *\n * Example JSON response for a missing repo:\n * {\n * \"jse_shortmsg\": \"\",\n * \"jse_info\": {},\n * \"message\": \"{\\\"errors\\\":[{\\\"code\\\":\\\"UNAUTHORIZED\\\",\\\"message\\\":\\\"...}\\n\",\n * \"body\": {\n * \"errors\": [{\n * \"code\": \"UNAUTHORIZED\",\n * \"message\": \"authentication required\",\n * \"detail\": [{\n * \"Type\": \"repository\",\n * \"Class\": \"\",\n * \"Name\": \"library/idontexist\",\n * \"Action\": \"pull\"\n * }]\n * }]\n * }\n * }\n *\n * Example JSON response for bad username/password:\n * {\n * \"statusCode\": 401,\n * \"jse_shortmsg\":\"\",\n * \"jse_info\":{},\n * \"message\":\"{\\\"details\\\":\\\"incorrect username or password\\\"}\\n\",\n * \"body\":{\n * \"details\": \"incorrect username or password\"\n * }\n * }\n *\n * Example AWS token error:\n * {\n * \"statusCode\": 400,\n * \"errors\": [\n * {\n * \"code\": \"DENIED\",\n * \"message\": \"Your Authorization Token is invalid.\"\n * }\n * ]\n * }\n */\nfunction _getRegistryErrorMessage(err) {\n const e = err;\n if (e.body && typeof e.body === \"object\" && e.body !== null) {\n const body = e.body;\n if (Array.isArray(body.errors) && body.errors[0]) {\n return body.errors[0].message;\n }\n else if (body.details) {\n return body.details;\n }\n }\n if (Array.isArray(e.errors) && e.errors[0]) {\n return e.errors[0].message;\n }\n else if (e.message) {\n return e.message;\n }\n else if (e.details) {\n return e.details;\n }\n return String(err);\n}\n/**\n * Return a scope string to be used for an auth request. Example:\n * repository:library/nginx:pull\n */\nfunction _makeAuthScope(resource, name, actions) {\n return `${resource}:${name}:${actions.join(\",\")}`;\n}\n/*\n * Parse the 'Docker-Content-Digest' header.\n *\n * @throws {BadDigestError} if the value is missing or malformed\n */\nfunction _parseDockerContentDigest(dcd) {\n if (!dcd)\n throw new e.BadDigestError('missing \"Docker-Content-Digest\" header');\n const errPre = `could not parse Docker-Content-Digest header \"${dcd}\": `;\n // E.g. docker-content-digest: sha256:887f7ecfd0bda3...\n const parts = splitIntoTwo(dcd, \":\");\n if (parts.length !== 2)\n throw new e.BadDigestError(errPre + JSON.stringify(dcd));\n if (parts[0] !== \"sha256\")\n throw new e.BadDigestError(errPre + \"Unsupported hash algorithm \" + JSON.stringify(parts[0]));\n return {\n raw: dcd,\n algorithm: parts[0],\n expectedDigest: parts[1],\n async validate(buffer) {\n switch (this.algorithm) {\n case \"sha256\": {\n const hashBuffer = await getCrypto().subtle.digest(\"SHA-256\", buffer);\n const digest = encodeHex(hashBuffer);\n if (this.expectedDigest !== digest) {\n throw new e.BadDigestError(`Docker-Content-Digest mismatch (expected: ${this.expectedDigest}, got: ${digest})`);\n }\n return;\n }\n default:\n throw new e.BadDigestError(`Unsupported hash algorithm ${this.algorithm}`);\n }\n },\n };\n}\nexport class OciRegistryClient {\n version = 2;\n insecure;\n repo;\n acceptOCIManifests;\n acceptManifestLists;\n username;\n password;\n scopes;\n _loggedIn;\n _loggedInScope;\n _authInfo;\n _headers;\n _url;\n _userAgent;\n _adapter;\n /**\n * Create a new GHCR client for a particular repository.\n *\n * @param opts.insecure {Boolean} Optional. Default false. Set to true\n * to *not* fail on an invalid or this-signed server certificate.\n * @param opts.adapter {FetchAdapter} Required. HTTP adapter for making requests.\n * ... TODO: lots more to document\n *\n */\n constructor(opts) {\n this.insecure = Boolean(opts.insecure);\n if (opts.repo) {\n this.repo = opts.repo;\n }\n else if (opts.name) {\n this.repo = parseRepo(opts.name);\n }\n else\n throw new Error(`name or repo required`);\n this.acceptOCIManifests = opts.acceptOCIManifests ?? true;\n this.acceptManifestLists = opts.acceptManifestLists ?? false;\n this.username = opts.username;\n this.password = opts.password;\n this.scopes = opts.scopes ?? [\"pull\"];\n this._loggedIn = false;\n this._loggedInScope = null; // Keeps track of the login type.\n this._authInfo = null;\n this._headers = {};\n if (opts.token) {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"Bearer\",\n token: opts.token,\n });\n }\n else if (opts.username || opts.password) {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"Basic\",\n username: opts.username ?? \"\",\n password: opts.password ?? \"\",\n });\n }\n else {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"None\",\n });\n }\n this._url = urlFromIndex(this.repo.index, opts.scheme);\n this._userAgent = opts.userAgent || DEFAULT_USERAGENT;\n this._adapter = opts.adapter;\n }\n /**\n * Login V2\n *\n * Typically one does not need to call this function directly because most\n * methods of a `GHCRClient` will automatically login as necessary.\n *\n * @param opts {Object}\n * - opts.scope {String} Optional. A scope string passed in for\n * bearer/token auth. If this is just a login request where the token\n * won't be used, then the empty string (the default) is sufficient.\n * // JSSTYLED\n * See <https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md#requesting-a-token>\n * @return an object with authentication info\n */\n async performLogin(opts) {\n return {\n type: \"Bearer\",\n token: await this._getToken({\n realm: REALM,\n service: SERVICE,\n scopes: opts.scope ? [opts.scope] : [],\n }),\n };\n }\n /**\n * Get an auth token.\n *\n * See: docker/docker.git:registry/token.go\n */\n async _getToken(opts) {\n // - add https:// prefix (or http) if none on 'realm'\n let tokenUrl = opts.realm;\n const match = /^(\\w+):\\/\\//.exec(tokenUrl);\n if (!match) {\n tokenUrl = (this.insecure ? \"http\" : \"https\") + \"://\" + tokenUrl;\n }\n else if (match[1] && [\"http\", \"https\"].indexOf(match[1]) === -1) {\n // TODO: Verify the logic above\n throw new Error(\"unsupported scheme for \" +\n `WWW-Authenticate realm \"${opts.realm}\": \"${match[1]}\"`);\n }\n // - GET $realm\n // ?service=$service\n // (&scope=$scope)*\n // (&account=$username)\n // Authorization: Basic ...\n const headers = {};\n const query = new URLSearchParams();\n if (opts.service) {\n query.set(\"service\", opts.service);\n }\n if (opts.scopes && opts.scopes.length) {\n for (const scope of opts.scopes) {\n query.append(\"scope\", scope); // intentionally singular 'scope'\n }\n }\n if (this.username) {\n query.set(\"account\", this.username);\n _setAuthHeaderFromAuthInfo(headers, {\n type: \"Basic\",\n username: this.username,\n password: this.password ?? \"\",\n });\n }\n if (query.toString()) {\n tokenUrl += \"?\" + query.toString();\n }\n // log.trace({tokenUrl: tokenUrl}, '_getToken: url');\n headers[\"user-agent\"] = this._userAgent;\n const resp = await this._adapter.fetch(tokenUrl, {\n method: \"GET\",\n headers: headers,\n });\n if (resp.status === 401) {\n // Convert *all* 401 errors to use a generic error constructor\n // with a simple error message.\n const body = await resp.json();\n const errMsg = _getRegistryErrorMessage(body);\n throw new Error(`Registry auth failed: ${errMsg}`);\n }\n if (resp.status !== 200) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${tokenUrl}`);\n }\n const body = (await resp.json());\n if (typeof body?.token !== \"string\") {\n console.error(\"TODO: auth resp:\", body);\n throw new Error(\"authorization \" + \"server did not include a token in the response\");\n }\n return body.token;\n }\n /**\n * Get a registry session (i.e. login to the registry).\n *\n * Typically one does not need to call this method directly because most\n * methods of a client will automatically login as necessary.\n *\n * @param opts {Object} Optional.\n * - opts.scope {String} Optional. Scope to use in the auth Bearer token.\n *\n * Side-effects:\n * - On success, all of `this._loggedIn*`, `this._authInfo`, and\n * `this._headers.authorization` are set.\n */\n async login(opts = {}) {\n const scope = opts.scope ||\n _makeAuthScope(\"repository\", this.repo.remoteName, this.scopes);\n if (this._loggedIn && this._loggedInScope === scope) {\n return;\n }\n const authInfo = await this.performLogin({\n scope: scope,\n });\n this._loggedIn = true;\n this._loggedInScope = scope;\n this._authInfo = authInfo;\n _setAuthHeaderFromAuthInfo(this._headers, authInfo);\n // this.log.trace({err: err, loggedIn: this._loggedIn}, 'login: done');\n }\n async listTags(props = {}) {\n const searchParams = new URLSearchParams();\n if (props.pageSize != null)\n searchParams.set(\"n\", `${props.pageSize}`);\n if (props.startingAfter != null)\n searchParams.set(\"last\", props.startingAfter);\n await this.login();\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName)}/tags/list`, this._url);\n url.search = searchParams.toString();\n const headers = { ...this._headers, \"user-agent\": this._userAgent };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers,\n });\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n return (await resp.json());\n }\n async listAllTags(props = {}) {\n const pages = [];\n for await (const page of this.listTagsPaginated(props)) {\n pages.push(page);\n }\n const firstPage = pages.shift();\n for (const nextPage of pages) {\n firstPage.tags = [...firstPage.tags, ...nextPage.tags];\n }\n return firstPage;\n }\n async *listTagsPaginated(props = {}) {\n await this.login();\n let path = `/v2/${encodeURI(this.repo.remoteName)}/tags/list`;\n if (props.pageSize != null) {\n path += `?n=${props.pageSize}`;\n }\n while (path) {\n const url = new URL(path, this._url);\n const headers = { ...this._headers, \"user-agent\": this._userAgent };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers,\n });\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n const linkHeader = resp.headers.get(\"link\");\n const links = parseLinkHeader(linkHeader ?? null);\n const nextLink = links.find((x) => x.rel == \"next\");\n // If there's no next link then we use a null to end the loop.\n path = nextLink?.url ?? null;\n yield (await resp.json());\n }\n }\n /*\n * Get an image manifest. `ref` is either a tag or a digest.\n * <https://docs.docker.com/registry/spec/api/#pulling-an-image-manifest>\n *\n * Note that docker-content-digest header can be undefined, so if you\n * need a manifest digest, use the `digestFromManifestStr` function.\n */\n async getManifest(opts) {\n const acceptOCIManifests = opts.acceptOCIManifests ?? this.acceptOCIManifests;\n const acceptManifestLists = opts.acceptManifestLists ?? this.acceptManifestLists;\n await this.login();\n const headers = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n };\n const acceptTypes = [MEDIATYPE_MANIFEST_V2];\n if (acceptManifestLists) {\n acceptTypes.push(MEDIATYPE_MANIFEST_LIST_V2);\n }\n if (acceptOCIManifests) {\n acceptTypes.push(MEDIATYPE_OCI_MANIFEST_V1);\n if (acceptManifestLists) {\n acceptTypes.push(MEDIATYPE_OCI_MANIFEST_INDEX_V1);\n }\n }\n headers[\"accept\"] = acceptTypes.join(\", \");\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName ?? \"\")}/manifests/${encodeURI(opts.ref)}`, this._url);\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers: headers,\n redirect: opts.followRedirects == false ? \"manual\" : \"follow\",\n });\n if (resp.status === 401) {\n const body = await resp.json();\n const errMsg = _getRegistryErrorMessage(body);\n throw new Error(`Manifest ${JSON.stringify(opts.ref)} Not Found: ${errMsg}`);\n }\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n const manifest = (await resp.json());\n if (manifest.schemaVersion === 1) {\n throw new Error(`schemaVersion 1 is not supported by /x/docker_registry_client.`);\n }\n return { resp, manifest };\n }\n /**\n * Makes a http request to the given url, following any redirects, then fires\n * the callback(err, req, responses) with the result.\n *\n * Note that 'responses' is an *array* of Response objects, with\n * the last response being at the end of the array. When there is more than\n * one response, it means a redirect has been followed.\n */\n async _makeHttpRequest(opts) {\n const followRedirects = opts.followRedirects ?? true;\n const maxRedirects = opts.maxRedirects ?? 3;\n let numRedirs = 0;\n const req = {\n path: opts.path,\n headers: opts.headers,\n };\n const ress = new Array();\n while (numRedirs < maxRedirects) {\n numRedirs += 1;\n const url = new URL(req.path, this._url);\n const headers = {\n ...req.headers,\n \"user-agent\": this._userAgent,\n };\n const resp = await this._adapter.fetch(url.toString(), {\n method: opts.method,\n headers: headers,\n redirect: \"manual\",\n });\n ress.push(resp);\n if (!followRedirects)\n return ress;\n if (!(resp.status === 302 || resp.status === 307))\n return ress;\n const location = resp.headers.get(\"location\");\n if (!location)\n return ress;\n const loc = new URL(location, url);\n // this.log.trace({numRedirs: numRedirs, loc: loc}, 'got redir response');\n req.path = loc.toString();\n req.headers = {};\n }\n throw new e.TooManyRedirectsError(`maximum number of redirects (${maxRedirects}) hit`);\n }\n async _headOrGetBlob(method, digest) {\n await this.login();\n return await this._makeHttpRequest({\n method: method,\n path: `/v2/${encodeURI(this.repo.remoteName ?? \"\")}/blobs/${encodeURI(digest)}`,\n headers: this._headers,\n });\n }\n /*\n * Get an image file blob -- just the headers. See `getBlob`.\n *\n * <https://docs.docker.com/registry/spec/api/#get-blob>\n * <https://docs.docker.com/registry/spec/api/#pulling-an-image-manifest>\n *\n * This endpoint can return 3xx redirects. The first response often redirects\n * to an object CDN, which would then return the raw data.\n *\n * Interesting headers:\n * - `ress[0].headers.get('docker-content-digest')` is the digest of the\n * content to be downloaded\n * - `ress[-1].headers.get('content-length')` is the number of bytes to download\n * - `ress[-1].headers[*]` as appropriate for HTTP caching, range gets, etc.\n */\n async headBlob(opts) {\n const resp = await this._headOrGetBlob(\"HEAD\", opts.digest);\n // No need to cancel body - fetch returns complete responses\n return resp;\n }\n /**\n * Download a blob and return its ArrayBuffer.\n * <https://docs.docker.com/registry/spec/api/#get-blob>\n *\n * @return\n * The `buffer` is the blob's content as an ArrayBuffer.\n * `ress` (plural of 'res') is an array of responses\n * after following redirects. The full set of responses are returned mainly because\n * headers on both the first, e.g. 'Docker-Content-Digest', and last,\n * e.g. 'Content-Length', might be interesting.\n */\n async downloadBlob(opts) {\n const ress = await this._headOrGetBlob(\"GET\", opts.digest);\n const lastResp = ress[ress.length - 1];\n if (!lastResp) {\n throw new e.BlobReadError(`No response available for blob ${opts.digest}`);\n }\n const buffer = await lastResp.arrayBuffer();\n const dcdHeader = ress[0]?.headers.get(\"docker-content-digest\");\n if (dcdHeader) {\n const dcdInfo = _parseDockerContentDigest(dcdHeader);\n if (dcdInfo.raw !== opts.digest) {\n throw new e.BadDigestError(`Docker-Content-Digest header, ${dcdInfo.raw}, does not match ` +\n `given digest, ${opts.digest}`);\n }\n // Validate the digest\n await dcdInfo.validate(buffer);\n }\n return { ress, buffer };\n }\n /**\n * Upload a blob using POST then PUT workflow.\n * <https://github.com/opencontainers/distribution-spec/blob/main/spec.md#post-then-put>\n *\n * @param opts.data The blob data as ArrayBuffer or Uint8Array\n * @param opts.digest Optional digest. If not provided, it will be calculated.\n * @returns Object with digest and size of the uploaded blob\n */\n async pushBlob(opts) {\n await this.login();\n // Convert to ArrayBuffer if needed\n const buffer = opts.data instanceof Uint8Array\n ? new Uint8Array(opts.data).buffer\n : opts.data;\n // Calculate digest\n const hashBuffer = await getCrypto().subtle.digest(\"SHA-256\", buffer);\n const digest = `sha256:${encodeHex(hashBuffer)}`;\n // Step 1: POST to initiate upload\n const postUrl = new URL(`/v2/${encodeURI(this.repo.remoteName)}/blobs/uploads/`, this._url);\n const postHeaders = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-length\": \"0\",\n };\n const postResp = await this._adapter.fetch(postUrl.toString(), {\n method: \"POST\",\n headers: postHeaders,\n });\n if (postResp.status !== 202) {\n throw new Error(`Failed to initiate blob upload: HTTP ${postResp.status}`);\n }\n // Get upload URL from Location header\n const uploadLocation = postResp.headers.get(\"location\");\n if (!uploadLocation) {\n throw new Error(\"No Location header in POST response\");\n }\n // Step 2: PUT to upload blob\n const uploadUrl = new URL(uploadLocation, this._url);\n uploadUrl.searchParams.set(\"digest\", digest);\n const putHeaders = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-type\": \"application/octet-stream\",\n \"content-length\": buffer.byteLength.toString(),\n };\n const putResp = await this._adapter.fetch(uploadUrl.toString(), {\n method: \"PUT\",\n headers: putHeaders,\n body: buffer,\n });\n if (putResp.status !== 201) {\n throw new Error(`Failed to upload blob: HTTP ${putResp.status}`);\n }\n // Verify digest from response\n const returnedDigest = putResp.headers.get(\"docker-content-digest\");\n if (returnedDigest && returnedDigest !== digest) {\n throw new e.BadDigestError(`Digest mismatch: expected ${digest}, got ${returnedDigest}`);\n }\n return { digest, size: buffer.byteLength };\n }\n /**\n * Upload an image manifest.\n * <https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests>\n *\n * @param opts.ref The tag or digest to push to\n * @param opts.manifest The manifest object to upload\n * @param opts.mediaType Optional media type (defaults to OCI manifest type)\n * @returns Object with digest and size of the uploaded manifest\n */\n async pushManifest(opts) {\n await this.login();\n const manifestStr = JSON.stringify(opts.manifest);\n const manifestBuffer = new TextEncoder().encode(manifestStr);\n // Calculate digest\n const digest = await digestFromManifestStr(manifestStr);\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName)}/manifests/${encodeURI(opts.ref)}`, this._url);\n const headers = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-type\": opts.mediaType || \"application/vnd.oci.image.manifest.v1+json\",\n \"content-length\": manifestBuffer.byteLength.toString(),\n };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"PUT\",\n headers,\n body: manifestBuffer,\n });\n if (resp.status !== 201) {\n throw new Error(`Failed to push manifest: HTTP ${resp.status}`);\n }\n // Verify digest from response\n const returnedDigest = resp.headers.get(\"docker-content-digest\");\n if (returnedDigest && returnedDigest !== digest) {\n throw new e.BadDigestError(`Digest mismatch: expected ${digest}, got ${returnedDigest}`);\n }\n return { digest, size: manifestBuffer.byteLength };\n }\n /**\n * Push a plugin manifest as a config blob and create an OCI manifest.\n * This follows the OCI spec where the Obsidian manifest is stored as the config.\n *\n * @param opts.ref The tag or digest to push to\n * @param opts.pluginManifest The Obsidian plugin manifest\n * @param opts.layers The layer descriptors (main.js, styles.css, etc.)\n * @param opts.annotations Optional annotations for the OCI manifest\n * @returns Object with digest, configDigest, and the created manifest\n */\n async pushPluginManifest(opts) {\n // Step 1: Push plugin manifest as config blob\n const manifestStr = JSON.stringify(opts.pluginManifest);\n const manifestBuffer = new TextEncoder().encode(manifestStr);\n const configResult = await this.pushBlob({\n data: manifestBuffer,\n });\n // Step 2: Build OCI manifest with plugin manifest as config\n const manifest = {\n schemaVersion: 2,\n mediaType: MEDIATYPE_OCI_MANIFEST_V1,\n artifactType: \"application/vnd.obsidian.plugin.v1+json\",\n config: {\n mediaType: MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1,\n digest: configResult.digest,\n size: configResult.size,\n },\n layers: opts.layers,\n annotations: opts.annotations,\n };\n // Step 3: Push the OCI manifest\n const manifestResult = await this.pushManifest({\n ref: opts.ref,\n manifest,\n mediaType: MEDIATYPE_OCI_MANIFEST_V1,\n });\n return {\n digest: manifestResult.digest,\n configDigest: configResult.digest,\n manifest,\n };\n }\n /**\n * Pull a plugin manifest by extracting it from the OCI config blob.\n * This follows the OCI spec where the Obsidian manifest is stored as the config.\n *\n * @param opts.ref The tag or digest to pull\n * @returns Object with the plugin manifest, OCI manifest, and digests\n */\n async pullPluginManifest(opts) {\n // Step 1: Pull the OCI manifest\n const manifestResult = await this.getManifest({ ref: opts.ref });\n const manifest = manifestResult.manifest;\n // Step 2: Validate manifest has config\n if (!(\"config\" in manifest) || !manifest.config) {\n throw new Error(\"Manifest does not contain a config\");\n }\n const ociManifest = manifest;\n const manifestDigest = manifestResult.resp.headers.get(\"docker-content-digest\") || \"\";\n // Step 3: Pull the config blob\n const { buffer: configBuffer } = await this.downloadBlob({\n digest: ociManifest.config.digest,\n });\n // Step 4: Parse the plugin manifest from config\n const configText = new TextDecoder().decode(configBuffer);\n const pluginManifest = JSON.parse(configText);\n return {\n pluginManifest,\n manifest: ociManifest,\n manifestDigest,\n configDigest: ociManifest.config.digest,\n };\n }\n}\n", "import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ObsidianManifest } from \"shard-lib\";\n\nexport interface DiscoveredPlugin {\n directory: string;\n manifest: {\n path: string;\n content: ArrayBuffer;\n parsed: ObsidianManifest;\n };\n mainJs: {\n path: string;\n content: ArrayBuffer;\n };\n stylesCss?: {\n path: string;\n content: ArrayBuffer;\n };\n}\n\n/**\n * Discover plugin files in a directory.\n * Finds manifest.json (required), main.js (required), and styles.css (optional).\n *\n * @param directory - Directory to scan for plugin files\n * @returns Discovered plugin information\n * @throws Error if required files are missing or invalid\n */\nexport async function discoverPlugin(\n directory: string,\n): Promise<DiscoveredPlugin> {\n // Resolve absolute path\n const absDirectory = path.resolve(directory);\n\n // Check directory exists\n try {\n const stat = await fs.stat(absDirectory);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${directory}`);\n }\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`Directory not found: ${directory}`);\n }\n throw err;\n }\n\n // Find manifest.json (required)\n const manifestPath = path.join(absDirectory, \"manifest.json\");\n let manifestContent: ArrayBuffer;\n let manifestParsed: ObsidianManifest;\n\n try {\n const buffer = await fs.readFile(manifestPath);\n manifestContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n\n // Parse and validate manifest\n const text = new TextDecoder().decode(manifestContent);\n const parsed = JSON.parse(text) as Record<string, unknown>;\n\n // Validate required fields\n if (!parsed.version || typeof parsed.version !== \"string\") {\n throw new Error('manifest.json missing required \"version\" field');\n }\n if (!parsed.id || typeof parsed.id !== \"string\") {\n throw new Error('manifest.json missing required \"id\" field');\n }\n if (!parsed.name || typeof parsed.name !== \"string\") {\n throw new Error('manifest.json missing required \"name\" field');\n }\n if (!parsed.minAppVersion || typeof parsed.minAppVersion !== \"string\") {\n throw new Error('manifest.json missing required \"minAppVersion\" field');\n }\n if (!parsed.description || typeof parsed.description !== \"string\") {\n throw new Error('manifest.json missing required \"description\" field');\n }\n if (!parsed.author || typeof parsed.author !== \"string\") {\n throw new Error('manifest.json missing required \"author\" field');\n }\n\n manifestParsed = parsed as unknown as ObsidianManifest;\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`manifest.json not found in ${directory}`);\n }\n if (err instanceof SyntaxError) {\n throw new Error(`Could not parse manifest.json: ${err.message}`);\n }\n throw err;\n }\n\n // Find main.js (required)\n const mainJsPath = path.join(absDirectory, \"main.js\");\n let mainJsContent: ArrayBuffer;\n\n try {\n const buffer = await fs.readFile(mainJsPath);\n mainJsContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`main.js not found in ${directory}`);\n }\n throw err;\n }\n\n // Find styles.css (optional)\n const stylesCssPath = path.join(absDirectory, \"styles.css\");\n let stylesCssContent: ArrayBuffer | undefined;\n\n try {\n const buffer = await fs.readFile(stylesCssPath);\n stylesCssContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n // styles.css is optional, ignore error\n stylesCssContent = undefined;\n } else {\n throw err;\n }\n }\n\n return {\n directory: absDirectory,\n manifest: {\n path: manifestPath,\n content: manifestContent,\n parsed: manifestParsed,\n },\n mainJs: {\n path: mainJsPath,\n content: mainJsContent,\n },\n ...(stylesCssContent && {\n stylesCss: {\n path: stylesCssPath,\n content: stylesCssContent,\n },\n }),\n };\n}\n", "import { OciRegistryClient, parseRepoAndRef } from \"shard-lib\";\nimport type {\n ManifestOCIDescriptor,\n FetchAdapter,\n ObsidianManifest,\n} from \"shard-lib\";\nimport { discoverPlugin } from \"../lib/plugin.js\";\nimport { Logger } from \"../lib/logger.js\";\n\n/**\n * Derive GitHub repository URL from registry remote name.\n * Takes the first two path components to form owner/repo.\n *\n * Examples:\n * shard-for-obsidian/shard -> https://github.com/shard-for-obsidian/shard\n * shard-for-obsidian/shard/plugin -> https://github.com/shard-for-obsidian/shard\n */\nfunction deriveGitHubUrl(remoteName: string): string {\n const parts = remoteName.split('/');\n if (parts.length < 2) {\n throw new Error(\n `Cannot derive GitHub URL from ${remoteName}. Need at least owner/repo format.`,\n );\n }\n\n const owner = parts[0];\n const repo = parts[1];\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport interface PushOptions {\n directory: string;\n repository: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface PushResult {\n digest: string;\n tag: string;\n size: number;\n repository: string;\n}\n\n/**\n * Push an Obsidian plugin to GHCR.\n *\n * @param opts - Push options\n * @returns Push result with digest and tag\n */\nexport async function pushCommand(opts: PushOptions): Promise<PushResult> {\n const { directory, repository, token, logger, adapter } = opts;\n\n // Step 1: Discover plugin files\n logger.log(`Discovering plugin files in ${directory}...`);\n const plugin = await discoverPlugin(directory);\n const version = plugin.manifest.parsed.version;\n logger.log(`Found plugin version ${version}`);\n\n // Step 2: Parse repository and add version tag\n const fullRef = repository.includes(\":\")\n ? repository\n : `${repository}:${version}`;\n logger.log(`Pushing to ${fullRef}...`);\n\n const ref = parseRepoAndRef(fullRef);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n scopes: [\"push\", \"pull\"],\n });\n\n // Step 3: Push each file as a blob\n const layers: ManifestOCIDescriptor[] = [];\n\n // No longer need to push manifest.json as a layer - it's now in the config\n\n // Push main.js\n logger.log(\"Pushing main.js...\");\n const mainJsResult = await client.pushBlob({\n data: plugin.mainJs.content,\n });\n layers.push({\n mediaType: \"application/javascript\",\n digest: mainJsResult.digest,\n size: mainJsResult.size,\n annotations: {\n \"org.opencontainers.image.title\": \"main.js\",\n },\n });\n logger.log(\n `Pushed main.js: ${mainJsResult.digest.slice(0, 19)}... (${mainJsResult.size} bytes)`,\n );\n\n // Push styles.css if present\n if (plugin.stylesCss) {\n logger.log(\"Pushing styles.css...\");\n const stylesCssResult = await client.pushBlob({\n data: plugin.stylesCss.content,\n });\n layers.push({\n mediaType: \"text/css\",\n digest: stylesCssResult.digest,\n size: stylesCssResult.size,\n annotations: {\n \"org.opencontainers.image.title\": \"styles.css\",\n },\n });\n logger.log(\n `Pushed styles.css: ${stylesCssResult.digest.slice(0, 19)}... (${stylesCssResult.size} bytes)`,\n );\n }\n\n // Step 4: Derive GitHub URL and prepare annotations\n const githubUrl = deriveGitHubUrl(ref.remoteName);\n const manifest = plugin.manifest.parsed;\n\n const annotations: Record<string, string> = {\n \"org.opencontainers.image.created\": new Date().toISOString(),\n \"org.opencontainers.image.source\": githubUrl,\n \"org.opencontainers.image.version\": manifest.version,\n \"org.opencontainers.image.description\": manifest.description,\n \"org.opencontainers.image.authors\": manifest.author,\n };\n\n // Add optional fields if present\n if (manifest.authorUrl) {\n annotations[\"org.opencontainers.image.url\"] = manifest.authorUrl;\n }\n\n // Step 5: Push plugin manifest using new method\n logger.log(\"Pushing plugin manifest...\");\n const manifestPushResult = await client.pushPluginManifest({\n ref: ref.tag || version,\n pluginManifest: manifest as unknown as ObsidianManifest,\n layers,\n annotations,\n });\n\n logger.success(`Successfully pushed ${fullRef}`);\n logger.log(`Manifest digest: ${manifestPushResult.digest}`);\n logger.log(`GitHub repository: ${githubUrl}`);\n\n // Calculate total size from manifest\n const totalSize = manifestPushResult.manifest.layers.reduce(\n (sum, layer) => sum + layer.size,\n 0,\n );\n\n return {\n digest: manifestPushResult.digest,\n tag: ref.tag || version,\n size: totalSize,\n repository: fullRef,\n };\n}\n", "import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { OciRegistryClient, parseRepoAndRef } from \"shard-lib\";\nimport type { FetchAdapter } from \"shard-lib\";\nimport { Logger } from \"../lib/logger.js\";\n\nexport interface PullOptions {\n repository: string;\n output: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface PullResult {\n files: string[];\n output: string;\n digest: string;\n}\n\n/**\n * Pull an Obsidian plugin from GHCR.\n *\n * @param opts - Pull options\n * @returns Pull result with extracted files\n */\nexport async function pullCommand(opts: PullOptions): Promise<PullResult> {\n const { repository, output, token, logger, adapter } = opts;\n\n // Step 1: Parse repository reference\n logger.log(`Pulling ${repository}...`);\n const ref = parseRepoAndRef(repository);\n\n if (!ref.tag && !ref.digest) {\n throw new Error(\"Repository reference must include tag or digest\");\n }\n\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n });\n\n // Step 2: Fetch manifest and extract plugin manifest from config\n logger.log(\"Fetching manifest...\");\n const refString = ref.tag || ref.digest || \"\";\n const pullResult = await client.pullPluginManifest({ ref: refString });\n const manifest = pullResult.manifest;\n\n logger.log(`Manifest digest: ${pullResult.manifestDigest}`);\n\n // Step 3: Create output directory if needed\n const absOutput = path.resolve(output);\n logger.log(`Creating output directory: ${absOutput}`);\n await fs.mkdir(absOutput, { recursive: true });\n\n // Step 4: Write manifest.json from config\n const manifestPath = path.join(absOutput, \"manifest.json\");\n const manifestJson = JSON.stringify(pullResult.pluginManifest, null, 2);\n await fs.writeFile(manifestPath, manifestJson, \"utf-8\");\n logger.log(`Wrote manifest.json (${manifestJson.length} bytes)`);\n\n const files: string[] = [\"manifest.json\"];\n\n // Step 5: Download and extract each layer\n for (const layer of manifest.layers) {\n // Extract filename from annotation\n const filename = layer.annotations?.[\"org.opencontainers.image.title\"];\n if (!filename) {\n throw new Error(\n `Layer ${layer.digest} missing required filename annotation`,\n );\n }\n\n logger.log(`Downloading ${filename}...`);\n\n // Download blob\n const blobResult = await client.downloadBlob({\n digest: layer.digest,\n });\n\n // Write to output directory\n const filePath = path.join(absOutput, filename);\n const buffer = Buffer.from(blobResult.buffer);\n await fs.writeFile(filePath, buffer);\n\n logger.log(`Wrote ${filename} (${buffer.length} bytes)`);\n files.push(filename);\n }\n\n logger.success(`Successfully pulled ${repository}`);\n logger.log(`Files extracted to: ${absOutput}`);\n\n return {\n files,\n output: absOutput,\n digest: pullResult.manifestDigest,\n };\n}\n", "/**\n * URL to the Obsidian community plugins JSON file\n */\nexport const COMMUNITY_PLUGINS_URL =\n \"https://raw.githubusercontent.com/obsidianmd/obsidian-releases/refs/heads/master/community-plugins.json\";\n\n/**\n * Represents a community plugin entry from the Obsidian marketplace\n */\nexport interface CommunityPlugin {\n /** Plugin ID (used in folder name and as identifier) */\n id: string;\n /** Display name of the plugin */\n name: string;\n /** Author name */\n author: string;\n /** Plugin description */\n description: string;\n /** GitHub repository in format \"owner/repo\" */\n repo: string;\n}\n", "import type { FetchAdapter } from \"shard-lib\";\nimport {\n COMMUNITY_PLUGINS_URL,\n type CommunityPlugin,\n} from \"./community-plugins.js\";\n\n/**\n * Cache for Obsidian community plugins.\n * Fetches and caches the list of community plugins from the official repository.\n */\nexport class CommunityPluginsCache {\n private adapter: FetchAdapter;\n private plugins: CommunityPlugin[] | null = null;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n }\n\n /**\n * Fetch the list of community plugins.\n * Results are cached for subsequent calls.\n *\n * @returns Array of community plugins\n * @throws Error if fetch fails\n */\n async fetch(): Promise<CommunityPlugin[]> {\n // Return cached data if available\n if (this.plugins !== null) {\n return this.plugins;\n }\n\n // Fetch from URL\n const response = await this.adapter.fetch(COMMUNITY_PLUGINS_URL);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch community plugins: ${response.status}`);\n }\n\n const plugins = (await response.json()) as CommunityPlugin[];\n this.plugins = plugins;\n return plugins;\n }\n\n /**\n * Find a plugin by ID.\n * Fetches the plugin list if not already cached.\n *\n * @param id - Plugin ID to search for\n * @returns Plugin if found, undefined otherwise\n */\n async findPlugin(id: string): Promise<CommunityPlugin | undefined> {\n const plugins = await this.fetch();\n return plugins.find((plugin) => plugin.id === id);\n }\n}\n", "import type { FetchAdapter } from \"shard-lib\";\n\n/**\n * Represents a GitHub release asset\n */\nexport interface GitHubReleaseAsset {\n /** Name of the asset file */\n name: string;\n /** URL to download the asset */\n browser_download_url: string;\n}\n\n/**\n * Represents a GitHub release\n */\nexport interface GitHubRelease {\n /** Tag name of the release (e.g., \"1.2.3\") */\n tag_name: string;\n /** Array of release assets */\n assets: GitHubReleaseAsset[];\n}\n\n/**\n * Fetcher for GitHub releases.\n * Provides methods to fetch releases from the GitHub API.\n */\nexport class GitHubReleaseFetcher {\n private adapter: FetchAdapter;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n }\n\n /**\n * Fetch the latest release from a GitHub repository.\n *\n * @param repo - Repository in format \"owner/repo\"\n * @param token - Optional GitHub token for authentication\n * @returns Latest release information\n * @throws Error if fetch fails\n */\n async fetchLatestRelease(\n repo: string,\n token?: string,\n ): Promise<GitHubRelease> {\n const url = `https://api.github.com/repos/${repo}/releases/latest`;\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await this.adapter.fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch latest release: ${response.status}`);\n }\n\n return (await response.json()) as GitHubRelease;\n }\n\n /**\n * Fetch a specific release by tag from a GitHub repository.\n *\n * @param repo - Repository in format \"owner/repo\"\n * @param tag - Tag name (e.g., \"1.2.3\")\n * @param token - Optional GitHub token for authentication\n * @returns Release information\n * @throws Error if fetch fails\n */\n async fetchReleaseByTag(\n repo: string,\n tag: string,\n token?: string,\n ): Promise<GitHubRelease> {\n const url = `https://api.github.com/repos/${repo}/releases/tags/${tag}`;\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await this.adapter.fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch release ${tag}: ${response.status}`);\n }\n\n return (await response.json()) as GitHubRelease;\n }\n}\n", "import type {\n FetchAdapter,\n ObsidianManifest,\n ManifestOCIDescriptor,\n} from \"shard-lib\";\nimport { OciRegistryClient, parseRepoAndRef } from \"shard-lib\";\nimport { CommunityPluginsCache } from \"./community-cache.js\";\nimport { GitHubReleaseFetcher } from \"./github-release.js\";\n\n/**\n * Options for converting a plugin\n */\nexport interface ConvertPluginOptions {\n /** Plugin ID from community plugins list */\n pluginId: string;\n /** Optional specific version to convert (defaults to latest) */\n version?: string;\n /** Target OCI repository */\n repository: string;\n /** GitHub token for authentication */\n token: string;\n}\n\n/**\n * Result of a plugin conversion\n */\nexport interface ConvertPluginResult {\n /** Plugin ID */\n pluginId: string;\n /** Plugin version */\n version: string;\n /** Target repository with tag */\n repository: string;\n /** GitHub repository URL */\n githubRepo: string;\n /** Parsed manifest */\n manifest: ObsidianManifest;\n /** main.js content */\n mainJs: string;\n /** styles.css content (if present) */\n stylesCss?: string;\n}\n\n/**\n * Options for pushing to registry\n */\nexport interface PushToRegistryOptions {\n /** Repository with tag */\n repository: string;\n /** GitHub repository URL */\n githubRepo: string;\n /** GitHub token */\n token: string;\n /** Plugin data to push */\n pluginData: {\n manifest: ObsidianManifest;\n mainJs: string;\n stylesCss?: string;\n };\n}\n\n/**\n * Result of pushing to registry\n */\nexport interface PushToRegistryResult {\n /** Manifest digest */\n digest: string;\n /** Tag */\n tag: string;\n /** Size */\n size: number;\n /** Repository */\n repository: string;\n}\n\n/**\n * Converts legacy Obsidian plugins to OCI format.\n * Fetches plugins from GitHub releases and pushes to OCI registry.\n */\nexport class PluginConverter {\n private adapter: FetchAdapter;\n private communityCache: CommunityPluginsCache;\n private releaseFetcher: GitHubReleaseFetcher;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n this.communityCache = new CommunityPluginsCache(adapter);\n this.releaseFetcher = new GitHubReleaseFetcher(adapter);\n }\n\n /**\n * Convert a plugin from GitHub releases to OCI format.\n *\n * @param options - Conversion options\n * @returns Conversion result with plugin data\n * @throws Error if plugin not found or conversion fails\n */\n async convertPlugin(\n options: ConvertPluginOptions,\n ): Promise<ConvertPluginResult> {\n const { pluginId, version, repository, token } = options;\n\n // Step 1: Find plugin in community list\n const plugin = await this.communityCache.findPlugin(pluginId);\n if (!plugin) {\n throw new Error(`Plugin \"${pluginId}\" not found in community plugins`);\n }\n\n // Step 2: Fetch release from GitHub\n const release = version\n ? await this.releaseFetcher.fetchReleaseByTag(plugin.repo, version, token)\n : await this.releaseFetcher.fetchLatestRelease(plugin.repo, token);\n\n // Step 3: Find required assets\n const manifestAsset = release.assets.find(\n (a) => a.name === \"manifest.json\",\n );\n const mainJsAsset = release.assets.find((a) => a.name === \"main.js\");\n const stylesCssAsset = release.assets.find((a) => a.name === \"styles.css\");\n\n if (!manifestAsset) {\n throw new Error(\"manifest.json not found in release\");\n }\n if (!mainJsAsset) {\n throw new Error(\"main.js not found in release\");\n }\n\n // Step 4: Download assets\n const manifestResponse = await this.adapter.fetch(\n manifestAsset.browser_download_url,\n );\n if (!manifestResponse.ok) {\n throw new Error(\n `Failed to download manifest.json: ${manifestResponse.status}`,\n );\n }\n const manifestJson = await manifestResponse.text();\n const manifest = JSON.parse(manifestJson) as ObsidianManifest;\n\n const mainJsResponse = await this.adapter.fetch(\n mainJsAsset.browser_download_url,\n );\n if (!mainJsResponse.ok) {\n throw new Error(`Failed to download main.js: ${mainJsResponse.status}`);\n }\n const mainJs = await mainJsResponse.text();\n\n let stylesCss: string | undefined;\n if (stylesCssAsset) {\n const stylesCssResponse = await this.adapter.fetch(\n stylesCssAsset.browser_download_url,\n );\n if (!stylesCssResponse.ok) {\n throw new Error(\n `Failed to download styles.css: ${stylesCssResponse.status}`,\n );\n }\n stylesCss = await stylesCssResponse.text();\n }\n\n // Step 5: Build result\n const fullRepository = repository.includes(\":\")\n ? repository\n : `${repository}:${release.tag_name}`;\n\n return {\n pluginId,\n version: release.tag_name,\n repository: fullRepository,\n githubRepo: plugin.repo,\n manifest,\n mainJs,\n stylesCss,\n };\n }\n\n /**\n * Push plugin data to OCI registry.\n *\n * @param options - Push options\n * @returns Push result\n * @throws Error if push fails\n */\n async pushToRegistry(\n options: PushToRegistryOptions,\n ): Promise<PushToRegistryResult> {\n const { repository, githubRepo, token, pluginData } = options;\n\n // Parse repository reference\n const ref = parseRepoAndRef(repository);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter: this.adapter,\n scopes: [\"push\", \"pull\"],\n });\n\n // Push blobs\n const layers: ManifestOCIDescriptor[] = [];\n\n // Push main.js\n const mainJsResult = await client.pushBlob({\n data: new TextEncoder().encode(pluginData.mainJs),\n });\n layers.push({\n mediaType: \"application/javascript\",\n digest: mainJsResult.digest,\n size: mainJsResult.size,\n annotations: {\n \"org.opencontainers.image.title\": \"main.js\",\n },\n });\n\n // Push styles.css if present\n if (pluginData.stylesCss) {\n const stylesCssResult = await client.pushBlob({\n data: new TextEncoder().encode(pluginData.stylesCss),\n });\n layers.push({\n mediaType: \"text/css\",\n digest: stylesCssResult.digest,\n size: stylesCssResult.size,\n annotations: {\n \"org.opencontainers.image.title\": \"styles.css\",\n },\n });\n }\n\n // Push manifest with OCI annotations\n const annotations: Record<string, string> = {\n \"org.opencontainers.image.created\": new Date().toISOString(),\n \"org.opencontainers.image.source\": githubRepo,\n \"org.opencontainers.image.version\": pluginData.manifest.version,\n \"org.opencontainers.image.description\": pluginData.manifest.description,\n \"org.opencontainers.image.authors\": pluginData.manifest.author,\n };\n\n // Add optional fields if present\n if (pluginData.manifest.authorUrl) {\n annotations[\"org.opencontainers.image.url\"] = pluginData.manifest.authorUrl;\n }\n\n const manifestPushResult = await client.pushPluginManifest({\n ref: ref.tag || pluginData.manifest.version,\n pluginManifest: pluginData.manifest,\n layers,\n annotations,\n });\n\n // Calculate total size from manifest\n const totalSize = manifestPushResult.manifest.layers.reduce(\n (sum, layer) => sum + layer.size,\n 0,\n );\n\n return {\n digest: manifestPushResult.digest,\n tag: ref.tag || pluginData.manifest.version,\n size: totalSize,\n repository,\n };\n }\n}\n", "import type { FetchAdapter } from \"shard-lib\";\nimport { PluginConverter } from \"../lib/converter.js\";\nimport { Logger } from \"../lib/logger.js\";\n\nexport interface ConvertOptions {\n pluginId: string;\n repository: string;\n version?: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface ConvertResult {\n pluginId: string;\n version: string;\n repository: string;\n digest: string;\n size: number;\n}\n\n/**\n * Convert a legacy Obsidian plugin from GitHub releases to OCI format.\n *\n * @param opts - Convert options\n * @returns Convert result with digest and repository\n */\nexport async function convertCommand(\n opts: ConvertOptions,\n): Promise<ConvertResult> {\n const { pluginId, repository, version, token, logger, adapter } = opts;\n\n // Step 1: Create converter\n const converter = new PluginConverter(adapter);\n\n // Step 2: Convert plugin from GitHub releases\n logger.log(`Converting plugin \"${pluginId}\"...`);\n if (version) {\n logger.log(`Using specific version: ${version}`);\n } else {\n logger.log(\"Using latest version\");\n }\n\n const convertResult = await converter.convertPlugin({\n pluginId,\n version,\n repository,\n token,\n });\n\n logger.log(\n `Downloaded plugin ${convertResult.pluginId} v${convertResult.version}`,\n );\n logger.log(` - manifest.json: ${convertResult.manifest.name}`);\n logger.log(` - main.js: ${convertResult.mainJs.length} bytes`);\n if (convertResult.stylesCss) {\n logger.log(` - styles.css: ${convertResult.stylesCss.length} bytes`);\n }\n\n // Step 3: Push to OCI registry\n logger.log(`\\nPushing to ${convertResult.repository}...`);\n const pushResult = await converter.pushToRegistry({\n repository: convertResult.repository,\n githubRepo: convertResult.githubRepo,\n token,\n pluginData: {\n manifest: convertResult.manifest,\n mainJs: convertResult.mainJs,\n stylesCss: convertResult.stylesCss,\n },\n });\n\n logger.success(\n `Successfully converted and pushed ${convertResult.pluginId} v${convertResult.version}`,\n );\n logger.log(`Manifest digest: ${pushResult.digest}`);\n logger.log(`Repository: ${pushResult.repository}`);\n\n return {\n pluginId: convertResult.pluginId,\n version: convertResult.version,\n repository: pushResult.repository,\n digest: pushResult.digest,\n size: pushResult.size,\n };\n}\n", "import type { FetchAdapter } from \"shard-lib\";\n\nexport interface MarketplacePlugin {\n // Primary identifiers\n id: string;\n registryUrl: string; // ghcr.io/owner/repo (PRIMARY)\n\n // Metadata from manifest\n name: string;\n author: string;\n description: string;\n version: string; // Latest published version\n\n // Optional metadata\n license?: string;\n minObsidianVersion?: string;\n authorUrl?: string;\n\n // Derived/optional\n repository?: string; // GitHub URL derived from org.opencontainers.image.source\n tags?: string[]; // For categorization\n\n // Marketplace metadata\n updatedAt: string; // ISO 8601 timestamp\n}\n\nexport interface MarketplaceIndex {\n plugins: MarketplacePlugin[];\n}\n\nexport const DEFAULT_MARKETPLACE_URL =\n \"https://shard-for-obsidian.github.io/shard/plugins.json\";\n\n/**\n * Client for fetching marketplace data from the registry.\n */\nexport class MarketplaceClient {\n constructor(\n private adapter: FetchAdapter,\n private marketplaceUrl: string = DEFAULT_MARKETPLACE_URL,\n ) {}\n\n /**\n * Fetch all plugins from the marketplace.\n */\n async fetchPlugins(): Promise<MarketplacePlugin[]> {\n const response = await this.adapter.fetch(this.marketplaceUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch marketplace data: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = (await response.json()) as MarketplaceIndex;\n\n if (!data.plugins || !Array.isArray(data.plugins)) {\n throw new Error(\"Invalid marketplace data format\");\n }\n\n return data.plugins;\n }\n\n /**\n * Find a plugin by ID.\n */\n async findPluginById(pluginId: string): Promise<MarketplacePlugin | null> {\n const plugins = await this.fetchPlugins();\n return plugins.find((p) => p.id === pluginId) || null;\n }\n\n /**\n * Search plugins by keyword (searches in name, description, author, tags).\n */\n async searchPlugins(keyword: string): Promise<MarketplacePlugin[]> {\n const plugins = await this.fetchPlugins();\n const lowerKeyword = keyword.toLowerCase();\n\n return plugins.filter(\n (p) =>\n p.name.toLowerCase().includes(lowerKeyword) ||\n p.description.toLowerCase().includes(lowerKeyword) ||\n p.author.toLowerCase().includes(lowerKeyword) ||\n p.id.toLowerCase().includes(lowerKeyword) ||\n p.tags?.some((tag) => tag.toLowerCase().includes(lowerKeyword)),\n );\n }\n}\n", "import { OciRegistryClient, parseRepoAndRef } from \"shard-lib\";\nimport type { FetchAdapter } from \"shard-lib\";\nimport { Logger } from \"../lib/logger.js\";\nimport { MarketplaceClient } from \"../lib/marketplace-client.js\";\nimport type { MarketplacePlugin } from \"../lib/marketplace-client.js\";\nimport { pullCommand } from \"./pull.js\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport interface MarketplaceRegisterOptions {\n repository: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface MarketplaceRegisterResult {\n pluginId: string;\n name: string;\n author: string;\n description: string;\n version: string;\n registryUrl: string;\n repository?: string;\n license?: string;\n minObsidianVersion?: string;\n authorUrl?: string;\n yamlPath: string;\n}\n\n/**\n * Register a plugin to the Shard marketplace.\n * Pulls plugin metadata from OCI registry and creates a YAML file.\n *\n * @param opts - Marketplace register options\n * @returns Register result with plugin metadata\n */\nexport async function marketplaceRegisterCommand(\n opts: MarketplaceRegisterOptions,\n): Promise<MarketplaceRegisterResult> {\n const { repository, token, logger, adapter } = opts;\n\n // Step 1: Parse repository reference\n logger.log(`Fetching plugin metadata from ${repository}...`);\n const ref = parseRepoAndRef(repository);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n scopes: [\"pull\"],\n });\n\n // Step 2: Pull plugin manifest\n const manifestResult = await client.pullPluginManifest({\n ref: ref.tag || \"latest\",\n });\n\n const pluginManifest = manifestResult.pluginManifest;\n const ociManifest = manifestResult.manifest;\n\n // Step 3: Extract metadata from plugin manifest\n const pluginId = pluginManifest.id;\n const name = pluginManifest.name;\n const author = pluginManifest.author;\n const description = pluginManifest.description || \"\";\n const version = pluginManifest.version;\n const minObsidianVersion = pluginManifest.minAppVersion;\n const authorUrl = pluginManifest.authorUrl;\n\n // Step 4: Get repository URL from annotations\n let gitHubRepoUrl: string | undefined;\n if (\n ociManifest.annotations &&\n ociManifest.annotations[\"org.opencontainers.image.source\"]\n ) {\n const source = ociManifest.annotations[\"org.opencontainers.image.source\"];\n // Ensure it's a full GitHub URL\n if (source.startsWith(\"http\")) {\n gitHubRepoUrl = source;\n } else {\n // Convert short form to full URL\n gitHubRepoUrl = `https://github.com/${source}`;\n }\n }\n\n // Step 5: Build registry URL (use canonical name which includes registry host)\n const registryUrl = ref.canonicalName;\n\n logger.log(`Plugin ID: ${pluginId}`);\n logger.log(`Name: ${name}`);\n logger.log(`Author: ${author}`);\n logger.log(`Version: ${version}`);\n logger.log(`Registry URL: ${registryUrl}`);\n if (gitHubRepoUrl) {\n logger.log(`Repository: ${gitHubRepoUrl}`);\n }\n\n // Step 6: Generate enhanced YAML content\n let yamlContent = `id: ${pluginId}\nregistryUrl: ${registryUrl}\nname: ${name}\nauthor: ${author}\ndescription: ${description}\nversion: ${version}\n`;\n\n if (gitHubRepoUrl) {\n yamlContent += `repository: ${gitHubRepoUrl}\\n`;\n }\n\n if (minObsidianVersion) {\n yamlContent += `minObsidianVersion: ${minObsidianVersion}\\n`;\n }\n\n if (authorUrl) {\n yamlContent += `authorUrl: ${authorUrl}\\n`;\n }\n\n // Add timestamp\n yamlContent += `updatedAt: ${new Date().toISOString()}\\n`;\n\n // Step 7: Find marketplace directory (walk up from cwd)\n const marketplacePath = await findMarketplaceDir();\n const pluginsDir = path.join(marketplacePath, \"plugins\");\n const yamlPath = path.join(pluginsDir, `${pluginId}.yml`);\n\n // Step 8: Ensure plugins directory exists\n await fs.mkdir(pluginsDir, { recursive: true });\n\n // Step 9: Write YAML file\n await fs.writeFile(yamlPath, yamlContent, \"utf-8\");\n\n logger.success(`Successfully registered plugin to ${yamlPath}`);\n logger.log(`\\nNext steps:`);\n logger.log(`1. Review the generated YAML file`);\n logger.log(`2. Commit and push to the marketplace repository`);\n logger.log(`3. Submit a pull request to add your plugin to the marketplace`);\n\n return {\n pluginId,\n name,\n author,\n description,\n version,\n registryUrl,\n repository: gitHubRepoUrl,\n minObsidianVersion,\n authorUrl,\n yamlPath,\n };\n}\n\n/**\n * Find the marketplace directory by walking up from cwd.\n * Looks for a directory containing marketplace/plugins/.\n */\nasync function findMarketplaceDir(): Promise<string> {\n let currentDir = process.cwd();\n const root = path.parse(currentDir).root;\n\n while (currentDir !== root) {\n const marketplacePath = path.join(currentDir, \"marketplace\");\n try {\n const stat = await fs.stat(marketplacePath);\n if (stat.isDirectory()) {\n return marketplacePath;\n }\n } catch {\n // Directory doesn't exist, continue searching\n }\n\n // Move up one directory\n currentDir = path.dirname(currentDir);\n }\n\n throw new Error(\n \"Could not find marketplace directory. \" +\n \"Please run this command from within the marketplace repository.\",\n );\n}\n\n/**\n * List all plugins in the marketplace.\n */\nexport async function marketplaceListCommand(opts: {\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin[]> {\n const { logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(\"Fetching marketplace plugins...\");\n const plugins = await client.fetchPlugins();\n\n logger.log(`\\nFound ${plugins.length} plugins:\\n`);\n\n for (const plugin of plugins) {\n logger.log(`${plugin.name} (${plugin.id})`);\n logger.log(` Author: ${plugin.author}`);\n logger.log(` Version: ${plugin.version}`);\n logger.log(` Registry: ${plugin.registryUrl}`);\n if (plugin.description) {\n logger.log(` Description: ${plugin.description}`);\n }\n logger.log(\"\");\n }\n\n return plugins;\n}\n\n/**\n * Search for plugins in the marketplace.\n */\nexport async function marketplaceSearchCommand(opts: {\n keyword: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin[]> {\n const { keyword, logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(`Searching for \"${keyword}\"...`);\n const plugins = await client.searchPlugins(keyword);\n\n if (plugins.length === 0) {\n logger.log(`\\nNo plugins found matching \"${keyword}\"`);\n return [];\n }\n\n logger.log(`\\nFound ${plugins.length} matching plugin(s):\\n`);\n\n for (const plugin of plugins) {\n logger.log(`${plugin.name} (${plugin.id})`);\n logger.log(` Author: ${plugin.author}`);\n logger.log(` Version: ${plugin.version}`);\n logger.log(` Registry: ${plugin.registryUrl}`);\n if (plugin.description) {\n logger.log(` Description: ${plugin.description}`);\n }\n logger.log(\"\");\n }\n\n return plugins;\n}\n\n/**\n * Display detailed information about a plugin.\n */\nexport async function marketplaceInfoCommand(opts: {\n pluginId: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin | null> {\n const { pluginId, logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(`Fetching plugin \"${pluginId}\"...`);\n const plugin = await client.findPluginById(pluginId);\n\n if (!plugin) {\n logger.error(`Plugin \"${pluginId}\" not found in marketplace`);\n return null;\n }\n\n logger.log(\"\\n\" + \"=\".repeat(60));\n logger.log(`Plugin: ${plugin.name}`);\n logger.log(\"=\".repeat(60) + \"\\n\");\n\n logger.log(`ID: ${plugin.id}`);\n logger.log(`Version: ${plugin.version}`);\n logger.log(`Author: ${plugin.author}`);\n if (plugin.authorUrl) {\n logger.log(`Author URL: ${plugin.authorUrl}`);\n }\n logger.log(`Description: ${plugin.description}`);\n logger.log(`\\nRegistry URL: ${plugin.registryUrl}`);\n if (plugin.repository) {\n logger.log(`Repository: ${plugin.repository}`);\n }\n if (plugin.license) {\n logger.log(`License: ${plugin.license}`);\n }\n if (plugin.minObsidianVersion) {\n logger.log(`Min Obsidian Version: ${plugin.minObsidianVersion}`);\n }\n if (plugin.tags && plugin.tags.length > 0) {\n logger.log(`Tags: ${plugin.tags.join(\", \")}`);\n }\n logger.log(`Last Updated: ${plugin.updatedAt}`);\n\n logger.log(\"\\n\" + \"=\".repeat(60));\n logger.log(\"Installation:\");\n logger.log(\"=\".repeat(60) + \"\\n\");\n logger.log(`shard pull ${plugin.registryUrl}:${plugin.version} --output <path>`);\n logger.log(\n `shard marketplace install ${plugin.id} # (coming soon)`,\n );\n\n return plugin;\n}\n\n/**\n * Install a plugin from the marketplace by ID.\n */\nexport async function marketplaceInstallCommand(opts: {\n pluginId: string;\n output: string;\n version?: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<{ plugin: MarketplacePlugin; pullResult: unknown }> {\n const { pluginId, output, version, token, logger, adapter, marketplaceUrl } =\n opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n // Step 1: Find plugin in marketplace\n logger.log(`Looking up plugin \"${pluginId}\" in marketplace...`);\n const plugin = await client.findPluginById(pluginId);\n\n if (!plugin) {\n throw new Error(`Plugin \"${pluginId}\" not found in marketplace`);\n }\n\n logger.log(`Found: ${plugin.name} v${plugin.version} by ${plugin.author}`);\n\n // Step 2: Determine version to install\n const versionToInstall = version || plugin.version;\n const repository = `${plugin.registryUrl}:${versionToInstall}`;\n\n logger.log(`Installing from ${repository}...`);\n\n // Step 3: Use pull command to install\n const pullResult = await pullCommand({\n repository,\n output,\n token,\n logger,\n adapter,\n });\n\n logger.success(\n `Successfully installed ${plugin.name} v${versionToInstall} to ${output}`,\n );\n\n return { plugin, pullResult };\n}\n\n/**\n * Update a marketplace entry by re-registering from GHCR.\n */\nexport async function marketplaceUpdateCommand(opts: {\n repository: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}): Promise<MarketplaceRegisterResult> {\n const { logger } = opts;\n\n logger.log(\"Updating marketplace entry...\");\n logger.log(\n \"Note: This will overwrite the existing YAML file with fresh metadata from GHCR\\n\",\n );\n\n // Just call the register command - it will overwrite the existing file\n return marketplaceRegisterCommand(opts);\n}\n", "/**\n * Resolve GitHub authentication token from multiple sources.\n * Priority: CLI flag -> GITHUB_TOKEN -> GH_TOKEN -> error\n *\n * @param cliToken - Token provided via CLI flag\n * @returns Resolved token\n * @throws Error if no token is found\n */\nexport function resolveAuthToken(cliToken?: string): string {\n // Priority 1: CLI flag\n if (cliToken) {\n return cliToken;\n }\n\n // Priority 2: GITHUB_TOKEN environment variable (CI/CD)\n const githubToken = process.env.GITHUB_TOKEN;\n if (githubToken) {\n return githubToken;\n }\n\n // Priority 3: GH_TOKEN environment variable (gh CLI)\n const ghToken = process.env.GH_TOKEN;\n if (ghToken) {\n return ghToken;\n }\n\n // No token found\n throw new Error(\n \"GitHub token required. Use --token flag or set GITHUB_TOKEN environment variable\",\n );\n}\n", "/**\n * Simple logger that writes to stderr for progress messages.\n * This allows JSON output on stdout while still showing progress.\n */\nexport class Logger {\n private silent: boolean;\n\n constructor(silent = false) {\n this.silent = silent;\n }\n\n /**\n * Log a message to stderr\n */\n log(message: string): void {\n if (!this.silent) {\n process.stderr.write(message + \"\\n\");\n }\n }\n\n /**\n * Log an error message to stderr\n */\n error(message: string): void {\n if (!this.silent) {\n process.stderr.write(`Error: ${message}\\n`);\n }\n }\n\n /**\n * Log a success message to stderr\n */\n success(message: string): void {\n if (!this.silent) {\n process.stderr.write(`Success: ${message}\\n`);\n }\n }\n}\n", "import type { FetchAdapter } from \"shard-lib\";\n\n/**\n * Adapter for Node.js native fetch API.\n * Thin wrapper that passes through to native fetch.\n */\nexport class NodeFetchAdapter implements FetchAdapter {\n async fetch(input: string | Request, init?: RequestInit): Promise<Response> {\n // Use native Node.js fetch\n return fetch(input, init);\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../src/index.ts", "../../shard-lib/dist/parsing/IndexParser.js", "../../shard-lib/dist/utils/ValidationUtils.js", "../../shard-lib/dist/parsing/RepoParser.js", "../../shard-lib/dist/utils/DigestUtils.js", "../../shard-lib/dist/types/ManifestTypes.js", "../../shard-lib/dist/ghcr/GhcrConstants.js", "../../shard-lib/dist/errors/RegistryErrors.js", "../../shard-lib/dist/parsing/LinkHeaderParser.js", "../../shard-lib/dist/client/OciRegistryClient.js", "../src/lib/plugin.ts", "../src/commands/push.ts", "../src/commands/pull.ts", "../src/lib/community-plugins.ts", "../src/lib/community-cache.ts", "../src/lib/github-release.ts", "../src/lib/converter.ts", "../src/commands/convert.ts", "../src/lib/marketplace-client.ts", "../src/lib/oci-tags.ts", "../src/commands/marketplace.ts", "../src/lib/auth.ts", "../src/lib/logger.ts", "../src/adapters/node-fetch-adapter.ts"],
|
|
4
|
+
"sourcesContent": ["import { parseArgs } from \"node:util\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { convertCommand } from \"./commands/convert.js\";\nimport {\n marketplaceRegisterCommand,\n marketplaceListCommand,\n marketplaceSearchCommand,\n marketplaceInfoCommand,\n marketplaceInstallCommand,\n marketplaceUpdateCommand,\n marketplaceVersionsCommand,\n} from \"./commands/marketplace.js\";\nimport { resolveAuthToken } from \"./lib/auth.js\";\nimport { Logger } from \"./lib/logger.js\";\nimport { NodeFetchAdapter } from \"./adapters/node-fetch-adapter.js\";\n\nconst USAGE = `\nUsage: shard <command> [options]\n\nCommands:\n push <directory> <repository> Push a plugin to GHCR\n pull <repository> Pull a plugin from GHCR\n convert <plugin-id> <repository> Convert legacy plugin to OCI format\n marketplace <subcommand> Manage marketplace plugins\n\nMarketplace Subcommands:\n list List all marketplace plugins\n search <keyword> Search for plugins\n info <plugin-id> Show detailed plugin information\n install <plugin-id> Install a plugin by ID\n register <repository> Register plugin to marketplace\n update <repository> Update marketplace entry\n versions <registryUrl> List all available versions for a plugin\n\nPush Options:\n <directory> Path to plugin build output (e.g., ./dist)\n <repository> GHCR repository (e.g., ghcr.io/user/plugin)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nPull Options:\n <repository> Full reference with tag (e.g., ghcr.io/user/plugin:1.0.0)\n --output <dir> Where to extract files (required)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nConvert Options:\n <plugin-id> Plugin ID from community list (e.g., obsidian-git)\n <repository> GHCR repository (e.g., ghcr.io/user/plugin)\n --version <version> Specific version to convert (defaults to latest)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nMarketplace Options:\n --output <dir> Output directory for install command\n --version <version> Specific version to install (defaults to latest)\n --token <pat> GitHub Personal Access Token\n --json Output JSON result to stdout\n --help Show help\n\nEnvironment Variables:\n GITHUB_TOKEN GitHub token (alternative to --token)\n GH_TOKEN GitHub token (gh CLI compatibility)\n\nExamples:\n shard push ./dist ghcr.io/user/my-plugin\n shard pull ghcr.io/user/my-plugin:1.0.0 --output ./plugin\n shard convert obsidian-git ghcr.io/user/obsidian-git\n shard convert calendar ghcr.io/user/calendar --version 1.5.3\n shard marketplace list\n shard marketplace search \"calendar\"\n shard marketplace info obsidian-git\n shard marketplace install obsidian-git --output ./plugins/obsidian-git\n shard marketplace register ghcr.io/user/my-plugin:1.0.0\n shard marketplace update ghcr.io/user/my-plugin:1.0.1\n shard marketplace versions ghcr.io/user/my-plugin\n`;\n\ninterface CliArgs {\n values: {\n token?: string;\n json?: boolean;\n help?: boolean;\n output?: string;\n version?: string;\n };\n positionals: string[];\n}\n\nasync function main() {\n // Parse command line arguments\n let args: CliArgs;\n try {\n args = parseArgs({\n options: {\n token: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n help: { type: \"boolean\", default: false },\n output: { type: \"string\" },\n version: { type: \"string\" },\n },\n allowPositionals: true,\n }) as CliArgs;\n } catch (err) {\n console.error(\n `Error parsing arguments: ${err instanceof Error ? err.message : String(err)}`,\n );\n console.error(USAGE);\n process.exit(1);\n }\n\n // Show help\n if (args.values.help || args.positionals.length === 0) {\n console.log(USAGE);\n process.exit(0);\n }\n\n // Get command\n const command = args.positionals[0];\n const logger = new Logger(args.values.json);\n const adapter = new NodeFetchAdapter();\n\n try {\n if (command === \"push\") {\n // Parse push arguments\n if (args.positionals.length < 3) {\n throw new Error(\"Push command requires <directory> and <repository>\");\n }\n\n const directory = args.positionals[1];\n const repository = args.positionals[2];\n const token = resolveAuthToken(args.values.token);\n\n // Execute push\n const result = await pushCommand({\n directory,\n repository,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"pull\") {\n // Parse pull arguments\n if (args.positionals.length < 2) {\n throw new Error(\"Pull command requires <repository>\");\n }\n\n if (!args.values.output) {\n throw new Error(\"Pull command requires --output flag\");\n }\n\n const repository = args.positionals[1];\n const output = args.values.output;\n const token = resolveAuthToken(args.values.token);\n\n // Execute pull\n const result = await pullCommand({\n repository,\n output,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"convert\") {\n // Parse convert arguments\n if (args.positionals.length < 3) {\n throw new Error(\n \"Convert command requires <plugin-id> and <repository>\",\n );\n }\n\n const pluginId = args.positionals[1];\n const repository = args.positionals[2];\n const version = args.values.version;\n const token = resolveAuthToken(args.values.token);\n\n // Execute convert\n const result = await convertCommand({\n pluginId,\n repository,\n version,\n token,\n logger,\n adapter,\n });\n\n // Output result\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (command === \"marketplace\") {\n // Parse marketplace subcommand\n const subcommand = args.positionals[1];\n\n if (!subcommand) {\n throw new Error(\n \"Marketplace command requires a subcommand. Available: list, search, info, install, register, update, versions\",\n );\n }\n\n const token = resolveAuthToken(args.values.token);\n\n if (subcommand === \"list\") {\n // List all plugins\n const result = await marketplaceListCommand({\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"search\") {\n // Search for plugins\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace search command requires <keyword>\");\n }\n\n const keyword = args.positionals[2];\n const result = await marketplaceSearchCommand({\n keyword,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"info\") {\n // Show plugin info\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace info command requires <plugin-id>\");\n }\n\n const pluginId = args.positionals[2];\n const result = await marketplaceInfoCommand({\n pluginId,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"install\") {\n // Install plugin by ID\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace install command requires <plugin-id>\");\n }\n\n if (!args.values.output) {\n throw new Error(\"Marketplace install command requires --output flag\");\n }\n\n const pluginId = args.positionals[2];\n const output = args.values.output;\n const version = args.values.version;\n\n const result = await marketplaceInstallCommand({\n pluginId,\n output,\n version,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"register\") {\n // Register plugin to marketplace\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace register command requires <repository>\");\n }\n\n const repository = args.positionals[2];\n const result = await marketplaceRegisterCommand({\n repository,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"update\") {\n // Update marketplace entry\n if (args.positionals.length < 3) {\n throw new Error(\"Marketplace update command requires <repository>\");\n }\n\n const repository = args.positionals[2];\n const result = await marketplaceUpdateCommand({\n repository,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else if (subcommand === \"versions\") {\n // List all available versions for a registry URL\n if (args.positionals.length < 3) {\n throw new Error(\n \"Marketplace versions command requires <registryUrl>\",\n );\n }\n\n const registryUrl = args.positionals[2];\n const result = await marketplaceVersionsCommand({\n registryUrl,\n token,\n logger,\n adapter,\n });\n\n if (args.values.json) {\n console.log(JSON.stringify(result, null, 2));\n }\n process.exit(0);\n } else {\n throw new Error(\n `Unknown marketplace subcommand: ${subcommand}. Available: list, search, info, install, register, update, versions`,\n );\n }\n } else {\n throw new Error(`Unknown command: ${command}`);\n }\n } catch (err) {\n logger.error(err instanceof Error ? err.message : String(err));\n if (args.values.json) {\n console.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2,\n ),\n );\n }\n process.exit(1);\n }\n}\n\nvoid main();\n", "// --- globals\n// See `INDEXNAME` in docker/docker.git:registry/config.go.\nexport const DEFAULT_INDEX_NAME = \"docker.io\";\nexport const DEFAULT_INDEX_URL = \"https://registry-1.docker.io\";\nexport const DEFAULT_LOGIN_SERVERNAME = \"https://index.docker.io/v1/\";\n/**\n * Parse a docker index name or index URL.\n *\n * Examples:\n * docker.io (no scheme implies 'https')\n * index.docker.io (normalized to docker.io)\n * https://docker.io\n * http://localhost:5000\n * https://index.docker.io/v1/ (special case)\n *\n * Special case: `docker` still refers to \"https://index.docker.io/v1/\"\n * when dealing with auth (including in its json file).\n *\n * @param {String} arg: Optional. Index name (optionally with leading scheme).\n */\nexport function parseIndex(arg) {\n if (!arg || arg === DEFAULT_LOGIN_SERVERNAME) {\n // Default index.\n return {\n scheme: \"https\",\n name: DEFAULT_INDEX_NAME,\n official: true,\n };\n }\n // Optional protocol/scheme.\n let indexName;\n let scheme = \"https\";\n const protoSepIdx = arg.indexOf(\"://\");\n if (protoSepIdx !== -1) {\n const foundScheme = arg.slice(0, protoSepIdx);\n if (foundScheme !== \"http\" && foundScheme !== \"https\") {\n throw new Error(\"invalid index scheme, must be \" + '\"http\" or \"https\": ' + arg);\n }\n scheme = foundScheme;\n indexName = arg.slice(protoSepIdx + 3);\n }\n else {\n scheme = isLocalhost(arg) ? \"http\" : \"https\";\n indexName = arg;\n }\n if (!indexName) {\n throw new Error(\"invalid index, empty host: \" + arg);\n }\n else if (indexName.indexOf(\".\") === -1 &&\n indexName.indexOf(\":\") === -1 &&\n indexName !== \"localhost\") {\n throw new Error(`invalid index, \"${indexName}\" does not look like a valid host: ${arg}`);\n }\n else {\n // Allow a trailing '/' as from some URL builder functions that\n // add a default '/' path to a URL, e.g. 'https://docker.io/'.\n if (indexName[indexName.length - 1] === \"/\") {\n indexName = indexName.slice(0, indexName.length - 1);\n }\n // Ensure no trailing repo.\n if (indexName.indexOf(\"/\") !== -1) {\n throw new Error(\"invalid index, trailing repo: \" + arg);\n }\n }\n // Per docker.git's `ValidateIndexName`.\n if (indexName === \"index.\" + DEFAULT_INDEX_NAME) {\n indexName = DEFAULT_INDEX_NAME;\n }\n const index = {\n name: indexName,\n official: indexName === DEFAULT_INDEX_NAME,\n scheme,\n };\n // Disallow official and 'http'.\n if (index.official && index.scheme === \"http\") {\n throw new Error(\"invalid index, plaintext HTTP to official index \" +\n \"is disallowed: \" +\n arg);\n }\n return index;\n}\n/**\n * Similar in spirit to docker.git:registry/endpoint.go#NewEndpoint().\n */\nexport function urlFromIndex(index, scheme) {\n if (index.official) {\n // v1\n if (scheme != null && scheme !== \"https\")\n throw new Error(`Unencrypted communication with docker.io is not allowed`);\n return DEFAULT_INDEX_URL;\n }\n else {\n if (scheme != null && scheme !== \"https\" && scheme !== \"http\")\n throw new Error(`Non-HTTP communication with docker registries is not allowed`);\n return `${scheme ?? index.scheme}://${index.name}`;\n }\n}\nexport function isLocalhost(host) {\n const lead = host.split(\":\")[0];\n if (lead === \"localhost\" || lead === \"127.0.0.1\" || host.includes(\"::1\")) {\n return true;\n }\n else {\n return false;\n }\n}\n", "export function splitIntoTwo(str, sep) {\n const slashIdx = str.indexOf(sep);\n return slashIdx == -1\n ? [str]\n : [str.slice(0, slashIdx), str.slice(slashIdx + 1)];\n}\n", "import { parseIndex } from \"./IndexParser.js\";\nimport { splitIntoTwo } from \"../utils/ValidationUtils.js\";\n// JSSTYLED\n// 'DEFAULTTAG' from https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/graph/tags.go#L25\nexport const DEFAULT_TAG = \"latest\";\nconst VALID_NS = /^[a-z0-9._-]*$/;\nconst VALID_REPO = /^[a-z0-9_/.-]*$/;\n/**\n * Parse a docker repo and tag string: [INDEX/]REPO[:TAG|@DIGEST]\n *\n * Examples:\n * busybox\n * google/python\n * docker.io/ubuntu\n * localhost:5000/blarg\n * http://localhost:5000/blarg\n *\n * Dev Notes:\n * - This is meant to mimic\n * docker.git:registry/config.go#ServiceConfig.NewRepositoryInfo\n * as much as reasonable -- with the addition that we maintain the\n * 'tag' field. Also, that we accept the scheme on the \"INDEX\" is\n * different than docker.git's parsing.\n * - TODO: what about the '@digest' digest alternative to a tag? See:\n * // JSSTYLED\n * https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L68\n *\n * @param arg {String} The docker repo string to parse. See examples above.\n * @param defaultIndex {Object|String} Optional. The default index to use\n * if not specified with `arg`. If not given the default is 'docker.io'.\n * If given it may either be a string, e.g. 'https://myreg.example.com',\n * or parsed index object, as from `parseIndex()`.\n */\nexport function parseRepo(arg, defaultIndex) {\n let index;\n // Strip off optional leading `INDEX/`, parse it to `info.index` and\n // leave the rest in `remoteName`.\n let remoteNameRaw;\n const protoSepIdx = arg.indexOf(\"://\");\n if (protoSepIdx !== -1) {\n // (A) repo with a protocol, e.g. 'https://host/repo'.\n const slashIdx = arg.indexOf(\"/\", protoSepIdx + 3);\n if (slashIdx === -1) {\n throw new Error('invalid repository name, no \"/REPO\" after ' + \"hostame: \" + arg);\n }\n const indexName = arg.slice(0, slashIdx);\n remoteNameRaw = arg.slice(slashIdx + 1);\n index = parseIndex(indexName);\n }\n else {\n const parts = splitIntoTwo(arg, \"/\");\n if (parts.length === 1 ||\n /* or if parts[0] doesn't look like a hostname or IP */\n (parts[0].indexOf(\".\") === -1 &&\n parts[0].indexOf(\":\") === -1 &&\n parts[0] !== \"localhost\")) {\n // (B) repo without leading 'INDEX/'.\n if (defaultIndex === undefined) {\n index = parseIndex();\n }\n else if (typeof defaultIndex === \"string\") {\n index = parseIndex(defaultIndex);\n }\n else {\n index = defaultIndex;\n }\n remoteNameRaw = arg;\n }\n else {\n // (C) repo with leading 'INDEX/' (without protocol).\n index = parseIndex(parts[0]);\n remoteNameRaw = parts[1];\n }\n }\n // Validate remoteName (docker `validateRemoteName`).\n const nameParts = splitIntoTwo(remoteNameRaw, \"/\");\n let ns = \"\", name;\n if (nameParts.length === 2) {\n name = nameParts[1];\n // Validate ns.\n ns = nameParts[0];\n if (ns.length < 2 || ns.length > 255) {\n throw new Error(\"invalid repository namespace, must be between \" +\n \"2 and 255 characters: \" +\n ns);\n }\n if (!VALID_NS.test(ns)) {\n throw new Error(\"invalid repository namespace, may only contain \" +\n \"[a-z0-9._-] characters: \" +\n ns);\n }\n if (ns[0] === \"-\" && ns[ns.length - 1] === \"-\") {\n throw new Error(\"invalid repository namespace, cannot start or \" +\n \"end with a hypen: \" +\n ns);\n }\n if (ns.indexOf(\"--\") !== -1) {\n throw new Error(\"invalid repository namespace, cannot contain \" +\n \"consecutive hyphens: \" +\n ns);\n }\n }\n else {\n name = remoteNameRaw;\n if (index.official) {\n ns = \"library\";\n }\n }\n // Validate name.\n if (!VALID_REPO.test(name)) {\n throw new Error(\"invalid repository name, may only contain \" +\n \"[a-z0-9_/.-] characters: \" +\n name);\n }\n const isLibrary = index.official && ns === \"library\";\n const remoteName = ns ? `${ns}/${name}` : name;\n const localName = index.official\n ? isLibrary\n ? name\n : remoteName\n : `${index.name}/${remoteName}`;\n const canonicalName = index.official\n ? `${parseIndex().name}/${localName}`\n : localName;\n return {\n index,\n official: isLibrary,\n remoteName,\n localName,\n canonicalName,\n };\n}\n/**\n * Parse a docker repo and tag/digest string: [INDEX/]REPO[:TAG|@DIGEST|:TAG@DIGEST]\n *\n * Examples:\n * busybox\n * busybox:latest\n * google/python:3.3\n * docker.io/ubuntu\n * localhost:5000/blarg\n * http://localhost:5000/blarg:latest\n * google/python:3.3@sha256:fb9f16730ac6316afa4d97caa51302199...\n * alpine@sha256:fb9f16730ac6316afa4d97caa5130219927bfcecf0b0...\n *\n * Dev Notes:\n * - TODO Validation on digest and tag would be nice.\n *\n * @param arg {String} The docker repo:tag string to parse. See examples above.\n * @param defaultIndex {Object|String} Optional. The default index to use\n * if not specified with `arg`. If not given the default is 'docker.io'.\n * If given it may either be a string, e.g. 'https://myreg.example.com',\n * or parsed index object, as from `parseIndex()`.\n */\nexport function parseRepoAndRef(arg, defaultIndex) {\n // Parse off the tag/digest per\n // https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L69\n let digest = null;\n let tag = null;\n const atIdx = arg.lastIndexOf(\"@\");\n if (atIdx !== -1) {\n digest = arg.slice(atIdx + 1);\n arg = arg.slice(0, atIdx);\n }\n else {\n tag = DEFAULT_TAG;\n }\n const colonIdx = arg.lastIndexOf(\":\");\n const slashIdx = arg.lastIndexOf(\"/\");\n if (colonIdx !== -1 && colonIdx > slashIdx) {\n tag = arg.slice(colonIdx + 1);\n arg = arg.slice(0, colonIdx);\n }\n const repo = parseRepo(arg, defaultIndex);\n return {\n ...repo,\n digest,\n tag,\n canonicalRef: [\n repo.canonicalName,\n tag ? `:${tag}` : \"\",\n digest ? `@${digest}` : \"\",\n ].join(\"\"),\n };\n}\nexport const parseRepoAndTag = parseRepoAndRef;\n", "export function encodeHex(data) {\n return [...new Uint8Array(data)]\n .map((x) => x.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n/**\n * Calculate the 'Docker-Content-Digest' header for the given manifest.\n *\n * @returns {Promise<String>} The docker digest string.\n * @throws {InvalidContentError} if there is a problem parsing the manifest.\n */\nexport async function digestFromManifestStr(manifestStr) {\n let manifest;\n try {\n manifest = JSON.parse(manifestStr);\n }\n catch (thrown) {\n const err = thrown;\n throw new Error(`could not parse manifest: ${err.message}\\n${manifestStr}`);\n }\n if (manifest.schemaVersion === 1) {\n throw new Error(`schemaVersion 1 is not supported by /x/docker_registry_client.`);\n }\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(manifestStr));\n return `sha256:${encodeHex(hash)}`;\n}\n", "// Media type constants for Docker and OCI manifests\nexport const MEDIATYPE_MANIFEST_V2 = \"application/vnd.docker.distribution.manifest.v2+json\";\nexport const MEDIATYPE_MANIFEST_LIST_V2 = \"application/vnd.docker.distribution.manifest.list.v2+json\";\nexport const MEDIATYPE_OCI_MANIFEST_V1 = \"application/vnd.oci.image.manifest.v1+json\";\nexport const MEDIATYPE_OCI_MANIFEST_INDEX_V1 = \"application/vnd.oci.image.index.v1+json\";\nexport const MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1 = \"application/vnd.obsidianmd.plugin-manifest.v1+json\";\nexport const DEFAULT_USERAGENT = `open-obsidian-plugin-spec/0.1.0`;\n", "export const REALM = \"https://ghcr.io/token\";\nexport const SERVICE = \"ghcr.io\";\nexport const SCOPE_REPO_PREFIX = \"repository:\";\nexport const SCOPE_PULL_SUFFIX = \":pull\";\n", "/*\n * Error classes that docker-registry-client may produce.\n */\n/** Base class for custom error classes. */\nexport class ApiError extends Error {\n constructor(message) {\n super(message);\n this.name = new.target.name;\n Error.captureStackTrace?.(this, new.target);\n }\n}\nexport class HttpError extends ApiError {\n resp;\n errors;\n name = \"HttpError\";\n constructor(resp, errors, message) {\n super(message);\n this.resp = resp;\n this.errors = errors;\n }\n}\nexport class BadDigestError extends ApiError {\n name = \"BadDigestError\";\n}\nexport class InvalidContentError extends ApiError {\n name = \"InvalidContentError\";\n}\nexport class InternalError extends ApiError {\n name = \"InternalError\";\n}\nexport class ManifestVerificationError extends ApiError {\n name = \"ManifestVerificationError\";\n}\nexport class InvalidManifestError extends ApiError {\n name = \"InvalidManifestError\";\n}\nexport class DownloadError extends ApiError {\n name = \"DownloadError\";\n}\nexport class UploadError extends ApiError {\n name = \"UploadError\";\n}\nexport class BlobReadError extends ApiError {\n name = \"BlobReadError\";\n}\n// export class UnauthorizedError extends HttpError {\n// readonly name = 'UnauthorizedError';\n// readonly statusCode = 401;\n// }\nexport class TooManyRedirectsError extends ApiError {\n name = \"TooManyRedirectsError\";\n}\n", "const linkRegex = /^<([^>]+)>(?:\\s*;\\s*(.+))?$/;\nexport function parseLinkHeader(rawHeader) {\n if (!rawHeader)\n return [];\n return rawHeader\n .split(\",\")\n .slice(0, 5) // Arbitrary limit to how many links we are willing to parse\n .flatMap((piece) => {\n const matches = piece.trim().match(linkRegex);\n if (!matches)\n return [];\n const { rel, ...params } = matches[2]\n ?.split(\";\")\n .map((param) => param.trim().split(\"=\"))\n .reduce((acc, [key, value]) => {\n if (!value)\n return acc;\n if (value.startsWith('\"') && value.endsWith('\"')) {\n value = value.slice(1, -1);\n }\n acc[key] = value;\n return acc;\n }, {}) ?? {};\n if (!rel)\n return [];\n return [\n {\n rel,\n url: matches[1],\n params,\n },\n ];\n });\n}\n", "import { urlFromIndex } from \"../parsing/IndexParser.js\";\nimport { parseRepo } from \"../parsing/RepoParser.js\";\nimport { splitIntoTwo } from \"../utils/ValidationUtils.js\";\nimport { encodeHex, digestFromManifestStr } from \"../utils/DigestUtils.js\";\nimport { MEDIATYPE_MANIFEST_V2, MEDIATYPE_MANIFEST_LIST_V2, MEDIATYPE_OCI_MANIFEST_V1, MEDIATYPE_OCI_MANIFEST_INDEX_V1, MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1, } from \"../types/ManifestTypes.js\";\nimport { REALM, SERVICE } from \"../ghcr/GhcrConstants.js\";\nimport * as e from \"../errors/RegistryErrors.js\";\nimport { parseLinkHeader } from \"../parsing/LinkHeaderParser.js\";\nconst DEFAULT_USERAGENT = `open-obsidian-plugin-spec/0.1.0`;\n// Use globalThis.crypto (available in browsers/Electron/Node 18+)\nconst getCrypto = () => {\n if (!globalThis.crypto) {\n throw new Error(\"crypto API not available. This library requires Node.js 18+ or a modern browser environment.\");\n }\n return globalThis.crypto;\n};\n/*\n * Set the \"Authorization\" HTTP header into the headers object from the given\n * auth info.\n * - Bearer auth if `token`.\n * - Else, Basic auth if `username`.\n * - Else, if the authorization key exists, then it is removed from headers.\n */\nfunction _setAuthHeaderFromAuthInfo(headers, authInfo) {\n if (authInfo?.type === \"Bearer\") {\n headers[\"authorization\"] = \"Bearer \" + authInfo.token;\n }\n else if (authInfo?.type === \"Basic\") {\n const credentials = `${authInfo.username ?? \"\"}:${authInfo.password ?? \"\"}`;\n headers[\"authorization\"] = \"Basic \" + btoa(credentials);\n }\n else {\n delete headers[\"authorization\"];\n }\n return headers;\n}\n/**\n * Special handling of errors from the registry server.\n *\n * Some registry errors will use a custom error format, so detect those\n * and convert these as necessary.\n *\n * Example JSON response for a missing repo:\n * {\n * \"jse_shortmsg\": \"\",\n * \"jse_info\": {},\n * \"message\": \"{\\\"errors\\\":[{\\\"code\\\":\\\"UNAUTHORIZED\\\",\\\"message\\\":\\\"...}\\n\",\n * \"body\": {\n * \"errors\": [{\n * \"code\": \"UNAUTHORIZED\",\n * \"message\": \"authentication required\",\n * \"detail\": [{\n * \"Type\": \"repository\",\n * \"Class\": \"\",\n * \"Name\": \"library/idontexist\",\n * \"Action\": \"pull\"\n * }]\n * }]\n * }\n * }\n *\n * Example JSON response for bad username/password:\n * {\n * \"statusCode\": 401,\n * \"jse_shortmsg\":\"\",\n * \"jse_info\":{},\n * \"message\":\"{\\\"details\\\":\\\"incorrect username or password\\\"}\\n\",\n * \"body\":{\n * \"details\": \"incorrect username or password\"\n * }\n * }\n *\n * Example AWS token error:\n * {\n * \"statusCode\": 400,\n * \"errors\": [\n * {\n * \"code\": \"DENIED\",\n * \"message\": \"Your Authorization Token is invalid.\"\n * }\n * ]\n * }\n */\nfunction _getRegistryErrorMessage(err) {\n const e = err;\n if (e.body && typeof e.body === \"object\" && e.body !== null) {\n const body = e.body;\n if (Array.isArray(body.errors) && body.errors[0]) {\n return body.errors[0].message;\n }\n else if (body.details) {\n return body.details;\n }\n }\n if (Array.isArray(e.errors) && e.errors[0]) {\n return e.errors[0].message;\n }\n else if (e.message) {\n return e.message;\n }\n else if (e.details) {\n return e.details;\n }\n return String(err);\n}\n/**\n * Return a scope string to be used for an auth request. Example:\n * repository:library/nginx:pull\n */\nfunction _makeAuthScope(resource, name, actions) {\n return `${resource}:${name}:${actions.join(\",\")}`;\n}\n/*\n * Parse the 'Docker-Content-Digest' header.\n *\n * @throws {BadDigestError} if the value is missing or malformed\n */\nfunction _parseDockerContentDigest(dcd) {\n if (!dcd)\n throw new e.BadDigestError('missing \"Docker-Content-Digest\" header');\n const errPre = `could not parse Docker-Content-Digest header \"${dcd}\": `;\n // E.g. docker-content-digest: sha256:887f7ecfd0bda3...\n const parts = splitIntoTwo(dcd, \":\");\n if (parts.length !== 2)\n throw new e.BadDigestError(errPre + JSON.stringify(dcd));\n if (parts[0] !== \"sha256\")\n throw new e.BadDigestError(errPre + \"Unsupported hash algorithm \" + JSON.stringify(parts[0]));\n return {\n raw: dcd,\n algorithm: parts[0],\n expectedDigest: parts[1],\n async validate(buffer) {\n switch (this.algorithm) {\n case \"sha256\": {\n const hashBuffer = await getCrypto().subtle.digest(\"SHA-256\", buffer);\n const digest = encodeHex(hashBuffer);\n if (this.expectedDigest !== digest) {\n throw new e.BadDigestError(`Docker-Content-Digest mismatch (expected: ${this.expectedDigest}, got: ${digest})`);\n }\n return;\n }\n default:\n throw new e.BadDigestError(`Unsupported hash algorithm ${this.algorithm}`);\n }\n },\n };\n}\nexport class OciRegistryClient {\n version = 2;\n insecure;\n repo;\n acceptOCIManifests;\n acceptManifestLists;\n username;\n password;\n scopes;\n _loggedIn;\n _loggedInScope;\n _authInfo;\n _headers;\n _url;\n _userAgent;\n _adapter;\n /**\n * Create a new GHCR client for a particular repository.\n *\n * @param opts.insecure {Boolean} Optional. Default false. Set to true\n * to *not* fail on an invalid or this-signed server certificate.\n * @param opts.adapter {FetchAdapter} Required. HTTP adapter for making requests.\n * ... TODO: lots more to document\n *\n */\n constructor(opts) {\n this.insecure = Boolean(opts.insecure);\n if (opts.repo) {\n this.repo = opts.repo;\n }\n else if (opts.name) {\n this.repo = parseRepo(opts.name);\n }\n else\n throw new Error(`name or repo required`);\n this.acceptOCIManifests = opts.acceptOCIManifests ?? true;\n this.acceptManifestLists = opts.acceptManifestLists ?? false;\n this.username = opts.username;\n this.password = opts.password;\n this.scopes = opts.scopes ?? [\"pull\"];\n this._loggedIn = false;\n this._loggedInScope = null; // Keeps track of the login type.\n this._authInfo = null;\n this._headers = {};\n if (opts.token) {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"Bearer\",\n token: opts.token,\n });\n }\n else if (opts.username || opts.password) {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"Basic\",\n username: opts.username ?? \"\",\n password: opts.password ?? \"\",\n });\n }\n else {\n _setAuthHeaderFromAuthInfo(this._headers, {\n type: \"None\",\n });\n }\n this._url = urlFromIndex(this.repo.index, opts.scheme);\n this._userAgent = opts.userAgent || DEFAULT_USERAGENT;\n this._adapter = opts.adapter;\n }\n /**\n * Login V2\n *\n * Typically one does not need to call this function directly because most\n * methods of a `GHCRClient` will automatically login as necessary.\n *\n * @param opts {Object}\n * - opts.scope {String} Optional. A scope string passed in for\n * bearer/token auth. If this is just a login request where the token\n * won't be used, then the empty string (the default) is sufficient.\n * // JSSTYLED\n * See <https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md#requesting-a-token>\n * @return an object with authentication info\n */\n async performLogin(opts) {\n return {\n type: \"Bearer\",\n token: await this._getToken({\n realm: REALM,\n service: SERVICE,\n scopes: opts.scope ? [opts.scope] : [],\n }),\n };\n }\n /**\n * Get an auth token.\n *\n * See: docker/docker.git:registry/token.go\n */\n async _getToken(opts) {\n // - add https:// prefix (or http) if none on 'realm'\n let tokenUrl = opts.realm;\n const match = /^(\\w+):\\/\\//.exec(tokenUrl);\n if (!match) {\n tokenUrl = (this.insecure ? \"http\" : \"https\") + \"://\" + tokenUrl;\n }\n else if (match[1] && [\"http\", \"https\"].indexOf(match[1]) === -1) {\n // TODO: Verify the logic above\n throw new Error(\"unsupported scheme for \" +\n `WWW-Authenticate realm \"${opts.realm}\": \"${match[1]}\"`);\n }\n // - GET $realm\n // ?service=$service\n // (&scope=$scope)*\n // (&account=$username)\n // Authorization: Basic ...\n const headers = {};\n const query = new URLSearchParams();\n if (opts.service) {\n query.set(\"service\", opts.service);\n }\n if (opts.scopes && opts.scopes.length) {\n for (const scope of opts.scopes) {\n query.append(\"scope\", scope); // intentionally singular 'scope'\n }\n }\n if (this.username) {\n query.set(\"account\", this.username);\n _setAuthHeaderFromAuthInfo(headers, {\n type: \"Basic\",\n username: this.username,\n password: this.password ?? \"\",\n });\n }\n if (query.toString()) {\n tokenUrl += \"?\" + query.toString();\n }\n // log.trace({tokenUrl: tokenUrl}, '_getToken: url');\n headers[\"user-agent\"] = this._userAgent;\n const resp = await this._adapter.fetch(tokenUrl, {\n method: \"GET\",\n headers: headers,\n });\n if (resp.status === 401) {\n // Convert *all* 401 errors to use a generic error constructor\n // with a simple error message.\n const body = await resp.json();\n const errMsg = _getRegistryErrorMessage(body);\n throw new Error(`Registry auth failed: ${errMsg}`);\n }\n if (resp.status !== 200) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${tokenUrl}`);\n }\n const body = (await resp.json());\n if (typeof body?.token !== \"string\") {\n console.error(\"TODO: auth resp:\", body);\n throw new Error(\"authorization \" + \"server did not include a token in the response\");\n }\n return body.token;\n }\n /**\n * Get a registry session (i.e. login to the registry).\n *\n * Typically one does not need to call this method directly because most\n * methods of a client will automatically login as necessary.\n *\n * @param opts {Object} Optional.\n * - opts.scope {String} Optional. Scope to use in the auth Bearer token.\n *\n * Side-effects:\n * - On success, all of `this._loggedIn*`, `this._authInfo`, and\n * `this._headers.authorization` are set.\n */\n async login(opts = {}) {\n const scope = opts.scope ||\n _makeAuthScope(\"repository\", this.repo.remoteName, this.scopes);\n if (this._loggedIn && this._loggedInScope === scope) {\n return;\n }\n const authInfo = await this.performLogin({\n scope: scope,\n });\n this._loggedIn = true;\n this._loggedInScope = scope;\n this._authInfo = authInfo;\n _setAuthHeaderFromAuthInfo(this._headers, authInfo);\n // this.log.trace({err: err, loggedIn: this._loggedIn}, 'login: done');\n }\n async listTags(props = {}) {\n const searchParams = new URLSearchParams();\n if (props.pageSize != null)\n searchParams.set(\"n\", `${props.pageSize}`);\n if (props.startingAfter != null)\n searchParams.set(\"last\", props.startingAfter);\n await this.login();\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName)}/tags/list`, this._url);\n url.search = searchParams.toString();\n const headers = { ...this._headers, \"user-agent\": this._userAgent };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers,\n });\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n return (await resp.json());\n }\n async listAllTags(props = {}) {\n const pages = [];\n for await (const page of this.listTagsPaginated(props)) {\n pages.push(page);\n }\n const firstPage = pages.shift();\n for (const nextPage of pages) {\n firstPage.tags = [...firstPage.tags, ...nextPage.tags];\n }\n return firstPage;\n }\n async *listTagsPaginated(props = {}) {\n await this.login();\n let path = `/v2/${encodeURI(this.repo.remoteName)}/tags/list`;\n if (props.pageSize != null) {\n path += `?n=${props.pageSize}`;\n }\n while (path) {\n const url = new URL(path, this._url);\n const headers = { ...this._headers, \"user-agent\": this._userAgent };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers,\n });\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n const linkHeader = resp.headers.get(\"link\");\n const links = parseLinkHeader(linkHeader ?? null);\n const nextLink = links.find((x) => x.rel == \"next\");\n // If there's no next link then we use a null to end the loop.\n path = nextLink?.url ?? null;\n yield (await resp.json());\n }\n }\n /*\n * Get an image manifest. `ref` is either a tag or a digest.\n * <https://docs.docker.com/registry/spec/api/#pulling-an-image-manifest>\n *\n * Note that docker-content-digest header can be undefined, so if you\n * need a manifest digest, use the `digestFromManifestStr` function.\n */\n async getManifest(opts) {\n const acceptOCIManifests = opts.acceptOCIManifests ?? this.acceptOCIManifests;\n const acceptManifestLists = opts.acceptManifestLists ?? this.acceptManifestLists;\n await this.login();\n const headers = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n };\n const acceptTypes = [MEDIATYPE_MANIFEST_V2];\n if (acceptManifestLists) {\n acceptTypes.push(MEDIATYPE_MANIFEST_LIST_V2);\n }\n if (acceptOCIManifests) {\n acceptTypes.push(MEDIATYPE_OCI_MANIFEST_V1);\n if (acceptManifestLists) {\n acceptTypes.push(MEDIATYPE_OCI_MANIFEST_INDEX_V1);\n }\n }\n headers[\"accept\"] = acceptTypes.join(\", \");\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName ?? \"\")}/manifests/${encodeURI(opts.ref)}`, this._url);\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"GET\",\n headers: headers,\n redirect: opts.followRedirects == false ? \"manual\" : \"follow\",\n });\n if (resp.status === 401) {\n const body = await resp.json();\n const errMsg = _getRegistryErrorMessage(body);\n throw new Error(`Manifest ${JSON.stringify(opts.ref)} Not Found: ${errMsg}`);\n }\n if (!resp.ok) {\n throw new Error(`Unexpected HTTP ${resp.status} from ${url.toString()}`);\n }\n const manifest = (await resp.json());\n if (manifest.schemaVersion === 1) {\n throw new Error(`schemaVersion 1 is not supported by /x/docker_registry_client.`);\n }\n return { resp, manifest };\n }\n /**\n * Makes a http request to the given url, following any redirects, then fires\n * the callback(err, req, responses) with the result.\n *\n * Note that 'responses' is an *array* of Response objects, with\n * the last response being at the end of the array. When there is more than\n * one response, it means a redirect has been followed.\n */\n async _makeHttpRequest(opts) {\n const followRedirects = opts.followRedirects ?? true;\n const maxRedirects = opts.maxRedirects ?? 3;\n let numRedirs = 0;\n const req = {\n path: opts.path,\n headers: opts.headers,\n };\n const ress = new Array();\n while (numRedirs < maxRedirects) {\n numRedirs += 1;\n const url = new URL(req.path, this._url);\n const headers = {\n ...req.headers,\n \"user-agent\": this._userAgent,\n };\n const resp = await this._adapter.fetch(url.toString(), {\n method: opts.method,\n headers: headers,\n redirect: \"manual\",\n });\n ress.push(resp);\n if (!followRedirects)\n return ress;\n if (!(resp.status === 302 || resp.status === 307))\n return ress;\n const location = resp.headers.get(\"location\");\n if (!location)\n return ress;\n const loc = new URL(location, url);\n // this.log.trace({numRedirs: numRedirs, loc: loc}, 'got redir response');\n req.path = loc.toString();\n req.headers = {};\n }\n throw new e.TooManyRedirectsError(`maximum number of redirects (${maxRedirects}) hit`);\n }\n async _headOrGetBlob(method, digest) {\n await this.login();\n return await this._makeHttpRequest({\n method: method,\n path: `/v2/${encodeURI(this.repo.remoteName ?? \"\")}/blobs/${encodeURI(digest)}`,\n headers: this._headers,\n });\n }\n /*\n * Get an image file blob -- just the headers. See `getBlob`.\n *\n * <https://docs.docker.com/registry/spec/api/#get-blob>\n * <https://docs.docker.com/registry/spec/api/#pulling-an-image-manifest>\n *\n * This endpoint can return 3xx redirects. The first response often redirects\n * to an object CDN, which would then return the raw data.\n *\n * Interesting headers:\n * - `ress[0].headers.get('docker-content-digest')` is the digest of the\n * content to be downloaded\n * - `ress[-1].headers.get('content-length')` is the number of bytes to download\n * - `ress[-1].headers[*]` as appropriate for HTTP caching, range gets, etc.\n */\n async headBlob(opts) {\n const resp = await this._headOrGetBlob(\"HEAD\", opts.digest);\n // No need to cancel body - fetch returns complete responses\n return resp;\n }\n /**\n * Download a blob and return its ArrayBuffer.\n * <https://docs.docker.com/registry/spec/api/#get-blob>\n *\n * @return\n * The `buffer` is the blob's content as an ArrayBuffer.\n * `ress` (plural of 'res') is an array of responses\n * after following redirects. The full set of responses are returned mainly because\n * headers on both the first, e.g. 'Docker-Content-Digest', and last,\n * e.g. 'Content-Length', might be interesting.\n */\n async downloadBlob(opts) {\n const ress = await this._headOrGetBlob(\"GET\", opts.digest);\n const lastResp = ress[ress.length - 1];\n if (!lastResp) {\n throw new e.BlobReadError(`No response available for blob ${opts.digest}`);\n }\n const buffer = await lastResp.arrayBuffer();\n const dcdHeader = ress[0]?.headers.get(\"docker-content-digest\");\n if (dcdHeader) {\n const dcdInfo = _parseDockerContentDigest(dcdHeader);\n if (dcdInfo.raw !== opts.digest) {\n throw new e.BadDigestError(`Docker-Content-Digest header, ${dcdInfo.raw}, does not match ` +\n `given digest, ${opts.digest}`);\n }\n // Validate the digest\n await dcdInfo.validate(buffer);\n }\n return { ress, buffer };\n }\n /**\n * Upload a blob using POST then PUT workflow.\n * <https://github.com/opencontainers/distribution-spec/blob/main/spec.md#post-then-put>\n *\n * @param opts.data The blob data as ArrayBuffer or Uint8Array\n * @param opts.digest Optional digest. If not provided, it will be calculated.\n * @returns Object with digest and size of the uploaded blob\n */\n async pushBlob(opts) {\n await this.login();\n // Convert to ArrayBuffer if needed\n const buffer = opts.data instanceof Uint8Array\n ? new Uint8Array(opts.data).buffer\n : opts.data;\n // Calculate digest\n const hashBuffer = await getCrypto().subtle.digest(\"SHA-256\", buffer);\n const digest = `sha256:${encodeHex(hashBuffer)}`;\n // Step 1: POST to initiate upload\n const postUrl = new URL(`/v2/${encodeURI(this.repo.remoteName)}/blobs/uploads/`, this._url);\n const postHeaders = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-length\": \"0\",\n };\n const postResp = await this._adapter.fetch(postUrl.toString(), {\n method: \"POST\",\n headers: postHeaders,\n });\n if (postResp.status !== 202) {\n throw new Error(`Failed to initiate blob upload: HTTP ${postResp.status}`);\n }\n // Get upload URL from Location header\n const uploadLocation = postResp.headers.get(\"location\");\n if (!uploadLocation) {\n throw new Error(\"No Location header in POST response\");\n }\n // Step 2: PUT to upload blob\n const uploadUrl = new URL(uploadLocation, this._url);\n uploadUrl.searchParams.set(\"digest\", digest);\n const putHeaders = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-type\": \"application/octet-stream\",\n \"content-length\": buffer.byteLength.toString(),\n };\n const putResp = await this._adapter.fetch(uploadUrl.toString(), {\n method: \"PUT\",\n headers: putHeaders,\n body: buffer,\n });\n if (putResp.status !== 201) {\n throw new Error(`Failed to upload blob: HTTP ${putResp.status}`);\n }\n // Verify digest from response\n const returnedDigest = putResp.headers.get(\"docker-content-digest\");\n if (returnedDigest && returnedDigest !== digest) {\n throw new e.BadDigestError(`Digest mismatch: expected ${digest}, got ${returnedDigest}`);\n }\n return { digest, size: buffer.byteLength };\n }\n /**\n * Upload an image manifest.\n * <https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests>\n *\n * @param opts.ref The tag or digest to push to\n * @param opts.manifest The manifest object to upload\n * @param opts.mediaType Optional media type (defaults to OCI manifest type)\n * @returns Object with digest and size of the uploaded manifest\n */\n async pushManifest(opts) {\n await this.login();\n const manifestStr = JSON.stringify(opts.manifest);\n const manifestBuffer = new TextEncoder().encode(manifestStr);\n // Calculate digest\n const digest = await digestFromManifestStr(manifestStr);\n const url = new URL(`/v2/${encodeURI(this.repo.remoteName)}/manifests/${encodeURI(opts.ref)}`, this._url);\n const headers = {\n ...this._headers,\n \"user-agent\": this._userAgent,\n \"content-type\": opts.mediaType || \"application/vnd.oci.image.manifest.v1+json\",\n \"content-length\": manifestBuffer.byteLength.toString(),\n };\n const resp = await this._adapter.fetch(url.toString(), {\n method: \"PUT\",\n headers,\n body: manifestBuffer,\n });\n if (resp.status !== 201) {\n throw new Error(`Failed to push manifest: HTTP ${resp.status}`);\n }\n // Verify digest from response\n const returnedDigest = resp.headers.get(\"docker-content-digest\");\n if (returnedDigest && returnedDigest !== digest) {\n throw new e.BadDigestError(`Digest mismatch: expected ${digest}, got ${returnedDigest}`);\n }\n return { digest, size: manifestBuffer.byteLength };\n }\n /**\n * Push a plugin manifest as a config blob and create an OCI manifest.\n * This follows the OCI spec where the Obsidian manifest is stored as the config.\n *\n * @param opts.ref The tag or digest to push to\n * @param opts.pluginManifest The Obsidian plugin manifest\n * @param opts.layers The layer descriptors (main.js, styles.css, etc.)\n * @param opts.annotations Optional annotations for the OCI manifest\n * @returns Object with digest, configDigest, and the created manifest\n */\n async pushPluginManifest(opts) {\n // Step 1: Push plugin manifest as config blob\n const manifestStr = JSON.stringify(opts.pluginManifest);\n const manifestBuffer = new TextEncoder().encode(manifestStr);\n const configResult = await this.pushBlob({\n data: manifestBuffer,\n });\n // Step 2: Build OCI manifest with plugin manifest as config\n const manifest = {\n schemaVersion: 2,\n mediaType: MEDIATYPE_OCI_MANIFEST_V1,\n artifactType: \"application/vnd.obsidian.plugin.v1+json\",\n config: {\n mediaType: MEDIATYPE_OBSIDIAN_PLUGIN_CONFIG_V1,\n digest: configResult.digest,\n size: configResult.size,\n },\n layers: opts.layers,\n annotations: opts.annotations,\n };\n // Step 3: Push the OCI manifest\n const manifestResult = await this.pushManifest({\n ref: opts.ref,\n manifest,\n mediaType: MEDIATYPE_OCI_MANIFEST_V1,\n });\n return {\n digest: manifestResult.digest,\n configDigest: configResult.digest,\n manifest,\n };\n }\n /**\n * Pull a plugin manifest by extracting it from the OCI config blob.\n * This follows the OCI spec where the Obsidian manifest is stored as the config.\n *\n * @param opts.ref The tag or digest to pull\n * @returns Object with the plugin manifest, OCI manifest, and digests\n */\n async pullPluginManifest(opts) {\n // Step 1: Pull the OCI manifest\n const manifestResult = await this.getManifest({ ref: opts.ref });\n const manifest = manifestResult.manifest;\n // Step 2: Validate manifest has config\n if (!(\"config\" in manifest) || !manifest.config) {\n throw new Error(\"Manifest does not contain a config\");\n }\n const ociManifest = manifest;\n const manifestDigest = manifestResult.resp.headers.get(\"docker-content-digest\") || \"\";\n // Step 3: Pull the config blob\n const { buffer: configBuffer } = await this.downloadBlob({\n digest: ociManifest.config.digest,\n });\n // Step 4: Parse the plugin manifest from config\n const configText = new TextDecoder().decode(configBuffer);\n const pluginManifest = JSON.parse(configText);\n return {\n pluginManifest,\n manifest: ociManifest,\n manifestDigest,\n configDigest: ociManifest.config.digest,\n };\n }\n}\n", "import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ObsidianManifest } from \"@shard-for-obsidian/lib\";\n\nexport interface DiscoveredPlugin {\n directory: string;\n manifest: {\n path: string;\n content: ArrayBuffer;\n parsed: ObsidianManifest;\n };\n mainJs: {\n path: string;\n content: ArrayBuffer;\n };\n stylesCss?: {\n path: string;\n content: ArrayBuffer;\n };\n}\n\n/**\n * Discover plugin files in a directory.\n * Finds manifest.json (required), main.js (required), and styles.css (optional).\n *\n * @param directory - Directory to scan for plugin files\n * @returns Discovered plugin information\n * @throws Error if required files are missing or invalid\n */\nexport async function discoverPlugin(\n directory: string,\n): Promise<DiscoveredPlugin> {\n // Resolve absolute path\n const absDirectory = path.resolve(directory);\n\n // Check directory exists\n try {\n const stat = await fs.stat(absDirectory);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${directory}`);\n }\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`Directory not found: ${directory}`);\n }\n throw err;\n }\n\n // Find manifest.json (required)\n const manifestPath = path.join(absDirectory, \"manifest.json\");\n let manifestContent: ArrayBuffer;\n let manifestParsed: ObsidianManifest;\n\n try {\n const buffer = await fs.readFile(manifestPath);\n manifestContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n\n // Parse and validate manifest\n const text = new TextDecoder().decode(manifestContent);\n const parsed = JSON.parse(text) as Record<string, unknown>;\n\n // Validate required fields\n if (!parsed.version || typeof parsed.version !== \"string\") {\n throw new Error('manifest.json missing required \"version\" field');\n }\n if (!parsed.id || typeof parsed.id !== \"string\") {\n throw new Error('manifest.json missing required \"id\" field');\n }\n if (!parsed.name || typeof parsed.name !== \"string\") {\n throw new Error('manifest.json missing required \"name\" field');\n }\n if (!parsed.minAppVersion || typeof parsed.minAppVersion !== \"string\") {\n throw new Error('manifest.json missing required \"minAppVersion\" field');\n }\n if (!parsed.description || typeof parsed.description !== \"string\") {\n throw new Error('manifest.json missing required \"description\" field');\n }\n if (!parsed.author || typeof parsed.author !== \"string\") {\n throw new Error('manifest.json missing required \"author\" field');\n }\n\n manifestParsed = parsed as unknown as ObsidianManifest;\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`manifest.json not found in ${directory}`);\n }\n if (err instanceof SyntaxError) {\n throw new Error(`Could not parse manifest.json: ${err.message}`);\n }\n throw err;\n }\n\n // Find main.js (required)\n const mainJsPath = path.join(absDirectory, \"main.js\");\n let mainJsContent: ArrayBuffer;\n\n try {\n const buffer = await fs.readFile(mainJsPath);\n mainJsContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n throw new Error(`main.js not found in ${directory}`);\n }\n throw err;\n }\n\n // Find styles.css (optional)\n const stylesCssPath = path.join(absDirectory, \"styles.css\");\n let stylesCssContent: ArrayBuffer | undefined;\n\n try {\n const buffer = await fs.readFile(stylesCssPath);\n stylesCssContent = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n } catch (err) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n ) {\n // styles.css is optional, ignore error\n stylesCssContent = undefined;\n } else {\n throw err;\n }\n }\n\n return {\n directory: absDirectory,\n manifest: {\n path: manifestPath,\n content: manifestContent,\n parsed: manifestParsed,\n },\n mainJs: {\n path: mainJsPath,\n content: mainJsContent,\n },\n ...(stylesCssContent && {\n stylesCss: {\n path: stylesCssPath,\n content: stylesCssContent,\n },\n }),\n };\n}\n", "import { OciRegistryClient, parseRepoAndRef } from \"@shard-for-obsidian/lib\";\nimport type {\n ManifestOCIDescriptor,\n FetchAdapter,\n ObsidianManifest,\n} from \"@shard-for-obsidian/lib\";\nimport { discoverPlugin } from \"../lib/plugin.js\";\nimport { Logger } from \"../lib/logger.js\";\n\n/**\n * Derive GitHub repository URL from registry remote name.\n * Takes the first two path components to form owner/repo.\n *\n * Examples:\n * shard-for-obsidian/shard -> https://github.com/shard-for-obsidian/shard\n * shard-for-obsidian/shard/plugin -> https://github.com/shard-for-obsidian/shard\n */\nfunction deriveGitHubUrl(remoteName: string): string {\n const parts = remoteName.split('/');\n if (parts.length < 2) {\n throw new Error(\n `Cannot derive GitHub URL from ${remoteName}. Need at least owner/repo format.`,\n );\n }\n\n const owner = parts[0];\n const repo = parts[1];\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport interface PushOptions {\n directory: string;\n repository: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface PushResult {\n digest: string;\n tag: string;\n size: number;\n repository: string;\n}\n\n/**\n * Push an Obsidian plugin to GHCR.\n *\n * @param opts - Push options\n * @returns Push result with digest and tag\n */\nexport async function pushCommand(opts: PushOptions): Promise<PushResult> {\n const { directory, repository, token, logger, adapter } = opts;\n\n // Step 1: Discover plugin files\n logger.log(`Discovering plugin files in ${directory}...`);\n const plugin = await discoverPlugin(directory);\n const version = plugin.manifest.parsed.version;\n logger.log(`Found plugin version ${version}`);\n\n // Step 2: Parse repository and add version tag\n const fullRef = repository.includes(\":\")\n ? repository\n : `${repository}:${version}`;\n logger.log(`Pushing to ${fullRef}...`);\n\n const ref = parseRepoAndRef(fullRef);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n scopes: [\"push\", \"pull\"],\n });\n\n // Step 3: Push each file as a blob\n const layers: ManifestOCIDescriptor[] = [];\n\n // No longer need to push manifest.json as a layer - it's now in the config\n\n // Push main.js\n logger.log(\"Pushing main.js...\");\n const mainJsResult = await client.pushBlob({\n data: plugin.mainJs.content,\n });\n layers.push({\n mediaType: \"application/javascript\",\n digest: mainJsResult.digest,\n size: mainJsResult.size,\n annotations: {\n \"vnd.obsidianmd.layer.filename\": \"main.js\",\n },\n });\n logger.log(\n `Pushed main.js: ${mainJsResult.digest.slice(0, 19)}... (${mainJsResult.size} bytes)`,\n );\n\n // Push styles.css if present\n if (plugin.stylesCss) {\n logger.log(\"Pushing styles.css...\");\n const stylesCssResult = await client.pushBlob({\n data: plugin.stylesCss.content,\n });\n layers.push({\n mediaType: \"text/css\",\n digest: stylesCssResult.digest,\n size: stylesCssResult.size,\n annotations: {\n \"vnd.obsidianmd.layer.filename\": \"styles.css\",\n },\n });\n logger.log(\n `Pushed styles.css: ${stylesCssResult.digest.slice(0, 19)}... (${stylesCssResult.size} bytes)`,\n );\n }\n\n // Step 4: Derive GitHub URL and prepare annotations\n const githubUrl = deriveGitHubUrl(ref.remoteName);\n const manifest = plugin.manifest.parsed;\n\n const annotations: Record<string, string> = {\n \"vnd.obsidianmd.plugin.id\": manifest.id,\n \"vnd.obsidianmd.plugin.name\": manifest.name,\n \"vnd.obsidianmd.plugin.version\": manifest.version,\n \"vnd.obsidianmd.plugin.description\": manifest.description,\n \"vnd.obsidianmd.plugin.author\": manifest.author,\n \"vnd.obsidianmd.plugin.repo\": githubUrl,\n \"vnd.obsidianmd.plugin.published-at\": new Date().toISOString(),\n };\n\n // Add optional fields if present\n if (manifest.authorUrl) {\n annotations[\"vnd.obsidianmd.plugin.author-url\"] = manifest.authorUrl;\n }\n if (manifest.minAppVersion) {\n annotations[\"vnd.obsidianmd.plugin.min-app-version\"] = manifest.minAppVersion;\n }\n\n // Step 5: Push plugin manifest using new method\n logger.log(\"Pushing plugin manifest...\");\n const manifestPushResult = await client.pushPluginManifest({\n ref: ref.tag || version,\n pluginManifest: manifest as unknown as ObsidianManifest,\n layers,\n annotations,\n });\n\n logger.success(`Successfully pushed ${fullRef}`);\n logger.log(`Manifest digest: ${manifestPushResult.digest}`);\n logger.log(`GitHub repository: ${githubUrl}`);\n\n // Calculate total size from manifest\n const totalSize = manifestPushResult.manifest.layers.reduce(\n (sum, layer) => sum + layer.size,\n 0,\n );\n\n return {\n digest: manifestPushResult.digest,\n tag: ref.tag || version,\n size: totalSize,\n repository: fullRef,\n };\n}\n", "import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { OciRegistryClient, parseRepoAndRef } from \"@shard-for-obsidian/lib\";\nimport type { FetchAdapter } from \"@shard-for-obsidian/lib\";\nimport { Logger } from \"../lib/logger.js\";\n\nexport interface PullOptions {\n repository: string;\n output: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface PullResult {\n files: string[];\n output: string;\n digest: string;\n}\n\n/**\n * Pull an Obsidian plugin from GHCR.\n *\n * @param opts - Pull options\n * @returns Pull result with extracted files\n */\nexport async function pullCommand(opts: PullOptions): Promise<PullResult> {\n const { repository, output, token, logger, adapter } = opts;\n\n // Step 1: Parse repository reference\n logger.log(`Pulling ${repository}...`);\n const ref = parseRepoAndRef(repository);\n\n if (!ref.tag && !ref.digest) {\n throw new Error(\"Repository reference must include tag or digest\");\n }\n\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n });\n\n // Step 2: Fetch manifest and extract plugin manifest from config\n logger.log(\"Fetching manifest...\");\n const refString = ref.tag || ref.digest || \"\";\n const pullResult = await client.pullPluginManifest({ ref: refString });\n const manifest = pullResult.manifest;\n\n logger.log(`Manifest digest: ${pullResult.manifestDigest}`);\n\n // Step 3: Create output directory if needed\n const absOutput = path.resolve(output);\n logger.log(`Creating output directory: ${absOutput}`);\n await fs.mkdir(absOutput, { recursive: true });\n\n // Step 4: Write manifest.json from config\n const manifestPath = path.join(absOutput, \"manifest.json\");\n const manifestJson = JSON.stringify(pullResult.pluginManifest, null, 2);\n await fs.writeFile(manifestPath, manifestJson, \"utf-8\");\n logger.log(`Wrote manifest.json (${manifestJson.length} bytes)`);\n\n const files: string[] = [\"manifest.json\"];\n\n // Step 5: Download and extract each layer\n for (const layer of manifest.layers) {\n // Extract filename from annotation\n const filename = layer.annotations?.[\"vnd.obsidianmd.layer.filename\"];\n if (!filename) {\n throw new Error(\n `Layer ${layer.digest} missing required filename annotation`,\n );\n }\n\n logger.log(`Downloading ${filename}...`);\n\n // Download blob\n const blobResult = await client.downloadBlob({\n digest: layer.digest,\n });\n\n // Write to output directory\n const filePath = path.join(absOutput, filename);\n const buffer = Buffer.from(blobResult.buffer);\n await fs.writeFile(filePath, buffer);\n\n logger.log(`Wrote ${filename} (${buffer.length} bytes)`);\n files.push(filename);\n }\n\n logger.success(`Successfully pulled ${repository}`);\n logger.log(`Files extracted to: ${absOutput}`);\n\n return {\n files,\n output: absOutput,\n digest: pullResult.manifestDigest,\n };\n}\n", "/**\n * URL to the Obsidian community plugins JSON file\n */\nexport const COMMUNITY_PLUGINS_URL =\n \"https://raw.githubusercontent.com/obsidianmd/obsidian-releases/refs/heads/master/community-plugins.json\";\n\n/**\n * Represents a community plugin entry from the Obsidian marketplace\n */\nexport interface CommunityPlugin {\n /** Plugin ID (used in folder name and as identifier) */\n id: string;\n /** Display name of the plugin */\n name: string;\n /** Author name */\n author: string;\n /** Plugin description */\n description: string;\n /** GitHub repository in format \"owner/repo\" */\n repo: string;\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\nimport {\n COMMUNITY_PLUGINS_URL,\n type CommunityPlugin,\n} from \"./community-plugins.js\";\n\n/**\n * Cache for Obsidian community plugins.\n * Fetches and caches the list of community plugins from the official repository.\n */\nexport class CommunityPluginsCache {\n private adapter: FetchAdapter;\n private plugins: CommunityPlugin[] | null = null;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n }\n\n /**\n * Fetch the list of community plugins.\n * Results are cached for subsequent calls.\n *\n * @returns Array of community plugins\n * @throws Error if fetch fails\n */\n async fetch(): Promise<CommunityPlugin[]> {\n // Return cached data if available\n if (this.plugins !== null) {\n return this.plugins;\n }\n\n // Fetch from URL\n const response = await this.adapter.fetch(COMMUNITY_PLUGINS_URL);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch community plugins: ${response.status}`);\n }\n\n const plugins = (await response.json()) as CommunityPlugin[];\n this.plugins = plugins;\n return plugins;\n }\n\n /**\n * Find a plugin by ID.\n * Fetches the plugin list if not already cached.\n *\n * @param id - Plugin ID to search for\n * @returns Plugin if found, undefined otherwise\n */\n async findPlugin(id: string): Promise<CommunityPlugin | undefined> {\n const plugins = await this.fetch();\n return plugins.find((plugin) => plugin.id === id);\n }\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\n\n/**\n * Represents a GitHub release asset\n */\nexport interface GitHubReleaseAsset {\n /** Name of the asset file */\n name: string;\n /** URL to download the asset */\n browser_download_url: string;\n}\n\n/**\n * Represents a GitHub release\n */\nexport interface GitHubRelease {\n /** Tag name of the release (e.g., \"1.2.3\") */\n tag_name: string;\n /** Array of release assets */\n assets: GitHubReleaseAsset[];\n}\n\n/**\n * Fetcher for GitHub releases.\n * Provides methods to fetch releases from the GitHub API.\n */\nexport class GitHubReleaseFetcher {\n private adapter: FetchAdapter;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n }\n\n /**\n * Fetch the latest release from a GitHub repository.\n *\n * @param repo - Repository in format \"owner/repo\"\n * @param token - Optional GitHub token for authentication\n * @returns Latest release information\n * @throws Error if fetch fails\n */\n async fetchLatestRelease(\n repo: string,\n token?: string,\n ): Promise<GitHubRelease> {\n const url = `https://api.github.com/repos/${repo}/releases/latest`;\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await this.adapter.fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch latest release: ${response.status}`);\n }\n\n return (await response.json()) as GitHubRelease;\n }\n\n /**\n * Fetch a specific release by tag from a GitHub repository.\n *\n * @param repo - Repository in format \"owner/repo\"\n * @param tag - Tag name (e.g., \"1.2.3\")\n * @param token - Optional GitHub token for authentication\n * @returns Release information\n * @throws Error if fetch fails\n */\n async fetchReleaseByTag(\n repo: string,\n tag: string,\n token?: string,\n ): Promise<GitHubRelease> {\n const url = `https://api.github.com/repos/${repo}/releases/tags/${tag}`;\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github.v3+json\",\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n const response = await this.adapter.fetch(url, { headers });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch release ${tag}: ${response.status}`);\n }\n\n return (await response.json()) as GitHubRelease;\n }\n}\n", "import type {\n FetchAdapter,\n ObsidianManifest,\n ManifestOCIDescriptor,\n} from \"@shard-for-obsidian/lib\";\nimport { OciRegistryClient, parseRepoAndRef } from \"@shard-for-obsidian/lib\";\nimport { CommunityPluginsCache } from \"./community-cache.js\";\nimport { GitHubReleaseFetcher } from \"./github-release.js\";\n\n/**\n * Options for converting a plugin\n */\nexport interface ConvertPluginOptions {\n /** Plugin ID from community plugins list */\n pluginId: string;\n /** Optional specific version to convert (defaults to latest) */\n version?: string;\n /** Target OCI repository */\n repository: string;\n /** GitHub token for authentication */\n token: string;\n}\n\n/**\n * Result of a plugin conversion\n */\nexport interface ConvertPluginResult {\n /** Plugin ID */\n pluginId: string;\n /** Plugin version */\n version: string;\n /** Target repository with tag */\n repository: string;\n /** GitHub repository URL */\n githubRepo: string;\n /** Parsed manifest */\n manifest: ObsidianManifest;\n /** main.js content */\n mainJs: string;\n /** styles.css content (if present) */\n stylesCss?: string;\n}\n\n/**\n * Options for pushing to registry\n */\nexport interface PushToRegistryOptions {\n /** Repository with tag */\n repository: string;\n /** GitHub repository URL */\n githubRepo: string;\n /** GitHub token */\n token: string;\n /** Plugin data to push */\n pluginData: {\n manifest: ObsidianManifest;\n mainJs: string;\n stylesCss?: string;\n };\n}\n\n/**\n * Result of pushing to registry\n */\nexport interface PushToRegistryResult {\n /** Manifest digest */\n digest: string;\n /** Tag */\n tag: string;\n /** Size */\n size: number;\n /** Repository */\n repository: string;\n}\n\n/**\n * Converts legacy Obsidian plugins to OCI format.\n * Fetches plugins from GitHub releases and pushes to OCI registry.\n */\nexport class PluginConverter {\n private adapter: FetchAdapter;\n private communityCache: CommunityPluginsCache;\n private releaseFetcher: GitHubReleaseFetcher;\n\n constructor(adapter: FetchAdapter) {\n this.adapter = adapter;\n this.communityCache = new CommunityPluginsCache(adapter);\n this.releaseFetcher = new GitHubReleaseFetcher(adapter);\n }\n\n /**\n * Convert a plugin from GitHub releases to OCI format.\n *\n * @param options - Conversion options\n * @returns Conversion result with plugin data\n * @throws Error if plugin not found or conversion fails\n */\n async convertPlugin(\n options: ConvertPluginOptions,\n ): Promise<ConvertPluginResult> {\n const { pluginId, version, repository, token } = options;\n\n // Step 1: Find plugin in community list\n const plugin = await this.communityCache.findPlugin(pluginId);\n if (!plugin) {\n throw new Error(`Plugin \"${pluginId}\" not found in community plugins`);\n }\n\n // Step 2: Fetch release from GitHub\n const release = version\n ? await this.releaseFetcher.fetchReleaseByTag(plugin.repo, version, token)\n : await this.releaseFetcher.fetchLatestRelease(plugin.repo, token);\n\n // Step 3: Find required assets\n const manifestAsset = release.assets.find(\n (a) => a.name === \"manifest.json\",\n );\n const mainJsAsset = release.assets.find((a) => a.name === \"main.js\");\n const stylesCssAsset = release.assets.find((a) => a.name === \"styles.css\");\n\n if (!manifestAsset) {\n throw new Error(\"manifest.json not found in release\");\n }\n if (!mainJsAsset) {\n throw new Error(\"main.js not found in release\");\n }\n\n // Step 4: Download assets\n const manifestResponse = await this.adapter.fetch(\n manifestAsset.browser_download_url,\n );\n if (!manifestResponse.ok) {\n throw new Error(\n `Failed to download manifest.json: ${manifestResponse.status}`,\n );\n }\n const manifestJson = await manifestResponse.text();\n const manifest = JSON.parse(manifestJson) as ObsidianManifest;\n\n const mainJsResponse = await this.adapter.fetch(\n mainJsAsset.browser_download_url,\n );\n if (!mainJsResponse.ok) {\n throw new Error(`Failed to download main.js: ${mainJsResponse.status}`);\n }\n const mainJs = await mainJsResponse.text();\n\n let stylesCss: string | undefined;\n if (stylesCssAsset) {\n const stylesCssResponse = await this.adapter.fetch(\n stylesCssAsset.browser_download_url,\n );\n if (!stylesCssResponse.ok) {\n throw new Error(\n `Failed to download styles.css: ${stylesCssResponse.status}`,\n );\n }\n stylesCss = await stylesCssResponse.text();\n }\n\n // Step 5: Build result\n const fullRepository = repository.includes(\":\")\n ? repository\n : `${repository}:${release.tag_name}`;\n\n return {\n pluginId,\n version: release.tag_name,\n repository: fullRepository,\n githubRepo: plugin.repo,\n manifest,\n mainJs,\n stylesCss,\n };\n }\n\n /**\n * Push plugin data to OCI registry.\n *\n * @param options - Push options\n * @returns Push result\n * @throws Error if push fails\n */\n async pushToRegistry(\n options: PushToRegistryOptions,\n ): Promise<PushToRegistryResult> {\n const { repository, githubRepo, token, pluginData } = options;\n\n // Parse repository reference\n const ref = parseRepoAndRef(repository);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter: this.adapter,\n scopes: [\"push\", \"pull\"],\n });\n\n // Push blobs\n const layers: ManifestOCIDescriptor[] = [];\n\n // Push main.js\n const mainJsResult = await client.pushBlob({\n data: new TextEncoder().encode(pluginData.mainJs),\n });\n layers.push({\n mediaType: \"application/javascript\",\n digest: mainJsResult.digest,\n size: mainJsResult.size,\n annotations: {\n \"vnd.obsidianmd.layer.filename\": \"main.js\",\n },\n });\n\n // Push styles.css if present\n if (pluginData.stylesCss) {\n const stylesCssResult = await client.pushBlob({\n data: new TextEncoder().encode(pluginData.stylesCss),\n });\n layers.push({\n mediaType: \"text/css\",\n digest: stylesCssResult.digest,\n size: stylesCssResult.size,\n annotations: {\n \"vnd.obsidianmd.layer.filename\": \"styles.css\",\n },\n });\n }\n\n // Push manifest with vendor annotations\n const annotations: Record<string, string> = {\n \"vnd.obsidianmd.plugin.id\": pluginData.manifest.id,\n \"vnd.obsidianmd.plugin.name\": pluginData.manifest.name,\n \"vnd.obsidianmd.plugin.version\": pluginData.manifest.version,\n \"vnd.obsidianmd.plugin.description\": pluginData.manifest.description,\n \"vnd.obsidianmd.plugin.author\": pluginData.manifest.author,\n \"vnd.obsidianmd.plugin.repo\": githubRepo,\n \"vnd.obsidianmd.plugin.published-at\": new Date().toISOString(),\n \"vnd.obsidianmd.plugin.converted\": \"true\",\n \"vnd.obsidianmd.plugin.original-repo\": \"obsidianmd/obsidian-releases\",\n };\n\n // Add optional fields if present\n if (pluginData.manifest.authorUrl) {\n annotations[\"vnd.obsidianmd.plugin.author-url\"] = pluginData.manifest.authorUrl;\n }\n if (pluginData.manifest.minAppVersion) {\n annotations[\"vnd.obsidianmd.plugin.min-app-version\"] = pluginData.manifest.minAppVersion;\n }\n\n const manifestPushResult = await client.pushPluginManifest({\n ref: ref.tag || pluginData.manifest.version,\n pluginManifest: pluginData.manifest,\n layers,\n annotations,\n });\n\n // Calculate total size from manifest\n const totalSize = manifestPushResult.manifest.layers.reduce(\n (sum, layer) => sum + layer.size,\n 0,\n );\n\n return {\n digest: manifestPushResult.digest,\n tag: ref.tag || pluginData.manifest.version,\n size: totalSize,\n repository,\n };\n }\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\nimport { PluginConverter } from \"../lib/converter.js\";\nimport { Logger } from \"../lib/logger.js\";\n\nexport interface ConvertOptions {\n pluginId: string;\n repository: string;\n version?: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface ConvertResult {\n pluginId: string;\n version: string;\n repository: string;\n digest: string;\n size: number;\n}\n\n/**\n * Convert a legacy Obsidian plugin from GitHub releases to OCI format.\n *\n * @param opts - Convert options\n * @returns Convert result with digest and repository\n */\nexport async function convertCommand(\n opts: ConvertOptions,\n): Promise<ConvertResult> {\n const { pluginId, repository, version, token, logger, adapter } = opts;\n\n // Step 1: Create converter\n const converter = new PluginConverter(adapter);\n\n // Step 2: Convert plugin from GitHub releases\n logger.log(`Converting plugin \"${pluginId}\"...`);\n if (version) {\n logger.log(`Using specific version: ${version}`);\n } else {\n logger.log(\"Using latest version\");\n }\n\n const convertResult = await converter.convertPlugin({\n pluginId,\n version,\n repository,\n token,\n });\n\n logger.log(\n `Downloaded plugin ${convertResult.pluginId} v${convertResult.version}`,\n );\n logger.log(` - manifest.json: ${convertResult.manifest.name}`);\n logger.log(` - main.js: ${convertResult.mainJs.length} bytes`);\n if (convertResult.stylesCss) {\n logger.log(` - styles.css: ${convertResult.stylesCss.length} bytes`);\n }\n\n // Step 3: Push to OCI registry\n logger.log(`\\nPushing to ${convertResult.repository}...`);\n const pushResult = await converter.pushToRegistry({\n repository: convertResult.repository,\n githubRepo: convertResult.githubRepo,\n token,\n pluginData: {\n manifest: convertResult.manifest,\n mainJs: convertResult.mainJs,\n stylesCss: convertResult.stylesCss,\n },\n });\n\n logger.success(\n `Successfully converted and pushed ${convertResult.pluginId} v${convertResult.version}`,\n );\n logger.log(`Manifest digest: ${pushResult.digest}`);\n logger.log(`Repository: ${pushResult.repository}`);\n\n return {\n pluginId: convertResult.pluginId,\n version: convertResult.version,\n repository: pushResult.repository,\n digest: pushResult.digest,\n size: pushResult.size,\n };\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\n\nexport interface PluginVersion {\n tag: string;\n publishedAt: string; // ISO 8601\n size: number; // bytes\n annotations: Record<string, string>;\n}\n\nexport interface MarketplacePlugin {\n // Primary identifiers\n id: string;\n registryUrl: string; // ghcr.io/owner/repo (PRIMARY)\n\n // Metadata from manifest\n name: string;\n author: string;\n description: string;\n\n // Optional metadata\n license?: string;\n minObsidianVersion?: string;\n authorUrl?: string;\n\n // Derived/optional\n repository?: string; // GitHub URL derived from vnd.obsidianmd.plugin.repo\n tags?: string[]; // For categorization\n\n // New fields\n introduction?: string; // Markdown content from file body\n versions?: PluginVersion[]; // All available versions from OCI\n}\n\nexport interface MarketplaceIndex {\n plugins: MarketplacePlugin[];\n generatedAt: string; // ISO 8601 timestamp\n}\n\nexport const DEFAULT_MARKETPLACE_URL =\n \"https://shard-for-obsidian.github.io/shard/plugins.json\";\n\n/**\n * Client for fetching marketplace data from the registry.\n */\nexport class MarketplaceClient {\n constructor(\n private adapter: FetchAdapter,\n private marketplaceUrl: string = DEFAULT_MARKETPLACE_URL,\n ) {}\n\n /**\n * Fetch all plugins from the marketplace.\n */\n async fetchPlugins(): Promise<MarketplacePlugin[]> {\n const response = await this.adapter.fetch(this.marketplaceUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch marketplace data: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = (await response.json()) as MarketplaceIndex;\n\n if (!data.plugins || !Array.isArray(data.plugins)) {\n throw new Error(\"Invalid marketplace data format\");\n }\n\n return data.plugins;\n }\n\n /**\n * Find a plugin by ID.\n */\n async findPluginById(pluginId: string): Promise<MarketplacePlugin | null> {\n const plugins = await this.fetchPlugins();\n return plugins.find((p) => p.id === pluginId) || null;\n }\n\n /**\n * Search plugins by keyword (searches in name, description, author, tags).\n */\n async searchPlugins(keyword: string): Promise<MarketplacePlugin[]> {\n const plugins = await this.fetchPlugins();\n const lowerKeyword = keyword.toLowerCase();\n\n return plugins.filter(\n (p) =>\n p.name.toLowerCase().includes(lowerKeyword) ||\n p.description.toLowerCase().includes(lowerKeyword) ||\n p.author.toLowerCase().includes(lowerKeyword) ||\n p.id.toLowerCase().includes(lowerKeyword) ||\n p.tags?.some((tag) => tag.toLowerCase().includes(lowerKeyword)),\n );\n }\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\nimport { parseRepoAndRef } from \"@shard-for-obsidian/lib\";\n\nexport interface QueryOciTagsOptions {\n registryUrl: string;\n adapter: FetchAdapter;\n token?: string;\n}\n\nexport interface QueryTagMetadataOptions {\n registryUrl: string;\n tag: string;\n adapter: FetchAdapter;\n token?: string;\n}\n\ninterface AuthChallenge {\n realm: string;\n service: string;\n scope: string;\n}\n\nexport interface TagMetadata {\n publishedAt: string;\n size: number;\n annotations: Record<string, string>;\n}\n\n/**\n * Parse WWW-Authenticate header to extract auth challenge.\n */\nfunction parseAuthChallenge(header: string): AuthChallenge | null {\n // Format: Bearer realm=\"...\",service=\"...\",scope=\"...\"\n const realmMatch = /realm=\"([^\"]+)\"/.exec(header);\n const serviceMatch = /service=\"([^\"]+)\"/.exec(header);\n const scopeMatch = /scope=\"([^\"]+)\"/.exec(header);\n\n if (!realmMatch || !serviceMatch || !scopeMatch) {\n return null;\n }\n\n return {\n realm: realmMatch[1],\n service: serviceMatch[1],\n scope: scopeMatch[1],\n };\n}\n\n/**\n * Exchange GitHub token for OCI registry token.\n */\nasync function getRegistryToken(\n challenge: AuthChallenge,\n adapter: FetchAdapter,\n githubToken: string,\n): Promise<string | null> {\n const url = `${challenge.realm}?service=${challenge.service}&scope=${challenge.scope}`;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n };\n\n // For GHCR, we need to use Basic auth with the GitHub token\n // Username can be anything, password is the token\n if (githubToken) {\n const basicAuth = Buffer.from(`token:${githubToken}`).toString(\"base64\");\n headers.Authorization = `Basic ${basicAuth}`;\n }\n\n try {\n const response = await adapter.fetch(url, { headers });\n\n if (!response.ok) {\n return null;\n }\n\n const data = (await response.json()) as { token?: string };\n return data.token || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Query all available tags from an OCI registry.\n */\nexport async function queryOciTags(\n opts: QueryOciTagsOptions,\n): Promise<string[]> {\n const { registryUrl, adapter, token } = opts;\n const ref = parseRepoAndRef(registryUrl);\n\n // OCI Distribution API: GET /v2/<name>/tags/list\n const url = `https://${ref.index.name}/v2/${ref.remoteName}/tags/list`;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n };\n\n // Try initial request\n let response = await adapter.fetch(url, { headers });\n\n // If 401, try to exchange token and retry\n if (response.status === 401 && token) {\n const wwwAuth = response.headers.get(\"www-authenticate\");\n if (wwwAuth) {\n const challenge = parseAuthChallenge(wwwAuth);\n if (challenge) {\n const registryToken = await getRegistryToken(challenge, adapter, token);\n if (registryToken) {\n headers.Authorization = `Bearer ${registryToken}`;\n response = await adapter.fetch(url, { headers });\n }\n }\n }\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to query OCI tags: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = (await response.json()) as { tags?: string[] };\n\n return data.tags || [];\n}\n\n/**\n * Query metadata for a specific tag.\n */\nexport async function queryTagMetadata(\n opts: QueryTagMetadataOptions,\n): Promise<TagMetadata> {\n const { registryUrl, tag, adapter, token } = opts;\n const ref = parseRepoAndRef(registryUrl);\n\n // OCI Distribution API: GET /v2/<name>/manifests/<tag>\n const url = `https://${ref.index.name}/v2/${ref.remoteName}/manifests/${tag}`;\n\n const headers: Record<string, string> = {\n Accept: \"application/vnd.oci.image.manifest.v1+json\",\n };\n\n // Try initial request\n let response = await adapter.fetch(url, { headers });\n\n // If 401, try to exchange token and retry\n if (response.status === 401 && token) {\n const wwwAuth = response.headers.get(\"www-authenticate\");\n if (wwwAuth) {\n const challenge = parseAuthChallenge(wwwAuth);\n if (challenge) {\n const registryToken = await getRegistryToken(challenge, adapter, token);\n if (registryToken) {\n headers.Authorization = `Bearer ${registryToken}`;\n response = await adapter.fetch(url, { headers });\n }\n }\n }\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to query tag metadata: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as {\n created?: string;\n config?: { size: number };\n layers?: Array<{ size: number }>;\n annotations?: Record<string, string>;\n };\n\n // Extract metadata\n const publishedAt = manifest.created || new Date().toISOString();\n const layerSizes = manifest.layers?.map((l) => l.size) || [];\n const size = layerSizes.reduce((sum, s) => sum + s, 0);\n const annotations = manifest.annotations || {};\n\n return {\n publishedAt,\n size,\n annotations,\n };\n}\n", "import { OciRegistryClient, parseRepoAndRef } from \"@shard-for-obsidian/lib\";\nimport type { FetchAdapter } from \"@shard-for-obsidian/lib\";\nimport { Logger } from \"../lib/logger.js\";\nimport { MarketplaceClient } from \"../lib/marketplace-client.js\";\nimport type { MarketplacePlugin } from \"../lib/marketplace-client.js\";\nimport { queryOciTags, queryTagMetadata } from \"../lib/oci-tags.js\";\nimport type { TagMetadata } from \"../lib/oci-tags.js\";\nimport { pullCommand } from \"./pull.js\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport interface MarketplaceRegisterOptions {\n repository: string;\n token: string;\n introduction?: string;\n logger: Logger;\n adapter: FetchAdapter;\n}\n\nexport interface MarketplaceRegisterResult {\n pluginId: string;\n name: string;\n author: string;\n description: string;\n registryUrl: string;\n repository?: string;\n license?: string;\n minObsidianVersion?: string;\n authorUrl?: string;\n mdPath: string;\n}\n\n/**\n * Register a plugin to the Shard marketplace.\n * Pulls plugin metadata from OCI registry and creates a markdown file with frontmatter.\n *\n * @param opts - Marketplace register options\n * @returns Register result with plugin metadata\n */\nexport async function marketplaceRegisterCommand(\n opts: MarketplaceRegisterOptions,\n): Promise<MarketplaceRegisterResult> {\n const { repository, token, introduction, logger, adapter } = opts;\n\n // Step 1: Parse repository reference\n logger.log(`Fetching plugin metadata from ${repository}...`);\n const ref = parseRepoAndRef(repository);\n const client = new OciRegistryClient({\n repo: ref,\n username: \"github\",\n password: token,\n adapter,\n scopes: [\"pull\"],\n });\n\n // Step 2: Pull plugin manifest\n const manifestResult = await client.pullPluginManifest({\n ref: ref.tag || \"latest\",\n });\n\n const pluginManifest = manifestResult.pluginManifest;\n const ociManifest = manifestResult.manifest;\n\n // Step 3: Extract metadata from plugin manifest\n const pluginId = pluginManifest.id;\n const name = pluginManifest.name;\n const author = pluginManifest.author;\n const description = pluginManifest.description || \"\";\n const minObsidianVersion = pluginManifest.minAppVersion;\n const authorUrl = pluginManifest.authorUrl;\n\n // Step 4: Get repository URL from annotations\n let gitHubRepoUrl: string | undefined;\n if (\n ociManifest.annotations &&\n ociManifest.annotations[\"vnd.obsidianmd.plugin.repo\"]\n ) {\n gitHubRepoUrl = ociManifest.annotations[\"vnd.obsidianmd.plugin.repo\"];\n }\n\n // Step 5: Build registry URL (use canonical name which includes registry host)\n const registryUrl = ref.canonicalName;\n\n logger.log(`Plugin ID: ${pluginId}`);\n logger.log(`Name: ${name}`);\n logger.log(`Author: ${author}`);\n logger.log(`Registry URL: ${registryUrl}`);\n if (gitHubRepoUrl) {\n logger.log(`Repository: ${gitHubRepoUrl}`);\n }\n\n // Step 6: Generate markdown with YAML frontmatter\n let frontmatter = `---\nid: ${pluginId}\nregistryUrl: ${registryUrl}\nname: ${name}\nauthor: ${author}\ndescription: ${description}\n`;\n\n if (gitHubRepoUrl) {\n frontmatter += `repository: ${gitHubRepoUrl}\\n`;\n }\n\n if (minObsidianVersion) {\n frontmatter += `minObsidianVersion: ${minObsidianVersion}\\n`;\n }\n\n if (authorUrl) {\n frontmatter += `authorUrl: ${authorUrl}\\n`;\n }\n\n frontmatter += `---\\n`;\n\n // Add introduction content if provided\n let markdownContent = frontmatter;\n if (introduction) {\n markdownContent += `\\n${introduction}\\n`;\n }\n\n // Step 7: Find marketplace directory (walk up from cwd)\n const marketplacePath = await findMarketplaceDir();\n const pluginsDir = path.join(marketplacePath, \"plugins\");\n const mdPath = path.join(pluginsDir, `${pluginId}.md`);\n\n // Step 8: Ensure plugins directory exists\n await fs.mkdir(pluginsDir, { recursive: true });\n\n // Step 9: Write markdown file\n await fs.writeFile(mdPath, markdownContent, \"utf-8\");\n\n logger.success(`Successfully registered plugin to ${mdPath}`);\n logger.log(`\\nNext steps:`);\n logger.log(`1. Review the generated markdown file`);\n logger.log(`2. Commit and push to the marketplace repository`);\n logger.log(`3. Submit a pull request to add your plugin to the marketplace`);\n\n return {\n pluginId,\n name,\n author,\n description,\n registryUrl,\n repository: gitHubRepoUrl,\n minObsidianVersion,\n authorUrl,\n mdPath,\n };\n}\n\n/**\n * Find the marketplace directory by walking up from cwd.\n * Looks for a directory containing marketplace/plugins/.\n */\nasync function findMarketplaceDir(): Promise<string> {\n let currentDir = process.cwd();\n const root = path.parse(currentDir).root;\n\n while (currentDir !== root) {\n const marketplacePath = path.join(currentDir, \"marketplace\");\n try {\n const stat = await fs.stat(marketplacePath);\n if (stat.isDirectory()) {\n return marketplacePath;\n }\n } catch {\n // Directory doesn't exist, continue searching\n }\n\n // Move up one directory\n currentDir = path.dirname(currentDir);\n }\n\n throw new Error(\n \"Could not find marketplace directory. \" +\n \"Please run this command from within the marketplace repository.\",\n );\n}\n\n/**\n * List all plugins in the marketplace.\n */\nexport async function marketplaceListCommand(opts: {\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin[]> {\n const { logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(\"Fetching marketplace plugins...\");\n const plugins = await client.fetchPlugins();\n\n logger.log(`\\nFound ${plugins.length} plugins:\\n`);\n\n for (const plugin of plugins) {\n logger.log(`${plugin.name} (${plugin.id})`);\n logger.log(` Author: ${plugin.author}`);\n // Display latest version if available\n if (plugin.versions && plugin.versions.length > 0) {\n logger.log(` Latest Version: ${plugin.versions[0].tag}`);\n }\n logger.log(` Registry: ${plugin.registryUrl}`);\n if (plugin.description) {\n logger.log(` Description: ${plugin.description}`);\n }\n logger.log(\"\");\n }\n\n return plugins;\n}\n\n/**\n * Search for plugins in the marketplace.\n */\nexport async function marketplaceSearchCommand(opts: {\n keyword: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin[]> {\n const { keyword, logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(`Searching for \"${keyword}\"...`);\n const plugins = await client.searchPlugins(keyword);\n\n if (plugins.length === 0) {\n logger.log(`\\nNo plugins found matching \"${keyword}\"`);\n return [];\n }\n\n logger.log(`\\nFound ${plugins.length} matching plugin(s):\\n`);\n\n for (const plugin of plugins) {\n logger.log(`${plugin.name} (${plugin.id})`);\n logger.log(` Author: ${plugin.author}`);\n // Display latest version if available\n if (plugin.versions && plugin.versions.length > 0) {\n logger.log(` Latest Version: ${plugin.versions[0].tag}`);\n }\n logger.log(` Registry: ${plugin.registryUrl}`);\n if (plugin.description) {\n logger.log(` Description: ${plugin.description}`);\n }\n logger.log(\"\");\n }\n\n return plugins;\n}\n\n/**\n * Display detailed information about a plugin.\n */\nexport async function marketplaceInfoCommand(opts: {\n pluginId: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<MarketplacePlugin | null> {\n const { pluginId, logger, adapter, marketplaceUrl } = opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n logger.log(`Fetching plugin \"${pluginId}\"...`);\n const plugin = await client.findPluginById(pluginId);\n\n if (!plugin) {\n logger.error(`Plugin \"${pluginId}\" not found in marketplace`);\n return null;\n }\n\n logger.log(\"\\n\" + \"=\".repeat(60));\n logger.log(`Plugin: ${plugin.name}`);\n logger.log(\"=\".repeat(60) + \"\\n\");\n\n logger.log(`ID: ${plugin.id}`);\n logger.log(`Author: ${plugin.author}`);\n if (plugin.authorUrl) {\n logger.log(`Author URL: ${plugin.authorUrl}`);\n }\n logger.log(`Description: ${plugin.description}`);\n logger.log(`\\nRegistry URL: ${plugin.registryUrl}`);\n if (plugin.repository) {\n logger.log(`Repository: ${plugin.repository}`);\n }\n if (plugin.license) {\n logger.log(`License: ${plugin.license}`);\n }\n if (plugin.minObsidianVersion) {\n logger.log(`Min Obsidian Version: ${plugin.minObsidianVersion}`);\n }\n if (plugin.tags && plugin.tags.length > 0) {\n logger.log(`Tags: ${plugin.tags.join(\", \")}`);\n }\n\n // Display available versions\n if (plugin.versions && plugin.versions.length > 0) {\n logger.log(`\\nAvailable Versions (${plugin.versions.length}):`);\n for (const version of plugin.versions.slice(0, 5)) {\n const date = new Date(version.publishedAt).toISOString().split(\"T\")[0];\n logger.log(` - ${version.tag} (${date})`);\n }\n if (plugin.versions.length > 5) {\n logger.log(` ... and ${plugin.versions.length - 5} more`);\n }\n }\n\n logger.log(\"\\n\" + \"=\".repeat(60));\n logger.log(\"Installation:\");\n logger.log(\"=\".repeat(60) + \"\\n\");\n const latestVersion =\n plugin.versions && plugin.versions.length > 0\n ? plugin.versions[0].tag\n : \"latest\";\n logger.log(\n `shard pull ${plugin.registryUrl}:${latestVersion} --output <path>`,\n );\n logger.log(\n `shard marketplace install ${plugin.id} # (coming soon)`,\n );\n\n return plugin;\n}\n\n/**\n * Install a plugin from the marketplace by ID.\n */\nexport async function marketplaceInstallCommand(opts: {\n pluginId: string;\n output: string;\n version?: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n marketplaceUrl?: string;\n}): Promise<{ plugin: MarketplacePlugin; pullResult: unknown }> {\n const { pluginId, output, version, token, logger, adapter, marketplaceUrl } =\n opts;\n\n const client = new MarketplaceClient(adapter, marketplaceUrl);\n\n // Step 1: Find plugin in marketplace\n logger.log(`Looking up plugin \"${pluginId}\" in marketplace...`);\n const plugin = await client.findPluginById(pluginId);\n\n if (!plugin) {\n throw new Error(`Plugin \"${pluginId}\" not found in marketplace`);\n }\n\n // Get latest version if available\n const latestVersion =\n plugin.versions && plugin.versions.length > 0\n ? plugin.versions[0].tag\n : \"latest\";\n\n logger.log(`Found: ${plugin.name} v${latestVersion} by ${plugin.author}`);\n\n // Step 2: Determine version to install\n const versionToInstall = version || latestVersion;\n const repository = `${plugin.registryUrl}:${versionToInstall}`;\n\n logger.log(`Installing from ${repository}...`);\n\n // Step 3: Use pull command to install\n const pullResult = await pullCommand({\n repository,\n output,\n token,\n logger,\n adapter,\n });\n\n logger.success(\n `Successfully installed ${plugin.name} v${versionToInstall} to ${output}`,\n );\n\n return { plugin, pullResult };\n}\n\n/**\n * Query and display all available versions for a plugin registry.\n */\nexport async function marketplaceVersionsCommand(opts: {\n registryUrl: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}): Promise<Array<{ tag: string } & TagMetadata>> {\n const { registryUrl, token, logger, adapter } = opts;\n\n logger.log(`Querying versions for ${registryUrl}...`);\n\n // Query all tags\n const tags = await queryOciTags({ registryUrl, token, adapter });\n\n if (tags.length === 0) {\n logger.log(\"No versions found\");\n return [];\n }\n\n logger.log(`\\nFound ${tags.length} version(s):\\n`);\n\n // Query metadata for each tag\n const versions: Array<{ tag: string } & TagMetadata> = [];\n\n for (const tag of tags) {\n const metadata = await queryTagMetadata({\n registryUrl,\n tag,\n token,\n adapter,\n });\n\n versions.push({ tag, ...metadata });\n\n // Format size\n const sizeKB = (metadata.size / 1024).toFixed(0);\n\n // Format date\n const date = new Date(metadata.publishedAt).toISOString().split(\"T\")[0];\n\n logger.log(`- ${tag} (published ${date}, ${sizeKB} KB)`);\n\n // Show commit SHA if available\n if (metadata.annotations[\"vnd.obsidianmd.plugin.commit\"]) {\n const sha = metadata.annotations[\"vnd.obsidianmd.plugin.commit\"];\n logger.log(` Commit: ${sha.substring(0, 7)}`);\n }\n }\n\n return versions;\n}\n\n/**\n * Update a marketplace entry by re-registering from GHCR.\n */\nexport async function marketplaceUpdateCommand(opts: {\n repository: string;\n token: string;\n logger: Logger;\n adapter: FetchAdapter;\n}): Promise<MarketplaceRegisterResult> {\n const { logger } = opts;\n\n logger.log(\"Updating marketplace entry...\");\n logger.log(\n \"Note: This will overwrite the existing markdown file with fresh metadata from GHCR\\n\",\n );\n\n // Just call the register command - it will overwrite the existing file\n return marketplaceRegisterCommand(opts);\n}\n", "/**\n * Resolve GitHub authentication token from multiple sources.\n * Priority: CLI flag -> GITHUB_TOKEN -> GH_TOKEN -> error\n *\n * @param cliToken - Token provided via CLI flag\n * @returns Resolved token\n * @throws Error if no token is found\n */\nexport function resolveAuthToken(cliToken?: string): string {\n // Priority 1: CLI flag\n if (cliToken) {\n return cliToken;\n }\n\n // Priority 2: GITHUB_TOKEN environment variable (CI/CD)\n const githubToken = process.env.GITHUB_TOKEN;\n if (githubToken) {\n return githubToken;\n }\n\n // Priority 3: GH_TOKEN environment variable (gh CLI)\n const ghToken = process.env.GH_TOKEN;\n if (ghToken) {\n return ghToken;\n }\n\n // No token found\n throw new Error(\n \"GitHub token required. Use --token flag or set GITHUB_TOKEN environment variable\",\n );\n}\n", "/**\n * Simple logger that writes to stderr for progress messages.\n * This allows JSON output on stdout while still showing progress.\n */\nexport class Logger {\n private silent: boolean;\n\n constructor(silent = false) {\n this.silent = silent;\n }\n\n /**\n * Log a message to stderr\n */\n log(message: string): void {\n if (!this.silent) {\n process.stderr.write(message + \"\\n\");\n }\n }\n\n /**\n * Log an error message to stderr\n */\n error(message: string): void {\n if (!this.silent) {\n process.stderr.write(`Error: ${message}\\n`);\n }\n }\n\n /**\n * Log a success message to stderr\n */\n success(message: string): void {\n if (!this.silent) {\n process.stderr.write(`Success: ${message}\\n`);\n }\n }\n}\n", "import type { FetchAdapter } from \"@shard-for-obsidian/lib\";\n\n/**\n * Adapter for Node.js native fetch API.\n * Thin wrapper that passes through to native fetch.\n */\nexport class NodeFetchAdapter implements FetchAdapter {\n async fetch(input: string | Request, init?: RequestInit): Promise<Response> {\n // Use native Node.js fetch\n return fetch(input, init);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["body", "path", "stat", "fs", "path", "fs", "path", "stat"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shard-for-obsidian/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Shard CLI tool to push and pull plugins from GHCR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Andrew Gillis",
|
|
@@ -21,16 +21,6 @@
|
|
|
21
21
|
"access": "public",
|
|
22
22
|
"provenance": true
|
|
23
23
|
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "node esbuild.config.mjs",
|
|
26
|
-
"build:prod": "node esbuild.config.mjs production",
|
|
27
|
-
"clean": "rimraf dist",
|
|
28
|
-
"ts-check": "tsc --noEmit",
|
|
29
|
-
"lint": "eslint .",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"test:watch": "vitest",
|
|
32
|
-
"test:ui": "vitest --ui"
|
|
33
|
-
},
|
|
34
24
|
"keywords": [
|
|
35
25
|
"shard",
|
|
36
26
|
"plugin",
|
|
@@ -44,11 +34,23 @@
|
|
|
44
34
|
"node": ">=18.0.0"
|
|
45
35
|
},
|
|
46
36
|
"dependencies": {
|
|
47
|
-
"
|
|
37
|
+
"gray-matter": "^4.0.3",
|
|
38
|
+
"@shard-for-obsidian/lib": "0.2.4"
|
|
48
39
|
},
|
|
49
40
|
"devDependencies": {
|
|
50
41
|
"@types/node": "^25.2.0",
|
|
51
42
|
"@vitest/ui": "^4.0.18",
|
|
52
43
|
"vitest": "^4.0.18"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"start": "tsx src/index.ts",
|
|
47
|
+
"build": "node esbuild.config.mjs",
|
|
48
|
+
"build:prod": "node esbuild.config.mjs production",
|
|
49
|
+
"clean": "rimraf dist",
|
|
50
|
+
"ts-check": "tsc --noEmit",
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"test:ui": "vitest --ui"
|
|
53
55
|
}
|
|
54
|
-
}
|
|
56
|
+
}
|