@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.
Files changed (2) hide show
  1. package/dist/index.js +201 -28
  2. 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://www.caik.dev";
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", "Set up authentication (opens browser)").addHelpText("after", `
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
- console.log(success("Authenticated and saved API key"));
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 counts = {};
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({ totalArtifacts, byPrimitive: counts, totalInstalls }, { json: true });
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(counts)) {
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`).action(async (path, opts) => {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caik.dev/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "CAIK CLI — Search, install, and publish AI artifacts from your terminal",
6
6
  "keywords": [