@caik.dev/cli 0.1.3 → 0.1.5

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 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -133,7 +133,7 @@ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from "f
133
133
  import { join } from "path";
134
134
  import { homedir } from "os";
135
135
  var DEFAULT_CONFIG = {
136
- apiUrl: "https://caik.dev",
136
+ apiUrl: "https://www.caik.dev",
137
137
  defaultLimit: 10,
138
138
  version: 1
139
139
  };
@@ -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();
@@ -1619,7 +1780,8 @@ async function authenticate(apiUrl, port = 0) {
1619
1780
  server.listen(port, async () => {
1620
1781
  const actualPort = server.address().port;
1621
1782
  const callbackUrl = `http://localhost:${actualPort}/callback`;
1622
- const loginUrl = `${apiUrl}/auth/signin?redirect=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
1783
+ const cliCallbackUrl = `${apiUrl}/api/auth/cli-callback?redirect=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
1784
+ const loginUrl = `${apiUrl}/cli-auth?continue=${encodeURIComponent(cliCallbackUrl)}`;
1623
1785
  console.log(info(`Listening on http://localhost:${actualPort} for callback...`));
1624
1786
  const opened = await openBrowser(loginUrl);
1625
1787
  if (opened) {
@@ -1656,7 +1818,15 @@ Examples:
1656
1818
  const apiKey = await authenticate(config.apiUrl);
1657
1819
  config.apiKey = apiKey;
1658
1820
  writeConfig(config);
1659
- 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
+ }
1660
1830
  console.log(info(`Config saved to ~/.caik/config.json`));
1661
1831
  } catch (err) {
1662
1832
  console.log(error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`));
@@ -1823,7 +1993,6 @@ function registerStatusCommand(program2) {
1823
1993
  }
1824
1994
 
1825
1995
  // src/commands/stats.ts
1826
- var PRIMITIVES = ["executable", "knowledge", "connector", "composition", "reference"];
1827
1996
  function registerStatsCommand(program2) {
1828
1997
  program2.command("stats").description("Show CAIK ecosystem statistics").addHelpText("after", `
1829
1998
  Examples:
@@ -1834,46 +2003,42 @@ Examples:
1834
2003
  const client = new CaikApiClient({ apiUrl, apiKey, verbose: globalOpts.verbose });
1835
2004
  const spinner = createSpinner("Fetching stats...");
1836
2005
  if (!globalOpts.json) spinner.start();
1837
- const counts = {};
1838
- let totalInstalls = 0;
1839
- await Promise.all(
1840
- PRIMITIVES.map(async (primitive) => {
1841
- try {
1842
- const result = await client.get(
1843
- `/artifacts/by-primitive/${primitive}`,
1844
- { limit: "1", offset: "0" }
1845
- );
1846
- counts[primitive] = result.total;
1847
- } catch {
1848
- counts[primitive] = 0;
1849
- }
1850
- })
1851
- );
1852
- const totalArtifacts = Object.values(counts).reduce((a, b) => a + b, 0);
2006
+ const stats = await client.get("/stats");
1853
2007
  if (!globalOpts.json) spinner.stop();
1854
2008
  if (globalOpts.json) {
1855
- outputResult({ totalArtifacts, byPrimitive: counts, totalInstalls }, { json: true });
2009
+ outputResult(stats, { json: true });
1856
2010
  return;
1857
2011
  }
1858
2012
  console.log(heading("CAIK Registry Stats"));
1859
2013
  console.log("\u2550".repeat(40));
1860
- 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)}`);
1861
2018
  console.log("");
1862
2019
  console.log("By primitive:");
1863
- for (const [primitive, count] of Object.entries(counts)) {
2020
+ for (const [primitive, count] of Object.entries(stats.byPrimitive)) {
1864
2021
  const label = primitive.charAt(0).toUpperCase() + primitive.slice(1);
1865
2022
  console.log(` ${label.padEnd(16)} ${formatNumber(count)}`);
1866
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
+ }
1867
2031
  });
1868
2032
  }
1869
2033
 
1870
2034
  // src/commands/publish.ts
1871
2035
  import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
1872
2036
  function registerPublishCommand(program2) {
1873
- 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", `
1874
2038
  Examples:
1875
2039
  caik publish ./my-skill --name "My Skill" --description "A useful skill for Claude" --tag productivity
1876
- 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) => {
1877
2042
  const globalOpts = program2.opts();
1878
2043
  const { apiUrl, apiKey } = resolveConfig(globalOpts);
1879
2044
  if (!apiKey) {
@@ -1889,6 +2054,14 @@ Examples:
1889
2054
  }
1890
2055
  const tags = opts.tag.length > 0 ? opts.tag : ["general"];
1891
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
+ }
1892
2065
  const spinner = createSpinner("Publishing artifact...");
1893
2066
  if (!globalOpts.json) spinner.start();
1894
2067
  const result = await client.post("/artifacts", {
@@ -1899,7 +2072,8 @@ Examples:
1899
2072
  platforms,
1900
2073
  tags,
1901
2074
  content,
1902
- sourceUrl: opts.sourceUrl
2075
+ sourceUrl: opts.sourceUrl,
2076
+ ...manifest ? { manifest } : {}
1903
2077
  });
1904
2078
  if (!globalOpts.json) spinner.stop();
1905
2079
  if (globalOpts.json) {
@@ -3187,7 +3361,7 @@ try {
3187
3361
  version = pkg.version;
3188
3362
  } catch {
3189
3363
  }
3190
- var program = new Command().name("caik").description("CAIK \u2014 Collective AI Knowledge CLI\nSearch, install, and publish AI artifacts from your terminal.").version(version, "-v, --version").option("--api-url <url>", "API base URL (default: https://caik.dev)").option("--api-key <key>", "API key for authentication").option("--verbose", "Show detailed output including API calls").option("--json", "Output raw JSON (for scripting)");
3364
+ var program = new Command().name("caik").description("CAIK \u2014 Collective AI Knowledge CLI\nSearch, install, and publish AI artifacts from your terminal.").version(version, "-v, --version").option("--api-url <url>", "API base URL (default: https://www.caik.dev)").option("--api-key <key>", "API key for authentication").option("--verbose", "Show detailed output including API calls").option("--json", "Output raw JSON (for scripting)");
3191
3365
  registerSearchCommand(program);
3192
3366
  registerInstallCommand(program);
3193
3367
  registerInitCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caik.dev/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "CAIK CLI — Search, install, and publish AI artifacts from your terminal",
6
6
  "keywords": [