cograph 0.1.0 → 0.1.2

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.
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import { homedir } from "os";
5
+ import { join } from "path";
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
7
+ function configDir() {
8
+ return join(homedir(), ".cograph");
9
+ }
10
+ function configPath() {
11
+ return join(configDir(), "config.json");
12
+ }
13
+ function readConfig() {
14
+ const path = configPath();
15
+ if (!existsSync(path)) return {};
16
+ try {
17
+ const raw = readFileSync(path, "utf-8");
18
+ const parsed = JSON.parse(raw);
19
+ if (parsed && typeof parsed === "object") {
20
+ return parsed;
21
+ }
22
+ } catch {
23
+ }
24
+ return {};
25
+ }
26
+ function writeConfig(patch) {
27
+ const dir = configDir();
28
+ if (!existsSync(dir)) {
29
+ mkdirSync(dir, { recursive: true, mode: 448 });
30
+ }
31
+ const merged = { ...readConfig(), ...patch };
32
+ const path = configPath();
33
+ writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf-8");
34
+ try {
35
+ chmodSync(path, 384);
36
+ } catch {
37
+ }
38
+ }
39
+ function configPathForDisplay() {
40
+ return configPath();
41
+ }
42
+
43
+ export {
44
+ readConfig,
45
+ writeConfig,
46
+ configPathForDisplay
47
+ };
48
+ //# sourceMappingURL=chunk-7VVBEUZQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from \"node:fs\";\n\nexport interface CographConfig {\n apiKey?: string;\n apiUrl?: string;\n tenant?: string;\n email?: string;\n}\n\nfunction configDir(): string {\n return join(homedir(), \".cograph\");\n}\n\nfunction configPath(): string {\n return join(configDir(), \"config.json\");\n}\n\n/**\n * Read `~/.cograph/config.json`. Returns an empty object if the file is\n * missing or unreadable — callers should treat fields as optional.\n */\nexport function readConfig(): CographConfig {\n const path = configPath();\n if (!existsSync(path)) return {};\n try {\n const raw = readFileSync(path, \"utf-8\");\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\") {\n return parsed as CographConfig;\n }\n } catch {\n // Corrupt or unreadable; behave as if absent so a fresh login can rewrite.\n }\n return {};\n}\n\n/**\n * Write `~/.cograph/config.json` with `chmod 600`. Creates the directory if\n * needed. Merges with the existing config so callers can update one field\n * without clobbering the others.\n */\nexport function writeConfig(patch: CographConfig): void {\n const dir = configDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n const merged = { ...readConfig(), ...patch };\n const path = configPath();\n writeFileSync(path, JSON.stringify(merged, null, 2) + \"\\n\", \"utf-8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n // best-effort; some filesystems (e.g., FAT) don't honor chmod\n }\n}\n\nexport function configPathForDisplay(): string {\n return configPath();\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,YAAY,WAAW,cAAc,eAAe,iBAAiB;AAS9E,SAAS,YAAoB;AAC3B,SAAO,KAAK,QAAQ,GAAG,UAAU;AACnC;AAEA,SAAS,aAAqB;AAC5B,SAAO,KAAK,UAAU,GAAG,aAAa;AACxC;AAMO,SAAS,aAA4B;AAC1C,QAAM,OAAO,WAAW;AACxB,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,OAAO;AACtC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAOO,SAAS,YAAY,OAA4B;AACtD,QAAM,MAAM,UAAU;AACtB,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AACA,QAAM,SAAS,EAAE,GAAG,WAAW,GAAG,GAAG,MAAM;AAC3C,QAAM,OAAO,WAAW;AACxB,gBAAc,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AACnE,MAAI;AACF,cAAU,MAAM,GAAK;AAAA,EACvB,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,uBAA+B;AAC7C,SAAO,WAAW;AACpB;","names":[]}
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readConfig
4
+ } from "./chunk-7VVBEUZQ.js";
5
+
6
+ // src/client.ts
7
+ import { existsSync, readFileSync, statSync } from "fs";
8
+ import { extname } from "path";
9
+ var CographError = class extends Error {
10
+ status;
11
+ body;
12
+ constructor(message, opts) {
13
+ super(message);
14
+ this.name = "CographError";
15
+ this.status = opts?.status;
16
+ this.body = opts?.body;
17
+ }
18
+ };
19
+ function envVar(name, fallback) {
20
+ return process.env[`COGRAPH_${name}`] || process.env[`OMNIX_${name}`] || fallback;
21
+ }
22
+ var EXT_FORMAT = {
23
+ ".csv": "csv",
24
+ ".json": "json",
25
+ ".jsonl": "json",
26
+ ".txt": "text"
27
+ };
28
+ function parseCsv(content) {
29
+ const rows = [];
30
+ let cur = [];
31
+ let field = "";
32
+ let inQuotes = false;
33
+ for (let i = 0; i < content.length; i++) {
34
+ const ch = content[i];
35
+ if (inQuotes) {
36
+ if (ch === '"') {
37
+ if (content[i + 1] === '"') {
38
+ field += '"';
39
+ i++;
40
+ } else {
41
+ inQuotes = false;
42
+ }
43
+ } else {
44
+ field += ch;
45
+ }
46
+ } else {
47
+ if (ch === '"') {
48
+ inQuotes = true;
49
+ } else if (ch === ",") {
50
+ cur.push(field);
51
+ field = "";
52
+ } else if (ch === "\n") {
53
+ cur.push(field);
54
+ rows.push(cur);
55
+ cur = [];
56
+ field = "";
57
+ } else if (ch === "\r") {
58
+ if (content[i + 1] !== "\n") {
59
+ cur.push(field);
60
+ rows.push(cur);
61
+ cur = [];
62
+ field = "";
63
+ }
64
+ } else {
65
+ field += ch;
66
+ }
67
+ }
68
+ }
69
+ if (field.length > 0 || cur.length > 0) {
70
+ cur.push(field);
71
+ rows.push(cur);
72
+ }
73
+ if (rows.length === 0) return [];
74
+ const headers = rows[0].map((h) => h.trim());
75
+ const out = [];
76
+ for (let r = 1; r < rows.length; r++) {
77
+ const row = rows[r];
78
+ if (row.length === 1 && row[0] === "") continue;
79
+ const obj = {};
80
+ for (let c = 0; c < headers.length; c++) {
81
+ obj[headers[c]] = row[c] ?? "";
82
+ }
83
+ out.push(obj);
84
+ }
85
+ return out;
86
+ }
87
+ var Client = class {
88
+ apiKey;
89
+ baseUrl;
90
+ tenant;
91
+ constructor(opts = {}) {
92
+ const cfg = readConfig();
93
+ this.apiKey = opts.apiKey ?? envVar("API_KEY") ?? cfg.apiKey;
94
+ const url = opts.baseUrl ?? envVar("API_URL") ?? cfg.apiUrl ?? "https://api.cograph.cloud";
95
+ this.baseUrl = url.replace(/\/+$/, "");
96
+ this.tenant = opts.tenant ?? envVar("TENANT") ?? cfg.tenant ?? "demo-tenant";
97
+ }
98
+ headers() {
99
+ const h = { "Content-Type": "application/json" };
100
+ if (this.apiKey) h["X-API-Key"] = this.apiKey;
101
+ return h;
102
+ }
103
+ base() {
104
+ return `${this.baseUrl}/graphs/${this.tenant}`;
105
+ }
106
+ async request(method, url, body, timeoutMs = 12e4) {
107
+ const controller = new AbortController();
108
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
109
+ let res;
110
+ try {
111
+ res = await fetch(url, {
112
+ method,
113
+ headers: this.headers(),
114
+ body: body === void 0 ? void 0 : JSON.stringify(body),
115
+ signal: controller.signal
116
+ });
117
+ } catch (err) {
118
+ clearTimeout(timer);
119
+ if (err instanceof Error && err.name === "AbortError") {
120
+ throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);
121
+ }
122
+ throw new CographError(
123
+ `Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`
124
+ );
125
+ }
126
+ clearTimeout(timer);
127
+ if (!res.ok) {
128
+ let text2 = "";
129
+ try {
130
+ text2 = await res.text();
131
+ } catch {
132
+ }
133
+ throw new CographError(`HTTP ${res.status}: ${text2}`, {
134
+ status: res.status,
135
+ body: text2
136
+ });
137
+ }
138
+ if (res.status === 204) return void 0;
139
+ const ct = res.headers.get("content-type") ?? "";
140
+ if (ct.includes("application/json")) {
141
+ return await res.json();
142
+ }
143
+ const text = await res.text();
144
+ try {
145
+ return JSON.parse(text);
146
+ } catch {
147
+ return text;
148
+ }
149
+ }
150
+ /**
151
+ * Ingest a file path or raw text into a knowledge graph.
152
+ *
153
+ * If `pathOrText` points to an existing file, its contents are read and the
154
+ * format is inferred from the extension (.csv, .json, .txt) unless
155
+ * `contentType` is given. CSV files use the two-step schema-inference + row
156
+ * mapping flow.
157
+ */
158
+ async ingest(pathOrText, opts = {}) {
159
+ let content;
160
+ let fmt;
161
+ let isFile = false;
162
+ try {
163
+ isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();
164
+ } catch {
165
+ isFile = false;
166
+ }
167
+ if (isFile) {
168
+ const ext = extname(pathOrText).toLowerCase();
169
+ if (ext === ".pdf") {
170
+ throw new CographError(
171
+ "PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API."
172
+ );
173
+ }
174
+ content = readFileSync(pathOrText, "utf-8");
175
+ fmt = opts.contentType ?? EXT_FORMAT[ext] ?? "text";
176
+ if (fmt === "csv") {
177
+ return this.ingestCsv(content, opts.kg);
178
+ }
179
+ } else {
180
+ content = pathOrText;
181
+ fmt = opts.contentType ?? "text";
182
+ }
183
+ const body = {
184
+ content,
185
+ content_type: fmt,
186
+ source: "client"
187
+ };
188
+ if (opts.kg) body.kg_name = opts.kg;
189
+ return this.request("POST", `${this.base()}/ingest`, body, 12e4);
190
+ }
191
+ async ingestCsv(content, kgName) {
192
+ const rows = parseCsv(content);
193
+ if (rows.length === 0) throw new CographError("CSV is empty");
194
+ const headers = Object.keys(rows[0]);
195
+ const schemaBody = {
196
+ headers,
197
+ sample_rows: rows.slice(0, 5),
198
+ total_rows: rows.length
199
+ };
200
+ const mapping = await this.request(
201
+ "POST",
202
+ `${this.base()}/ingest/csv/schema`,
203
+ schemaBody,
204
+ 3e5
205
+ );
206
+ const batchSize = 50;
207
+ let totalEntities = 0;
208
+ let totalTriples = 0;
209
+ for (let i = 0; i < rows.length; i += batchSize) {
210
+ const batch = rows.slice(i, i + batchSize);
211
+ const body = {
212
+ mapping,
213
+ rows: batch,
214
+ source: "client"
215
+ };
216
+ if (kgName) body.kg_name = kgName;
217
+ const result = await this.request("POST", `${this.base()}/ingest/csv/rows`, body, 3e5);
218
+ totalEntities += result.entities_resolved ?? 0;
219
+ totalTriples += result.triples_inserted ?? 0;
220
+ }
221
+ return {
222
+ entities_resolved: totalEntities,
223
+ triples_inserted: totalTriples,
224
+ mapping
225
+ };
226
+ }
227
+ /** Ask a natural language question and return the parsed response. */
228
+ async ask(question, opts = {}) {
229
+ const body = { question };
230
+ if (opts.kg) body.kg_name = opts.kg;
231
+ if (opts.model) body.model = opts.model;
232
+ return this.request("POST", `${this.base()}/ask`, body, 6e4);
233
+ }
234
+ /** List all knowledge graphs for the current tenant. */
235
+ async listKgs() {
236
+ const data = await this.request(
237
+ "GET",
238
+ `${this.base()}/kgs`,
239
+ void 0,
240
+ 15e3
241
+ );
242
+ if (Array.isArray(data)) return data;
243
+ if (data && typeof data === "object" && "kgs" in data) {
244
+ const kgs = data.kgs;
245
+ if (Array.isArray(kgs)) return kgs;
246
+ }
247
+ return [];
248
+ }
249
+ /** Create a knowledge graph. */
250
+ async createKg(name, description) {
251
+ const body = { name };
252
+ if (description) body.description = description;
253
+ return this.request("POST", `${this.base()}/kgs`, body, 15e3);
254
+ }
255
+ /** Delete a knowledge graph by name. */
256
+ async deleteKg(name) {
257
+ return this.request(
258
+ "DELETE",
259
+ `${this.base()}/kgs/${encodeURIComponent(name)}`,
260
+ void 0,
261
+ 3e4
262
+ );
263
+ }
264
+ /** List ontology types. */
265
+ async ontologyTypes() {
266
+ const data = await this.request(
267
+ "GET",
268
+ `${this.base()}/ontology/types`,
269
+ void 0,
270
+ 15e3
271
+ );
272
+ return Array.isArray(data) ? data : [];
273
+ }
274
+ };
275
+
276
+ export {
277
+ CographError,
278
+ Client
279
+ };
280
+ //# sourceMappingURL=chunk-YVKKUWD7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { extname } from \"node:path\";\nimport { readConfig } from \"./config.js\";\n\nexport class CographError extends Error {\n status?: number;\n body?: string;\n\n constructor(message: string, opts?: { status?: number; body?: string }) {\n super(message);\n this.name = \"CographError\";\n this.status = opts?.status;\n this.body = opts?.body;\n }\n}\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n tenant?: string;\n}\n\nexport interface IngestOptions {\n kg?: string;\n contentType?: \"text\" | \"csv\" | \"json\" | string;\n}\n\nexport interface AskOptions {\n kg?: string;\n model?: string;\n}\n\nfunction envVar(name: string, fallback?: string): string | undefined {\n // Prefer COGRAPH_, fall back to OMNIX_ so old configs keep working.\n return (\n process.env[`COGRAPH_${name}`] ||\n process.env[`OMNIX_${name}`] ||\n fallback\n );\n}\n\nconst EXT_FORMAT: Record<string, string> = {\n \".csv\": \"csv\",\n \".json\": \"json\",\n \".jsonl\": \"json\",\n \".txt\": \"text\",\n};\n\n/**\n * Parse a CSV string into an array of row objects.\n *\n * Minimal RFC-4180-ish parser: handles quoted fields with commas, escaped\n * quotes (`\"\"`), CRLF/LF line endings. Does not handle BOM stripping or\n * encoding detection — we assume UTF-8 text in.\n */\nexport function parseCsv(content: string): Record<string, string>[] {\n const rows: string[][] = [];\n let cur: string[] = [];\n let field = \"\";\n let inQuotes = false;\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i];\n if (inQuotes) {\n if (ch === '\"') {\n if (content[i + 1] === '\"') {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += ch;\n }\n } else {\n if (ch === '\"') {\n inQuotes = true;\n } else if (ch === \",\") {\n cur.push(field);\n field = \"\";\n } else if (ch === \"\\n\") {\n cur.push(field);\n rows.push(cur);\n cur = [];\n field = \"\";\n } else if (ch === \"\\r\") {\n // swallow; handled by the following \\n in CRLF, or treat lone \\r as line end\n if (content[i + 1] !== \"\\n\") {\n cur.push(field);\n rows.push(cur);\n cur = [];\n field = \"\";\n }\n } else {\n field += ch;\n }\n }\n }\n // flush trailing field/row\n if (field.length > 0 || cur.length > 0) {\n cur.push(field);\n rows.push(cur);\n }\n\n if (rows.length === 0) return [];\n const headers = rows[0]!.map((h) => h.trim());\n const out: Record<string, string>[] = [];\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r]!;\n // skip blank trailing lines\n if (row.length === 1 && row[0] === \"\") continue;\n const obj: Record<string, string> = {};\n for (let c = 0; c < headers.length; c++) {\n obj[headers[c]!] = row[c] ?? \"\";\n }\n out.push(obj);\n }\n return out;\n}\n\nexport class Client {\n apiKey: string | undefined;\n baseUrl: string;\n tenant: string;\n\n constructor(opts: ClientOptions = {}) {\n // Resolution order for each field: explicit opts → env var → ~/.cograph/config.json\n // (written by `cograph login`) → built-in default. Reading the config eagerly\n // is cheap (small JSON file) and lets users skip env vars entirely after login.\n const cfg = readConfig();\n this.apiKey = opts.apiKey ?? envVar(\"API_KEY\") ?? cfg.apiKey;\n const url =\n opts.baseUrl ?? envVar(\"API_URL\") ?? cfg.apiUrl ?? \"https://api.cograph.cloud\";\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.tenant = opts.tenant ?? envVar(\"TENANT\") ?? cfg.tenant ?? \"demo-tenant\";\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.apiKey) h[\"X-API-Key\"] = this.apiKey;\n return h;\n }\n\n private base(): string {\n return `${this.baseUrl}/graphs/${this.tenant}`;\n }\n\n private async request<T = unknown>(\n method: string,\n url: string,\n body?: unknown,\n timeoutMs: number = 120_000,\n ): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetch(url, {\n method,\n headers: this.headers(),\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);\n }\n throw new CographError(\n `Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n clearTimeout(timer);\n\n if (!res.ok) {\n let text = \"\";\n try {\n text = await res.text();\n } catch {\n // ignore\n }\n throw new CographError(`HTTP ${res.status}: ${text}`, {\n status: res.status,\n body: text,\n });\n }\n\n // 204 No Content\n if (res.status === 204) return undefined as T;\n\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"application/json\")) {\n return (await res.json()) as T;\n }\n // fall back to text\n const text = await res.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n }\n\n /**\n * Ingest a file path or raw text into a knowledge graph.\n *\n * If `pathOrText` points to an existing file, its contents are read and the\n * format is inferred from the extension (.csv, .json, .txt) unless\n * `contentType` is given. CSV files use the two-step schema-inference + row\n * mapping flow.\n */\n async ingest(\n pathOrText: string,\n opts: IngestOptions = {},\n ): Promise<Record<string, unknown>> {\n let content: string;\n let fmt: string;\n\n let isFile = false;\n try {\n isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();\n } catch {\n isFile = false;\n }\n\n if (isFile) {\n const ext = extname(pathOrText).toLowerCase();\n if (ext === \".pdf\") {\n throw new CographError(\n \"PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API.\",\n );\n }\n content = readFileSync(pathOrText, \"utf-8\");\n fmt = opts.contentType ?? EXT_FORMAT[ext] ?? \"text\";\n if (fmt === \"csv\") {\n return this.ingestCsv(content, opts.kg);\n }\n } else {\n content = pathOrText;\n fmt = opts.contentType ?? \"text\";\n }\n\n const body: Record<string, unknown> = {\n content,\n content_type: fmt,\n source: \"client\",\n };\n if (opts.kg) body.kg_name = opts.kg;\n return this.request(\"POST\", `${this.base()}/ingest`, body, 120_000);\n }\n\n private async ingestCsv(\n content: string,\n kgName: string | undefined,\n ): Promise<Record<string, unknown>> {\n const rows = parseCsv(content);\n if (rows.length === 0) throw new CographError(\"CSV is empty\");\n const headers = Object.keys(rows[0]!);\n\n const schemaBody = {\n headers,\n sample_rows: rows.slice(0, 5),\n total_rows: rows.length,\n };\n const mapping = await this.request<Record<string, unknown>>(\n \"POST\",\n `${this.base()}/ingest/csv/schema`,\n schemaBody,\n 300_000,\n );\n\n const batchSize = 50;\n let totalEntities = 0;\n let totalTriples = 0;\n for (let i = 0; i < rows.length; i += batchSize) {\n const batch = rows.slice(i, i + batchSize);\n const body: Record<string, unknown> = {\n mapping,\n rows: batch,\n source: \"client\",\n };\n if (kgName) body.kg_name = kgName;\n const result = await this.request<{\n entities_resolved?: number;\n triples_inserted?: number;\n }>(\"POST\", `${this.base()}/ingest/csv/rows`, body, 300_000);\n totalEntities += result.entities_resolved ?? 0;\n totalTriples += result.triples_inserted ?? 0;\n }\n\n return {\n entities_resolved: totalEntities,\n triples_inserted: totalTriples,\n mapping,\n };\n }\n\n /** Ask a natural language question and return the parsed response. */\n async ask(\n question: string,\n opts: AskOptions = {},\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = { question };\n if (opts.kg) body.kg_name = opts.kg;\n if (opts.model) body.model = opts.model;\n return this.request(\"POST\", `${this.base()}/ask`, body, 60_000);\n }\n\n /** List all knowledge graphs for the current tenant. */\n async listKgs(): Promise<Array<Record<string, unknown>>> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/kgs`,\n undefined,\n 15_000,\n );\n if (Array.isArray(data)) return data as Array<Record<string, unknown>>;\n if (data && typeof data === \"object\" && \"kgs\" in data) {\n const kgs = (data as { kgs?: unknown }).kgs;\n if (Array.isArray(kgs)) return kgs as Array<Record<string, unknown>>;\n }\n return [];\n }\n\n /** Create a knowledge graph. */\n async createKg(\n name: string,\n description?: string,\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = { name };\n if (description) body.description = description;\n return this.request(\"POST\", `${this.base()}/kgs`, body, 15_000);\n }\n\n /** Delete a knowledge graph by name. */\n async deleteKg(name: string): Promise<Record<string, unknown>> {\n return this.request(\n \"DELETE\",\n `${this.base()}/kgs/${encodeURIComponent(name)}`,\n undefined,\n 30_000,\n );\n }\n\n /** List ontology types. */\n async ontologyTypes(): Promise<Array<Record<string, unknown>>> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/ontology/types`,\n undefined,\n 15_000,\n );\n return Array.isArray(data) ? (data as Array<Record<string, unknown>>) : [];\n }\n}\n"],"mappings":"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","names":["text"]}
package/dist/cli.js CHANGED
@@ -1,279 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ Client,
4
+ CographError
5
+ } from "./chunk-YVKKUWD7.js";
6
+ import "./chunk-7VVBEUZQ.js";
2
7
 
3
8
  // src/cli.ts
4
9
  import { createInterface } from "readline";
5
10
  import { Command } from "commander";
6
-
7
- // src/client.ts
8
- import { existsSync, readFileSync, statSync } from "fs";
9
- import { extname } from "path";
10
- var CographError = class extends Error {
11
- status;
12
- body;
13
- constructor(message, opts) {
14
- super(message);
15
- this.name = "CographError";
16
- this.status = opts?.status;
17
- this.body = opts?.body;
18
- }
19
- };
20
- function envVar(name, fallback) {
21
- return process.env[`COGRAPH_${name}`] || process.env[`OMNIX_${name}`] || fallback;
22
- }
23
- var EXT_FORMAT = {
24
- ".csv": "csv",
25
- ".json": "json",
26
- ".jsonl": "json",
27
- ".txt": "text"
28
- };
29
- function parseCsv(content) {
30
- const rows = [];
31
- let cur = [];
32
- let field = "";
33
- let inQuotes = false;
34
- for (let i = 0; i < content.length; i++) {
35
- const ch = content[i];
36
- if (inQuotes) {
37
- if (ch === '"') {
38
- if (content[i + 1] === '"') {
39
- field += '"';
40
- i++;
41
- } else {
42
- inQuotes = false;
43
- }
44
- } else {
45
- field += ch;
46
- }
47
- } else {
48
- if (ch === '"') {
49
- inQuotes = true;
50
- } else if (ch === ",") {
51
- cur.push(field);
52
- field = "";
53
- } else if (ch === "\n") {
54
- cur.push(field);
55
- rows.push(cur);
56
- cur = [];
57
- field = "";
58
- } else if (ch === "\r") {
59
- if (content[i + 1] !== "\n") {
60
- cur.push(field);
61
- rows.push(cur);
62
- cur = [];
63
- field = "";
64
- }
65
- } else {
66
- field += ch;
67
- }
68
- }
69
- }
70
- if (field.length > 0 || cur.length > 0) {
71
- cur.push(field);
72
- rows.push(cur);
73
- }
74
- if (rows.length === 0) return [];
75
- const headers = rows[0].map((h) => h.trim());
76
- const out = [];
77
- for (let r = 1; r < rows.length; r++) {
78
- const row = rows[r];
79
- if (row.length === 1 && row[0] === "") continue;
80
- const obj = {};
81
- for (let c = 0; c < headers.length; c++) {
82
- obj[headers[c]] = row[c] ?? "";
83
- }
84
- out.push(obj);
85
- }
86
- return out;
87
- }
88
- var Client = class {
89
- apiKey;
90
- baseUrl;
91
- tenant;
92
- constructor(opts = {}) {
93
- this.apiKey = opts.apiKey ?? envVar("API_KEY");
94
- const url = opts.baseUrl ?? envVar("API_URL") ?? "https://api.cograph.cloud";
95
- this.baseUrl = url.replace(/\/+$/, "");
96
- this.tenant = opts.tenant ?? envVar("TENANT") ?? "demo-tenant";
97
- }
98
- headers() {
99
- const h = { "Content-Type": "application/json" };
100
- if (this.apiKey) h["X-API-Key"] = this.apiKey;
101
- return h;
102
- }
103
- base() {
104
- return `${this.baseUrl}/graphs/${this.tenant}`;
105
- }
106
- async request(method, url, body, timeoutMs = 12e4) {
107
- const controller = new AbortController();
108
- const timer = setTimeout(() => controller.abort(), timeoutMs);
109
- let res;
110
- try {
111
- res = await fetch(url, {
112
- method,
113
- headers: this.headers(),
114
- body: body === void 0 ? void 0 : JSON.stringify(body),
115
- signal: controller.signal
116
- });
117
- } catch (err) {
118
- clearTimeout(timer);
119
- if (err instanceof Error && err.name === "AbortError") {
120
- throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);
121
- }
122
- throw new CographError(
123
- `Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`
124
- );
125
- }
126
- clearTimeout(timer);
127
- if (!res.ok) {
128
- let text2 = "";
129
- try {
130
- text2 = await res.text();
131
- } catch {
132
- }
133
- throw new CographError(`HTTP ${res.status}: ${text2}`, {
134
- status: res.status,
135
- body: text2
136
- });
137
- }
138
- if (res.status === 204) return void 0;
139
- const ct = res.headers.get("content-type") ?? "";
140
- if (ct.includes("application/json")) {
141
- return await res.json();
142
- }
143
- const text = await res.text();
144
- try {
145
- return JSON.parse(text);
146
- } catch {
147
- return text;
148
- }
149
- }
150
- /**
151
- * Ingest a file path or raw text into a knowledge graph.
152
- *
153
- * If `pathOrText` points to an existing file, its contents are read and the
154
- * format is inferred from the extension (.csv, .json, .txt) unless
155
- * `contentType` is given. CSV files use the two-step schema-inference + row
156
- * mapping flow.
157
- */
158
- async ingest(pathOrText, opts = {}) {
159
- let content;
160
- let fmt;
161
- let isFile = false;
162
- try {
163
- isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();
164
- } catch {
165
- isFile = false;
166
- }
167
- if (isFile) {
168
- const ext = extname(pathOrText).toLowerCase();
169
- if (ext === ".pdf") {
170
- throw new CographError(
171
- "PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API."
172
- );
173
- }
174
- content = readFileSync(pathOrText, "utf-8");
175
- fmt = opts.contentType ?? EXT_FORMAT[ext] ?? "text";
176
- if (fmt === "csv") {
177
- return this.ingestCsv(content, opts.kg);
178
- }
179
- } else {
180
- content = pathOrText;
181
- fmt = opts.contentType ?? "text";
182
- }
183
- const body = {
184
- content,
185
- content_type: fmt,
186
- source: "client"
187
- };
188
- if (opts.kg) body.kg_name = opts.kg;
189
- return this.request("POST", `${this.base()}/ingest`, body, 12e4);
190
- }
191
- async ingestCsv(content, kgName) {
192
- const rows = parseCsv(content);
193
- if (rows.length === 0) throw new CographError("CSV is empty");
194
- const headers = Object.keys(rows[0]);
195
- const schemaBody = {
196
- headers,
197
- sample_rows: rows.slice(0, 5),
198
- total_rows: rows.length
199
- };
200
- const mapping = await this.request(
201
- "POST",
202
- `${this.base()}/ingest/csv/schema`,
203
- schemaBody,
204
- 3e5
205
- );
206
- const batchSize = 50;
207
- let totalEntities = 0;
208
- let totalTriples = 0;
209
- for (let i = 0; i < rows.length; i += batchSize) {
210
- const batch = rows.slice(i, i + batchSize);
211
- const body = {
212
- mapping,
213
- rows: batch,
214
- source: "client"
215
- };
216
- if (kgName) body.kg_name = kgName;
217
- const result = await this.request("POST", `${this.base()}/ingest/csv/rows`, body, 3e5);
218
- totalEntities += result.entities_resolved ?? 0;
219
- totalTriples += result.triples_inserted ?? 0;
220
- }
221
- return {
222
- entities_resolved: totalEntities,
223
- triples_inserted: totalTriples,
224
- mapping
225
- };
226
- }
227
- /** Ask a natural language question and return the parsed response. */
228
- async ask(question, opts = {}) {
229
- const body = { question };
230
- if (opts.kg) body.kg_name = opts.kg;
231
- if (opts.model) body.model = opts.model;
232
- return this.request("POST", `${this.base()}/ask`, body, 6e4);
233
- }
234
- /** List all knowledge graphs for the current tenant. */
235
- async listKgs() {
236
- const data = await this.request(
237
- "GET",
238
- `${this.base()}/kgs`,
239
- void 0,
240
- 15e3
241
- );
242
- if (Array.isArray(data)) return data;
243
- if (data && typeof data === "object" && "kgs" in data) {
244
- const kgs = data.kgs;
245
- if (Array.isArray(kgs)) return kgs;
246
- }
247
- return [];
248
- }
249
- /** Create a knowledge graph. */
250
- async createKg(name, description) {
251
- const body = { name };
252
- if (description) body.description = description;
253
- return this.request("POST", `${this.base()}/kgs`, body, 15e3);
254
- }
255
- /** Delete a knowledge graph by name. */
256
- async deleteKg(name) {
257
- return this.request(
258
- "DELETE",
259
- `${this.base()}/kgs/${encodeURIComponent(name)}`,
260
- void 0,
261
- 3e4
262
- );
263
- }
264
- /** List ontology types. */
265
- async ontologyTypes() {
266
- const data = await this.request(
267
- "GET",
268
- `${this.base()}/ontology/types`,
269
- void 0,
270
- 15e3
271
- );
272
- return Array.isArray(data) ? data : [];
273
- }
274
- };
275
-
276
- // src/cli.ts
277
11
  function client() {
278
12
  return new Client();
279
13
  }
@@ -544,6 +278,14 @@ program.command("clear").description("Clear data").option("--kg <name>", "Clear
544
278
  });
545
279
  }
546
280
  );
281
+ program.command("login").description("Sign in via your browser and save an API key").action(async () => {
282
+ const { runLogin } = await import("./login-Z3IUOAOB.js");
283
+ await runLogin();
284
+ });
285
+ program.command("shell").description("Start an interactive REPL").option("--kg <name>", "Knowledge graph to use").action(async (opts) => {
286
+ const { runShell } = await import("./shell-HXPNICDN.js");
287
+ await runShell({ kg: opts.kg });
288
+ });
547
289
  program.parseAsync(process.argv).catch((err) => {
548
290
  fail(`Error: ${err instanceof Error ? err.message : String(err)}`);
549
291
  });