integrationsdotsh 0.0.1-beta.0

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 (3) hide show
  1. package/README.md +38 -0
  2. package/dist/cli.js +145 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # integrations
2
+
3
+ The CLI for [integrations.sh](https://integrations.sh) — the registry of agent
4
+ integrations. Search the catalog and read everything a domain exposes
5
+ (MCP · REST · GraphQL · CLI) without leaving the terminal.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm i -g integrationsdotsh # command is `integrations`
11
+ # or
12
+ brew install integrations # (planned)
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```sh
18
+ integrations search slack # search the catalog
19
+ integrations stripe.com # everything a domain exposes
20
+ integrations notion # fuzzy domain match
21
+
22
+ integrations help
23
+ integrations --version
24
+ ```
25
+
26
+ Point at a different registry host (e.g. a local dev server):
27
+
28
+ ```sh
29
+ INTEGRATIONS_BASE=http://localhost:4321 integrations search slack
30
+ ```
31
+
32
+ ## Planned
33
+
34
+ - `integrations auth <domain>` — how to authenticate (credential primitives)
35
+ - `integrations probe <domain>` — live-read a domain's `.well-known/` surface
36
+ (`ai-catalog.json`, `api-catalog`, `ai-plugin.json`, `llms.txt`)
37
+ - `integrations add <domain>` — hand off to Executor
38
+ - `--json` machine output
package/dist/cli.js ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ var VERSION = "0.0.1-beta.0";
5
+ var BASE = (process.env.INTEGRATIONS_BASE ?? "https://integrations.sh").replace(/\/$/, "");
6
+ var KIND_ORDER = ["mcp", "openapi", "graphql", "cli"];
7
+ var TAG = { mcp: "mcp", openapi: "rest", graphql: "graphql", cli: "cli" };
8
+ var SECTION = {
9
+ mcp: "MCP SERVERS",
10
+ openapi: "REST · OPENAPI",
11
+ graphql: "GRAPHQL",
12
+ cli: "CLI"
13
+ };
14
+ var useColor = process.stdout.isTTY && !process.env.NO_COLOR;
15
+ var dim = (s) => useColor ? `\x1B[2m${s}\x1B[0m` : s;
16
+ var bold = (s) => useColor ? `\x1B[1m${s}\x1B[0m` : s;
17
+ function fail(msg) {
18
+ process.stderr.write(`integrations: ${msg}
19
+ `);
20
+ process.exit(1);
21
+ }
22
+ async function loadRegistry() {
23
+ let res;
24
+ try {
25
+ res = await fetch(`${BASE}/api.json`, { headers: { accept: "application/json" } });
26
+ } catch {
27
+ return fail(`could not reach ${BASE}/api.json`);
28
+ }
29
+ if (!res.ok)
30
+ return fail(`${BASE}/api.json returned ${res.status}`);
31
+ return await res.json();
32
+ }
33
+ function groupByDomain(records) {
34
+ const groups = new Map;
35
+ for (const r of records) {
36
+ const d = r.domain || r.slug;
37
+ if (!d)
38
+ continue;
39
+ let g = groups.get(d);
40
+ if (!g) {
41
+ g = { domain: d, total: 0, kinds: new Set, records: [], pop: 0 };
42
+ groups.set(d, g);
43
+ }
44
+ g.total++;
45
+ g.kinds.add(r.kind);
46
+ g.records.push(r);
47
+ g.pop = Math.max(g.pop, r.popularity ?? 0);
48
+ }
49
+ return groups;
50
+ }
51
+ var formatsOf = (g) => KIND_ORDER.filter((k) => g.kinds.has(k)).map((k) => TAG[k]).join(" · ");
52
+ var byPop = (a, b) => b.pop - a.pop || b.total - a.total || a.domain.localeCompare(b.domain);
53
+ var clip = (s, n) => s.replace(/\s+/g, " ").slice(0, n);
54
+ async function cmdSearch(query) {
55
+ if (!query)
56
+ fail("usage: integrations search <query>");
57
+ const q = query.toLowerCase();
58
+ const records = await loadRegistry();
59
+ const groups = [...groupByDomain(records).values()].filter((g) => g.domain.toLowerCase().includes(q) || g.records.some((r) => r.name.toLowerCase().includes(q) || (r.description ?? "").toLowerCase().includes(q))).sort(byPop);
60
+ if (groups.length === 0) {
61
+ process.stdout.write(dim(`no matches
62
+ `));
63
+ return;
64
+ }
65
+ const LIMIT = 30;
66
+ const shown = groups.slice(0, LIMIT);
67
+ const pad = Math.min(28, Math.max(...shown.map((g) => g.domain.length)));
68
+ for (const g of shown) {
69
+ const desc = g.records.find((r) => r.description)?.description ?? "";
70
+ process.stdout.write(`${bold(g.domain.padEnd(pad))} ${dim(formatsOf(g).padEnd(22))} ${dim(clip(desc, 64))}
71
+ `);
72
+ }
73
+ if (groups.length > LIMIT)
74
+ process.stdout.write(dim(`
75
+ … ${groups.length - LIMIT} more — narrow your query
76
+ `));
77
+ }
78
+ async function cmdDomain(term) {
79
+ const t = term.toLowerCase();
80
+ const records = await loadRegistry();
81
+ const groups = groupByDomain(records);
82
+ let g = groups.get(term);
83
+ if (!g) {
84
+ const cands = [...groups.values()].filter((x) => x.domain.toLowerCase().includes(t)).sort(byPop);
85
+ if (cands.length === 0)
86
+ fail(`no domain matching "${term}"`);
87
+ if (cands.length > 1 && cands[0].domain.toLowerCase() !== t) {
88
+ process.stdout.write(dim(`did you mean:
89
+ `));
90
+ for (const c of cands.slice(0, 8))
91
+ process.stdout.write(` ${c.domain} ${dim(formatsOf(c))}
92
+ `);
93
+ return;
94
+ }
95
+ g = cands[0];
96
+ }
97
+ process.stdout.write(`
98
+ ${bold(g.domain)} ${dim(`— ${g.total} integration${g.total === 1 ? "" : "s"}`)}
99
+ `);
100
+ for (const kind of KIND_ORDER) {
101
+ const items = g.records.filter((r) => r.kind === kind).sort((a, b) => (b.popularity ?? 0) - (a.popularity ?? 0) || a.name.localeCompare(b.name));
102
+ if (items.length === 0)
103
+ continue;
104
+ process.stdout.write(`
105
+ ${dim(SECTION[kind])}
106
+ `);
107
+ for (const r of items) {
108
+ const desc = r.description ? dim(" " + clip(r.description, 60)) : "";
109
+ process.stdout.write(` ${r.name}${desc} ${dim(`${BASE}/${r.kind}/${r.slug}/`)}
110
+ `);
111
+ }
112
+ }
113
+ process.stdout.write(`
114
+ `);
115
+ }
116
+ function help() {
117
+ process.stdout.write(`integrations — the registry of agent integrations (integrations.sh)
118
+
119
+ usage:
120
+ integrations search <query> search the catalog
121
+ integrations <domain> show everything a domain exposes
122
+ integrations help show this help
123
+ integrations --version print version
124
+
125
+ examples:
126
+ integrations search slack
127
+ integrations stripe.com
128
+ integrations notion
129
+
130
+ env:
131
+ INTEGRATIONS_BASE registry host (default https://integrations.sh)
132
+ `);
133
+ }
134
+ async function main() {
135
+ const [, , cmd, ...rest] = process.argv;
136
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h")
137
+ return help();
138
+ if (cmd === "--version" || cmd === "-v")
139
+ return void process.stdout.write(`${VERSION}
140
+ `);
141
+ if (cmd === "search")
142
+ return cmdSearch(rest.join(" "));
143
+ return cmdDomain(cmd);
144
+ }
145
+ main().catch((e) => fail(e?.message ?? String(e)));
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "integrationsdotsh",
3
+ "version": "0.0.1-beta.0",
4
+ "description": "CLI for integrations.sh — search the registry of agent integrations and read a domain's surface.",
5
+ "type": "module",
6
+ "bin": {
7
+ "integrations": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "scripts": {
16
+ "build": "bun build src/cli.ts --target node --outdir dist && chmod +x dist/cli.js",
17
+ "dev": "bun run src/cli.ts",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "bun run build"
20
+ },
21
+ "keywords": [
22
+ "integrations",
23
+ "mcp",
24
+ "openapi",
25
+ "graphql",
26
+ "agents",
27
+ "registry",
28
+ "cli"
29
+ ],
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "typescript": "^5.6.0"
34
+ }
35
+ }