@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.
- package/dist/index.js +201 -27
- 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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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);
|