@caik.dev/cli 0.1.4 → 0.1.6
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 +201 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1035,7 +1035,7 @@ async function loadConfig() {
|
|
|
1035
1035
|
|
|
1036
1036
|
async function getApiUrl() {
|
|
1037
1037
|
const cfg = await loadConfig();
|
|
1038
|
-
return process.env.CAIK_API_URL || cfg.apiUrl || "https://
|
|
1038
|
+
return process.env.CAIK_API_URL || cfg.apiUrl || "https://caik.dev";
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
1041
|
async function getApiKey() {
|
|
@@ -1349,6 +1349,41 @@ function getDefaultMcpConfig() {
|
|
|
1349
1349
|
}
|
|
1350
1350
|
|
|
1351
1351
|
// src/commands/install.ts
|
|
1352
|
+
function topoSort(nodes, edges) {
|
|
1353
|
+
const nodeMap = new Map(nodes.map((n) => [n.slug, n]));
|
|
1354
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
1355
|
+
const adj = /* @__PURE__ */ new Map();
|
|
1356
|
+
for (const n of nodes) {
|
|
1357
|
+
inDegree.set(n.slug, 0);
|
|
1358
|
+
adj.set(n.slug, []);
|
|
1359
|
+
}
|
|
1360
|
+
for (const edge of edges) {
|
|
1361
|
+
if (!nodeMap.has(edge.from) || !nodeMap.has(edge.to)) continue;
|
|
1362
|
+
adj.get(edge.from).push(edge.to);
|
|
1363
|
+
inDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);
|
|
1364
|
+
}
|
|
1365
|
+
const queue = [];
|
|
1366
|
+
for (const [slug, deg] of inDegree) {
|
|
1367
|
+
if (deg === 0) queue.push(slug);
|
|
1368
|
+
}
|
|
1369
|
+
const result = [];
|
|
1370
|
+
while (queue.length > 0) {
|
|
1371
|
+
const slug = queue.shift();
|
|
1372
|
+
const node = nodeMap.get(slug);
|
|
1373
|
+
if (node) result.push(node);
|
|
1374
|
+
for (const next of adj.get(slug) ?? []) {
|
|
1375
|
+
const newDeg = (inDegree.get(next) ?? 1) - 1;
|
|
1376
|
+
inDegree.set(next, newDeg);
|
|
1377
|
+
if (newDeg === 0) queue.push(next);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
for (const n of nodes) {
|
|
1381
|
+
if (!result.find((r) => r.slug === n.slug)) {
|
|
1382
|
+
result.push(n);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
return result;
|
|
1386
|
+
}
|
|
1352
1387
|
var legacyPlatformMap = {
|
|
1353
1388
|
claude: "claude-code",
|
|
1354
1389
|
cursor: "cursor",
|
|
@@ -1397,6 +1432,132 @@ Examples:
|
|
|
1397
1432
|
outputResult(installInfo, { json: true });
|
|
1398
1433
|
return;
|
|
1399
1434
|
}
|
|
1435
|
+
if (installInfo.artifact.primitive === "composition") {
|
|
1436
|
+
spinner.text = `Resolving stack dependencies for ${installInfo.artifact.name}...`;
|
|
1437
|
+
let graph;
|
|
1438
|
+
try {
|
|
1439
|
+
graph = await client.get(
|
|
1440
|
+
`/stacks/${encodeURIComponent(slug)}/dependencies`
|
|
1441
|
+
);
|
|
1442
|
+
} catch (err) {
|
|
1443
|
+
spinner.stop();
|
|
1444
|
+
console.error(error(`Failed to resolve stack dependencies: ${err instanceof Error ? err.message : String(err)}`));
|
|
1445
|
+
process.exit(1);
|
|
1446
|
+
}
|
|
1447
|
+
spinner.stop();
|
|
1448
|
+
if (graph.nodes.length === 0) {
|
|
1449
|
+
console.log(info("This stack has no components to install."));
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
const ordered = topoSort(graph.nodes, graph.edges);
|
|
1453
|
+
console.log(info(`
|
|
1454
|
+
Stack: ${installInfo.artifact.name}`));
|
|
1455
|
+
if (installInfo.artifact.description) {
|
|
1456
|
+
console.log(info(installInfo.artifact.description));
|
|
1457
|
+
}
|
|
1458
|
+
console.log(info(`
|
|
1459
|
+
Components (${ordered.length}):`));
|
|
1460
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
1461
|
+
const node = ordered[i];
|
|
1462
|
+
console.log(info(` ${i + 1}) ${node.name} (${node.primitive}) \u2014 ${node.slug}`));
|
|
1463
|
+
}
|
|
1464
|
+
console.log();
|
|
1465
|
+
if (!opts.yes && !globalOpts.json) {
|
|
1466
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1467
|
+
const answer = await rl.question(`Install all ${ordered.length} components? (y/N) `);
|
|
1468
|
+
rl.close();
|
|
1469
|
+
if (answer.toLowerCase() !== "y") {
|
|
1470
|
+
console.log(info("Stack installation cancelled."));
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
const installed = [];
|
|
1475
|
+
const failed = [];
|
|
1476
|
+
for (const node of ordered) {
|
|
1477
|
+
const componentSpinner = createSpinner(`Installing ${node.name}...`);
|
|
1478
|
+
if (!globalOpts.json) componentSpinner.start();
|
|
1479
|
+
try {
|
|
1480
|
+
const componentInfo = await client.get(
|
|
1481
|
+
`/install/${encodeURIComponent(node.slug)}`,
|
|
1482
|
+
{ platform: platform2 }
|
|
1483
|
+
);
|
|
1484
|
+
const componentFiles = [];
|
|
1485
|
+
const componentCwd = process.cwd();
|
|
1486
|
+
if (componentInfo.files && componentInfo.files.length > 0) {
|
|
1487
|
+
for (const file of componentInfo.files) {
|
|
1488
|
+
const resolvedPath = resolve(componentCwd, file.path);
|
|
1489
|
+
if (!resolvedPath.startsWith(componentCwd + "/") && resolvedPath !== componentCwd) continue;
|
|
1490
|
+
const dir = dirname3(resolvedPath);
|
|
1491
|
+
if (!existsSync7(dir)) mkdirSync6(dir, { recursive: true });
|
|
1492
|
+
writeFileSync6(resolvedPath, file.content ?? "", "utf-8");
|
|
1493
|
+
componentFiles.push({ resolvedPath, content: file.content ?? "" });
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (componentInfo.installCommand) {
|
|
1497
|
+
execSync3(componentInfo.installCommand, { stdio: "pipe" });
|
|
1498
|
+
}
|
|
1499
|
+
if (componentInfo.postInstallHook) {
|
|
1500
|
+
try {
|
|
1501
|
+
execSync3(componentInfo.postInstallHook, { stdio: "pipe" });
|
|
1502
|
+
} catch {
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
const resolvedPlatform2 = detectedPlatform ?? "generic";
|
|
1506
|
+
upsertRegistryEntry({
|
|
1507
|
+
slug: node.slug,
|
|
1508
|
+
version: componentInfo.artifact.version ?? "0.0.0",
|
|
1509
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1510
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1511
|
+
platform: resolvedPlatform2,
|
|
1512
|
+
files: componentFiles.map((f) => f.resolvedPath),
|
|
1513
|
+
artifactType: componentInfo.artifact.primitive ?? "unknown"
|
|
1514
|
+
});
|
|
1515
|
+
const adapter2 = getPlatformAdapter(resolvedPlatform2);
|
|
1516
|
+
if (componentInfo.artifact.primitive === "connector") {
|
|
1517
|
+
try {
|
|
1518
|
+
await adapter2.registerMcp(getDefaultMcpConfig());
|
|
1519
|
+
} catch {
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
if ((componentInfo.artifact.primitive === "executable" || componentInfo.artifact.primitive === "composition") && adapter2.installSkill) {
|
|
1523
|
+
try {
|
|
1524
|
+
const content = componentFiles.map((f) => f.content).join("\n");
|
|
1525
|
+
await adapter2.installSkill(node.slug, content);
|
|
1526
|
+
} catch {
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
client.post("/telemetry/install", {
|
|
1530
|
+
artifactSlug: node.slug,
|
|
1531
|
+
platform: platform2,
|
|
1532
|
+
success: true
|
|
1533
|
+
}).catch(() => {
|
|
1534
|
+
});
|
|
1535
|
+
componentSpinner.stop();
|
|
1536
|
+
console.log(success(` Installed ${node.name}`));
|
|
1537
|
+
installed.push(node.slug);
|
|
1538
|
+
} catch (err) {
|
|
1539
|
+
componentSpinner.stop();
|
|
1540
|
+
console.log(error(` Failed to install ${node.name}: ${err instanceof Error ? err.message : String(err)}`));
|
|
1541
|
+
failed.push(node.slug);
|
|
1542
|
+
client.post("/telemetry/install", {
|
|
1543
|
+
artifactSlug: node.slug,
|
|
1544
|
+
platform: platform2,
|
|
1545
|
+
success: false,
|
|
1546
|
+
errorMessage: err instanceof Error ? err.message : String(err)
|
|
1547
|
+
}).catch(() => {
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
console.log();
|
|
1552
|
+
console.log(success(`Stack "${installInfo.artifact.name}" \u2014 ${installed.length}/${ordered.length} components installed`));
|
|
1553
|
+
if (failed.length > 0) {
|
|
1554
|
+
console.log(error(`Failed: ${failed.join(", ")}`));
|
|
1555
|
+
}
|
|
1556
|
+
if (globalOpts.json) {
|
|
1557
|
+
outputResult({ stack: slug, installed, failed, total: ordered.length }, { json: true });
|
|
1558
|
+
}
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1400
1561
|
spinner.text = `Installing ${installInfo.artifact.name}...`;
|
|
1401
1562
|
const skipConfirm = Boolean(opts.yes);
|
|
1402
1563
|
const cwd = process.cwd();
|
|
@@ -1646,10 +1807,10 @@ async function authenticate(apiUrl, port = 0) {
|
|
|
1646
1807
|
|
|
1647
1808
|
// src/commands/init.ts
|
|
1648
1809
|
function registerInitCommand(program2) {
|
|
1649
|
-
program2.command("init").description("Configure the CAIK CLI").option("--auth", "
|
|
1810
|
+
program2.command("init").description("Configure the CAIK CLI").option("--no-auth", "Skip authentication, only configure API URL").addHelpText("after", `
|
|
1650
1811
|
Examples:
|
|
1651
|
-
caik init
|
|
1652
|
-
caik init --auth`).action(async (opts) => {
|
|
1812
|
+
caik init # opens browser for sign-in
|
|
1813
|
+
caik init --no-auth # configure API URL only`).action(async (opts) => {
|
|
1653
1814
|
const config = readConfig();
|
|
1654
1815
|
if (opts.auth) {
|
|
1655
1816
|
console.log(info("Opening browser for authentication..."));
|
|
@@ -1657,7 +1818,15 @@ Examples:
|
|
|
1657
1818
|
const apiKey = await authenticate(config.apiUrl);
|
|
1658
1819
|
config.apiKey = apiKey;
|
|
1659
1820
|
writeConfig(config);
|
|
1660
|
-
|
|
1821
|
+
const client = new CaikApiClient({ apiUrl: config.apiUrl, apiKey });
|
|
1822
|
+
try {
|
|
1823
|
+
const karma = await client.get("/me/karma");
|
|
1824
|
+
const name = karma.displayName ?? karma.handle;
|
|
1825
|
+
console.log(success(`Authenticated as ${name}`));
|
|
1826
|
+
} catch {
|
|
1827
|
+
console.log(success("Authenticated and saved API key"));
|
|
1828
|
+
console.log(info("Could not verify account details, but your key was saved."));
|
|
1829
|
+
}
|
|
1661
1830
|
console.log(info(`Config saved to ~/.caik/config.json`));
|
|
1662
1831
|
} catch (err) {
|
|
1663
1832
|
console.log(error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -1824,7 +1993,6 @@ function registerStatusCommand(program2) {
|
|
|
1824
1993
|
}
|
|
1825
1994
|
|
|
1826
1995
|
// src/commands/stats.ts
|
|
1827
|
-
var PRIMITIVES = ["executable", "knowledge", "connector", "composition", "reference"];
|
|
1828
1996
|
function registerStatsCommand(program2) {
|
|
1829
1997
|
program2.command("stats").description("Show CAIK ecosystem statistics").addHelpText("after", `
|
|
1830
1998
|
Examples:
|
|
@@ -1835,46 +2003,42 @@ Examples:
|
|
|
1835
2003
|
const client = new CaikApiClient({ apiUrl, apiKey, verbose: globalOpts.verbose });
|
|
1836
2004
|
const spinner = createSpinner("Fetching stats...");
|
|
1837
2005
|
if (!globalOpts.json) spinner.start();
|
|
1838
|
-
const
|
|
1839
|
-
let totalInstalls = 0;
|
|
1840
|
-
await Promise.all(
|
|
1841
|
-
PRIMITIVES.map(async (primitive) => {
|
|
1842
|
-
try {
|
|
1843
|
-
const result = await client.get(
|
|
1844
|
-
`/artifacts/by-primitive/${primitive}`,
|
|
1845
|
-
{ limit: "1", offset: "0" }
|
|
1846
|
-
);
|
|
1847
|
-
counts[primitive] = result.total;
|
|
1848
|
-
} catch {
|
|
1849
|
-
counts[primitive] = 0;
|
|
1850
|
-
}
|
|
1851
|
-
})
|
|
1852
|
-
);
|
|
1853
|
-
const totalArtifacts = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
2006
|
+
const stats = await client.get("/stats");
|
|
1854
2007
|
if (!globalOpts.json) spinner.stop();
|
|
1855
2008
|
if (globalOpts.json) {
|
|
1856
|
-
outputResult(
|
|
2009
|
+
outputResult(stats, { json: true });
|
|
1857
2010
|
return;
|
|
1858
2011
|
}
|
|
1859
2012
|
console.log(heading("CAIK Registry Stats"));
|
|
1860
2013
|
console.log("\u2550".repeat(40));
|
|
1861
|
-
console.log(`Total artifacts: ${formatNumber(totalArtifacts)}`);
|
|
2014
|
+
console.log(`Total artifacts: ${formatNumber(stats.totalArtifacts)}`);
|
|
2015
|
+
console.log(`Total installs: ${formatNumber(stats.totalInstalls)}`);
|
|
2016
|
+
console.log(`Contributors: ${formatNumber(stats.activeContributors)}`);
|
|
2017
|
+
console.log(`New (7 days): ${formatNumber(stats.recentPublications)}`);
|
|
1862
2018
|
console.log("");
|
|
1863
2019
|
console.log("By primitive:");
|
|
1864
|
-
for (const [primitive, count] of Object.entries(
|
|
2020
|
+
for (const [primitive, count] of Object.entries(stats.byPrimitive)) {
|
|
1865
2021
|
const label = primitive.charAt(0).toUpperCase() + primitive.slice(1);
|
|
1866
2022
|
console.log(` ${label.padEnd(16)} ${formatNumber(count)}`);
|
|
1867
2023
|
}
|
|
2024
|
+
if (stats.topTags && stats.topTags.length > 0) {
|
|
2025
|
+
console.log("");
|
|
2026
|
+
console.log("Top tags:");
|
|
2027
|
+
for (const { tag, count } of stats.topTags) {
|
|
2028
|
+
console.log(` ${dim(tag.padEnd(20))} ${formatNumber(count)}`);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
1868
2031
|
});
|
|
1869
2032
|
}
|
|
1870
2033
|
|
|
1871
2034
|
// src/commands/publish.ts
|
|
1872
2035
|
import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
|
|
1873
2036
|
function registerPublishCommand(program2) {
|
|
1874
|
-
program2.command("publish [path]").description("Publish an artifact to the CAIK registry").requiredOption("--name <name>", "Artifact name").requiredOption("--description <desc>", "Artifact description (min 20 chars)").option("--slug <slug>", "Custom slug (auto-generated from name if omitted)").option("--primitive <type>", "Primitive type", "executable").option("--platform <platform>", "Target platform(s) (comma-separated)", "claude").option("--tag <tag>", "Add a tag (can be repeated)", (val, prev) => [...prev, val], []).option("--source-url <url>", "Source code URL").addHelpText("after", `
|
|
2037
|
+
program2.command("publish [path]").description("Publish an artifact to the CAIK registry").requiredOption("--name <name>", "Artifact name").requiredOption("--description <desc>", "Artifact description (min 20 chars)").option("--slug <slug>", "Custom slug (auto-generated from name if omitted)").option("--primitive <type>", "Primitive type", "executable").option("--platform <platform>", "Target platform(s) (comma-separated)", "claude").option("--tag <tag>", "Add a tag (can be repeated)", (val, prev) => [...prev, val], []).option("--source-url <url>", "Source code URL").option("--manifest <json>", "Manifest JSON (e.g. entrypoint for executables, transport for MCP servers)").addHelpText("after", `
|
|
1875
2038
|
Examples:
|
|
1876
2039
|
caik publish ./my-skill --name "My Skill" --description "A useful skill for Claude" --tag productivity
|
|
1877
|
-
caik publish --name "Auth Helper" --description "Authentication middleware helper" --primitive knowledge
|
|
2040
|
+
caik publish --name "Auth Helper" --description "Authentication middleware helper" --primitive knowledge
|
|
2041
|
+
caik publish ./server.js --name "My MCP" --description "Custom MCP server for data" --primitive connector --manifest '{"type":"mcp-server","command":"node","args":["server.js"]}'`).action(async (path, opts) => {
|
|
1878
2042
|
const globalOpts = program2.opts();
|
|
1879
2043
|
const { apiUrl, apiKey } = resolveConfig(globalOpts);
|
|
1880
2044
|
if (!apiKey) {
|
|
@@ -1890,6 +2054,14 @@ Examples:
|
|
|
1890
2054
|
}
|
|
1891
2055
|
const tags = opts.tag.length > 0 ? opts.tag : ["general"];
|
|
1892
2056
|
const platforms = opts.platform.split(",").map((p) => p.trim());
|
|
2057
|
+
let manifest;
|
|
2058
|
+
if (opts.manifest) {
|
|
2059
|
+
try {
|
|
2060
|
+
manifest = JSON.parse(opts.manifest);
|
|
2061
|
+
} catch {
|
|
2062
|
+
throw new CaikError("Invalid --manifest JSON.", `Provide valid JSON, e.g. --manifest '{"type":"mcp-server","command":"npx"}'`);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
1893
2065
|
const spinner = createSpinner("Publishing artifact...");
|
|
1894
2066
|
if (!globalOpts.json) spinner.start();
|
|
1895
2067
|
const result = await client.post("/artifacts", {
|
|
@@ -1900,7 +2072,8 @@ Examples:
|
|
|
1900
2072
|
platforms,
|
|
1901
2073
|
tags,
|
|
1902
2074
|
content,
|
|
1903
|
-
sourceUrl: opts.sourceUrl
|
|
2075
|
+
sourceUrl: opts.sourceUrl,
|
|
2076
|
+
...manifest ? { manifest } : {}
|
|
1904
2077
|
});
|
|
1905
2078
|
if (!globalOpts.json) spinner.stop();
|
|
1906
2079
|
if (globalOpts.json) {
|