cograph 0.1.11 → 0.1.13
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/{chunk-LZAJDGV4.js → chunk-LHGNZB2L.js} +9 -2
- package/dist/chunk-LHGNZB2L.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/{shell-LWIHW6C7.js → shell-LPQMW4MI.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-LZAJDGV4.js.map +0 -1
- /package/dist/{shell-LWIHW6C7.js.map → shell-LPQMW4MI.js.map} +0 -0
|
@@ -195,9 +195,16 @@ var Client = class {
|
|
|
195
195
|
const rows = parseCsv(content);
|
|
196
196
|
if (rows.length === 0) throw new CographError("CSV is empty");
|
|
197
197
|
const headers = Object.keys(rows[0]);
|
|
198
|
+
const sampleRows = rows.map((row, idx) => ({
|
|
199
|
+
row,
|
|
200
|
+
idx,
|
|
201
|
+
score: Object.values(row).filter(
|
|
202
|
+
(v) => v != null && String(v).trim() !== ""
|
|
203
|
+
).length
|
|
204
|
+
})).sort((a, b) => b.score - a.score || a.idx - b.idx).slice(0, 10).map((s) => s.row);
|
|
198
205
|
const schemaBody = {
|
|
199
206
|
headers,
|
|
200
|
-
sample_rows:
|
|
207
|
+
sample_rows: sampleRows,
|
|
201
208
|
total_rows: rows.length
|
|
202
209
|
};
|
|
203
210
|
const mapping = await this.request(
|
|
@@ -308,4 +315,4 @@ export {
|
|
|
308
315
|
CographError,
|
|
309
316
|
Client
|
|
310
317
|
};
|
|
311
|
-
//# sourceMappingURL=chunk-
|
|
318
|
+
//# sourceMappingURL=chunk-LHGNZB2L.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 /** Rows per batch for CSV ingest. Default 200. Larger = fewer round-trips\n * but higher per-request memory; 200 is a good balance for typical KGs. */\n batchSize?: number;\n /** Max number of batches in flight at once. Default 4. Higher saturates\n * the backend faster but risks 429s on large ingests. */\n concurrency?: number;\n /** Called after each batch completes during CSV ingest, in batch order.\n * Use for progress UI. Not invoked for text/json ingest. */\n onProgress?: (progress: IngestProgress) => void;\n}\n\nexport interface IngestProgress {\n rowsProcessed: number;\n totalRows: number;\n entitiesResolved: number;\n triplesInserted: number;\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);\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 opts: IngestOptions,\n ): Promise<Record<string, unknown>> {\n const kgName = opts.kg;\n const batchSize = opts.batchSize ?? 200;\n const concurrency = opts.concurrency ?? 4;\n\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 // Pick the rows with the most non-empty fields for schema inference.\n // Mostly-empty leading rows (e.g. soft-deleted records) otherwise feed\n // the LLM a near-blank sample and reliably produce malformed JSON.\n // Stable on ties — original order preserved within equal scores.\n const sampleRows = rows\n .map((row, idx) => ({\n row,\n idx,\n score: Object.values(row).filter(\n (v) => v != null && String(v).trim() !== \"\",\n ).length,\n }))\n .sort((a, b) => b.score - a.score || a.idx - b.idx)\n .slice(0, 10)\n .map((s) => s.row);\n\n const schemaBody = {\n headers,\n sample_rows: sampleRows,\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 // Slice rows into batches up front so we can fire them off in a\n // bounded worker pool. Sequential 50-row batches over 891 rows took\n // ~60s end-to-end (18 round-trips); 200-row batches × 4 in flight\n // brings that to ~5s on the same backend.\n const batches: Array<Record<string, string>[]> = [];\n for (let i = 0; i < rows.length; i += batchSize) {\n batches.push(rows.slice(i, i + batchSize));\n }\n\n let totalEntities = 0;\n let totalTriples = 0;\n let rowsProcessed = 0;\n let nextBatch = 0;\n\n const postBatch = async (batch: Record<string, string>[]) => {\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 return {\n entities: result.entities_resolved ?? 0,\n triples: result.triples_inserted ?? 0,\n size: batch.length,\n };\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n const idx = nextBatch++;\n if (idx >= batches.length) return;\n const r = await postBatch(batches[idx]!);\n totalEntities += r.entities;\n totalTriples += r.triples;\n rowsProcessed += r.size;\n opts.onProgress?.({\n rowsProcessed,\n totalRows: rows.length,\n entitiesResolved: totalEntities,\n triplesInserted: totalTriples,\n });\n }\n };\n\n const workers: Array<Promise<void>> = [];\n for (let i = 0; i < Math.min(concurrency, batches.length); i++) {\n workers.push(worker());\n }\n await Promise.all(workers);\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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Client,
|
|
4
4
|
CographError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LHGNZB2L.js";
|
|
6
6
|
import "./chunk-7VVBEUZQ.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
@@ -36,7 +36,7 @@ async function confirm(prompt) {
|
|
|
36
36
|
}
|
|
37
37
|
var program = new Command();
|
|
38
38
|
program.name("cograph").description("Cograph Knowledge Graph CLI").version("0.1.7").action(async () => {
|
|
39
|
-
const { runShell } = await import("./shell-
|
|
39
|
+
const { runShell } = await import("./shell-LPQMW4MI.js");
|
|
40
40
|
await runShell({});
|
|
41
41
|
});
|
|
42
42
|
var kg = program.command("kg").description("Manage knowledge graphs");
|
|
@@ -249,7 +249,7 @@ program.command("clear").description("Clear data").option("--kg <name>", "Clear
|
|
|
249
249
|
};
|
|
250
250
|
if (c.apiKey) headers["X-API-Key"] = c.apiKey;
|
|
251
251
|
const filters = opts.includeOntology ? "" : `FILTER(CONTAINS(STR(?s), '/entities/') || CONTAINS(STR(?s), '/onto/') || CONTAINS(STR(?s), '/kgs/'))`;
|
|
252
|
-
const query = `SELECT ?s ?p ?o FROM <https://
|
|
252
|
+
const query = `SELECT ?s ?p ?o FROM <https://cograph.tech/graphs/${tenant}> WHERE { ?s ?p ?o . ${filters} } LIMIT 1000`;
|
|
253
253
|
process.stdout.write("Clearing...\n");
|
|
254
254
|
let deleted = 0;
|
|
255
255
|
for (let i = 0; i < 50; i++) {
|
|
@@ -286,7 +286,7 @@ program.command("login").description("Sign in via your browser and save an API k
|
|
|
286
286
|
await runLogin();
|
|
287
287
|
});
|
|
288
288
|
program.command("shell").description("Start an interactive REPL").option("--kg <name>", "Knowledge graph to use").action(async (opts) => {
|
|
289
|
-
const { runShell } = await import("./shell-
|
|
289
|
+
const { runShell } = await import("./shell-LPQMW4MI.js");
|
|
290
290
|
await runShell({ kg: opts.kg });
|
|
291
291
|
});
|
|
292
292
|
program.parseAsync(process.argv).catch((err) => {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\nimport { Command } from \"commander\";\nimport { Client, CographError } from \"./client.js\";\n\nfunction client(): Client {\n return new Client();\n}\n\nfunction printJson(data: unknown): void {\n process.stdout.write(JSON.stringify(data, null, 2) + \"\\n\");\n}\n\nfunction fail(msg: string, code = 1): never {\n process.stderr.write(msg.endsWith(\"\\n\") ? msg : msg + \"\\n\");\n process.exit(code);\n}\n\nasync function withErrors<T>(fn: () => Promise<T>): Promise<T | void> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof CographError) {\n fail(`Error: ${err.message}`);\n }\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\nasync function confirm(prompt: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(`${prompt} [y/N] `, (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase() === \"y\");\n });\n });\n}\n\nconst program = new Command();\nprogram\n .name(\"cograph\")\n .description(\"Cograph Knowledge Graph CLI\")\n .version(\"0.1.7\")\n // Default action when no subcommand is given: drop into the interactive\n // shell. So `cograph` (or `npx cograph`) Just Works for the common case;\n // subcommands like `cograph ingest <file>` still route to their own\n // actions because commander dispatches subcommands first.\n .action(async () => {\n const { runShell } = await import(\"./shell.js\");\n await runShell({});\n });\n\n// ---------------------------------------------------------------------------\n// kg\n// ---------------------------------------------------------------------------\n\nconst kg = program.command(\"kg\").description(\"Manage knowledge graphs\");\n\nkg.command(\"list\")\n .description(\"List knowledge graphs\")\n .action(async () => {\n await withErrors(async () => {\n const kgs = await client().listKgs();\n if (!kgs.length) {\n process.stdout.write(\n \"No knowledge graphs. Create one with: cograph kg create <name>\\n\",\n );\n return;\n }\n for (const k of kgs) {\n const name = String(k.name ?? \"?\");\n const triples = Number(k.triple_count ?? 0);\n const desc = k.description ? ` — ${k.description}` : \"\";\n const padName = name.padEnd(20, \" \");\n const padTriples = String(triples).padStart(6, \" \");\n process.stdout.write(` ${padName} ${padTriples} triples${desc}\\n`);\n }\n });\n });\n\nkg.command(\"create <name>\")\n .description(\"Create a knowledge graph\")\n .option(\"-d, --description <text>\", \"Description\")\n .action(async (name: string, opts: { description?: string }) => {\n await withErrors(async () => {\n const created = await client().createKg(name, opts.description);\n process.stdout.write(`Created knowledge graph: ${created.name ?? name}\\n`);\n });\n });\n\nkg.command(\"delete <name>\")\n .description(\"Delete a knowledge graph\")\n .action(async (name: string) => {\n await withErrors(async () => {\n await client().deleteKg(name);\n process.stdout.write(`Deleted knowledge graph: ${name}\\n`);\n });\n });\n\n// ---------------------------------------------------------------------------\n// ingest\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ingest [file]\")\n .description(\"Ingest data from a file or --text\")\n .option(\"-t, --text <text>\", \"Inline text to ingest\")\n .option(\"--kg <name>\", \"Target knowledge graph name\")\n .option(\n \"-f, --format <fmt>\",\n \"Override format detection (text|csv|json)\",\n )\n .action(\n async (\n file: string | undefined,\n opts: { text?: string; kg?: string; format?: string },\n ) => {\n await withErrors(async () => {\n const c = client();\n if (opts.text) {\n process.stdout.write(\n `Ingesting text (${opts.text.length.toLocaleString()} chars)...\\n`,\n );\n const result = await c.ingest(opts.text, {\n kg: opts.kg,\n contentType: opts.format ?? \"text\",\n });\n printIngestResult(result);\n return;\n }\n if (!file) {\n fail(\"Provide a file or --text\");\n }\n // ingest() handles file reading + format detection + CSV two-step flow.\n process.stdout.write(`Ingesting ${file}...\\n`);\n const result = await c.ingest(file, {\n kg: opts.kg,\n contentType: opts.format,\n });\n printIngestResult(result);\n });\n },\n );\n\nfunction printIngestResult(result: Record<string, unknown>): void {\n const num = (k: string) => Number(result[k] ?? 0);\n process.stdout.write(` Entities extracted: ${num(\"entities_extracted\")}\\n`);\n process.stdout.write(` Entities resolved: ${num(\"entities_resolved\")}\\n`);\n process.stdout.write(` Triples inserted: ${num(\"triples_inserted\")}\\n`);\n const types = result.types_created;\n if (Array.isArray(types) && types.length) {\n process.stdout.write(` Types created: ${types.join(\", \")}\\n`);\n }\n const rejections = result.rejections;\n if (Array.isArray(rejections) && rejections.length) {\n process.stdout.write(` Rejections: ${rejections.length}\\n`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// ask\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ask <question>\")\n .description(\"Ask a natural language question\")\n .option(\"--kg <name>\", \"Knowledge graph to query\")\n .option(\"-d, --debug\", \"Show SPARQL and latency breakdown\")\n .option(\"-m, --model <model>\", \"Override query model\")\n .action(\n async (\n question: string,\n opts: { kg?: string; debug?: boolean; model?: string },\n ) => {\n await withErrors(async () => {\n if (opts.model) process.stdout.write(`Model: ${opts.model}\\n`);\n process.stdout.write(`Q: ${question}\\n`);\n process.stdout.write(\"Generating answer...\\n\");\n const t0 = Date.now();\n const result = await client().ask(question, {\n kg: opts.kg,\n model: opts.model,\n });\n const roundtripMs = Date.now() - t0;\n process.stdout.write(`\\nA: ${result.answer ?? \"No answer\"}\\n`);\n if (opts.debug) {\n process.stdout.write(`\\nSPARQL:\\n${result.sparql ?? \"\"}\\n`);\n const timing = (result.timing ?? {}) as Record<string, unknown>;\n if (Object.keys(timing).length) {\n process.stdout.write(`\\n${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Stage\".padEnd(25)} ${\"Time\".padStart(10)}\\n`,\n );\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n for (const [key, val] of Object.entries(timing)) {\n if (key === \"attempts\") {\n process.stdout.write(\n `${\"Attempts\".padEnd(25)} ${String(val).padStart(10)}\\n`,\n );\n } else if (typeof val === \"string\") {\n const label = key\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n process.stdout.write(\n `${label.padEnd(25)} ${val.padStart(10)}\\n`,\n );\n } else {\n const label = key\n .replace(/_ms$/, \"\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n const num = typeof val === \"number\" ? val : Number(val);\n process.stdout.write(\n `${label.padEnd(25)} ${num.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Client roundtrip\".padEnd(25)} ${roundtripMs.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n// ontology\n// ---------------------------------------------------------------------------\n\nconst onto = program.command(\"ontology\").description(\"View ontology\");\n\nonto\n .command(\"types\")\n .description(\"List ontology types\")\n .action(async () => {\n await withErrors(async () => {\n const types = await client().ontologyTypes();\n if (!types.length) {\n process.stdout.write(\"No ontology types defined.\\n\");\n return;\n }\n for (const t of types) {\n const parent = t.parent_type\n ? ` (subClassOf ${t.parent_type})`\n : \"\";\n const desc = t.description ? ` — ${t.description}` : \"\";\n process.stdout.write(` ${t.name}${parent}${desc}\\n`);\n const attrs = (t.attributes ?? []) as Array<Record<string, unknown>>;\n for (const a of attrs) {\n process.stdout.write(\n ` .${a.name} (${a.datatype ?? \"string\"})\\n`,\n );\n }\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// clear\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"clear\")\n .description(\"Clear data\")\n .option(\"--kg <name>\", \"Clear a specific knowledge graph\")\n .option(\n \"--include-ontology\",\n \"Also clear the ontology (only meaningful when --kg is omitted)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip confirmation\", false)\n .action(\n async (opts: { kg?: string; includeOntology?: boolean; yes?: boolean }) => {\n await withErrors(async () => {\n let msg: string;\n if (opts.kg) {\n msg = `Clear KG '${opts.kg}'?`;\n } else if (opts.includeOntology) {\n msg = \"Clear EVERYTHING including ontology?\";\n } else {\n msg = \"Clear all instance data (ontology preserved)?\";\n }\n\n if (!opts.yes) {\n const ok = await confirm(msg);\n if (!ok) {\n process.stdout.write(\"Cancelled.\\n\");\n return;\n }\n }\n\n const c = client();\n if (opts.kg) {\n await c.deleteKg(opts.kg);\n process.stdout.write(`Cleared KG: ${opts.kg}\\n`);\n return;\n }\n\n // Bulk-clear via /query + DELETE /triples — same loop the Python CLI uses.\n const tenant = c.tenant;\n const baseUrl = `${c.baseUrl}/graphs/${tenant}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (c.apiKey) headers[\"X-API-Key\"] = c.apiKey;\n\n const filters = opts.includeOntology\n ? \"\"\n : `FILTER(CONTAINS(STR(?s), '/entities/') || CONTAINS(STR(?s), '/onto/') || CONTAINS(STR(?s), '/kgs/'))`;\n const query = `SELECT ?s ?p ?o FROM <https://omnix.dev/graphs/${tenant}> WHERE { ?s ?p ?o . ${filters} } LIMIT 1000`;\n\n process.stdout.write(\"Clearing...\\n\");\n let deleted = 0;\n for (let i = 0; i < 50; i++) {\n const fetchRes = await fetch(`${baseUrl}/query`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query }),\n });\n if (!fetchRes.ok) break;\n const data = (await fetchRes.json()) as {\n bindings?: Array<Record<string, unknown>>;\n };\n const bindings = data.bindings ?? [];\n if (!bindings.length) break;\n const triples = bindings\n .filter((b) => b.s)\n .map((b) => ({\n subject: b.s,\n predicate: b.p,\n object: b.o,\n }));\n for (let j = 0; j < triples.length; j += 100) {\n await fetch(`${baseUrl}/triples`, {\n method: \"DELETE\",\n headers,\n body: JSON.stringify({ triples: triples.slice(j, j + 100) }),\n });\n }\n deleted += triples.length;\n }\n process.stdout.write(`Deleted ${deleted} triples\\n`);\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n// login\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"login\")\n .description(\"Sign in via your browser and save an API key\")\n .action(async () => {\n const { runLogin } = await import(\"./login.js\");\n await runLogin();\n });\n\n// ---------------------------------------------------------------------------\n// shell\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"shell\")\n .description(\"Start an interactive REPL\")\n .option(\"--kg <name>\", \"Knowledge graph to use\")\n .action(async (opts: { kg?: string }) => {\n const { runShell } = await import(\"./shell.js\");\n await runShell({ kg: opts.kg });\n });\n\n// ---------------------------------------------------------------------------\n\nprogram.parseAsync(process.argv).catch((err) => {\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n});\n\n// silence unused import warning if ever needed\nvoid printJson;\n"],"mappings":"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","names":["result"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\nimport { Command } from \"commander\";\nimport { Client, CographError } from \"./client.js\";\n\nfunction client(): Client {\n return new Client();\n}\n\nfunction printJson(data: unknown): void {\n process.stdout.write(JSON.stringify(data, null, 2) + \"\\n\");\n}\n\nfunction fail(msg: string, code = 1): never {\n process.stderr.write(msg.endsWith(\"\\n\") ? msg : msg + \"\\n\");\n process.exit(code);\n}\n\nasync function withErrors<T>(fn: () => Promise<T>): Promise<T | void> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof CographError) {\n fail(`Error: ${err.message}`);\n }\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\nasync function confirm(prompt: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(`${prompt} [y/N] `, (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase() === \"y\");\n });\n });\n}\n\nconst program = new Command();\nprogram\n .name(\"cograph\")\n .description(\"Cograph Knowledge Graph CLI\")\n .version(\"0.1.7\")\n // Default action when no subcommand is given: drop into the interactive\n // shell. So `cograph` (or `npx cograph`) Just Works for the common case;\n // subcommands like `cograph ingest <file>` still route to their own\n // actions because commander dispatches subcommands first.\n .action(async () => {\n const { runShell } = await import(\"./shell.js\");\n await runShell({});\n });\n\n// ---------------------------------------------------------------------------\n// kg\n// ---------------------------------------------------------------------------\n\nconst kg = program.command(\"kg\").description(\"Manage knowledge graphs\");\n\nkg.command(\"list\")\n .description(\"List knowledge graphs\")\n .action(async () => {\n await withErrors(async () => {\n const kgs = await client().listKgs();\n if (!kgs.length) {\n process.stdout.write(\n \"No knowledge graphs. Create one with: cograph kg create <name>\\n\",\n );\n return;\n }\n for (const k of kgs) {\n const name = String(k.name ?? \"?\");\n const triples = Number(k.triple_count ?? 0);\n const desc = k.description ? ` — ${k.description}` : \"\";\n const padName = name.padEnd(20, \" \");\n const padTriples = String(triples).padStart(6, \" \");\n process.stdout.write(` ${padName} ${padTriples} triples${desc}\\n`);\n }\n });\n });\n\nkg.command(\"create <name>\")\n .description(\"Create a knowledge graph\")\n .option(\"-d, --description <text>\", \"Description\")\n .action(async (name: string, opts: { description?: string }) => {\n await withErrors(async () => {\n const created = await client().createKg(name, opts.description);\n process.stdout.write(`Created knowledge graph: ${created.name ?? name}\\n`);\n });\n });\n\nkg.command(\"delete <name>\")\n .description(\"Delete a knowledge graph\")\n .action(async (name: string) => {\n await withErrors(async () => {\n await client().deleteKg(name);\n process.stdout.write(`Deleted knowledge graph: ${name}\\n`);\n });\n });\n\n// ---------------------------------------------------------------------------\n// ingest\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ingest [file]\")\n .description(\"Ingest data from a file or --text\")\n .option(\"-t, --text <text>\", \"Inline text to ingest\")\n .option(\"--kg <name>\", \"Target knowledge graph name\")\n .option(\n \"-f, --format <fmt>\",\n \"Override format detection (text|csv|json)\",\n )\n .action(\n async (\n file: string | undefined,\n opts: { text?: string; kg?: string; format?: string },\n ) => {\n await withErrors(async () => {\n const c = client();\n if (opts.text) {\n process.stdout.write(\n `Ingesting text (${opts.text.length.toLocaleString()} chars)...\\n`,\n );\n const result = await c.ingest(opts.text, {\n kg: opts.kg,\n contentType: opts.format ?? \"text\",\n });\n printIngestResult(result);\n return;\n }\n if (!file) {\n fail(\"Provide a file or --text\");\n }\n // ingest() handles file reading + format detection + CSV two-step flow.\n process.stdout.write(`Ingesting ${file}...\\n`);\n const result = await c.ingest(file, {\n kg: opts.kg,\n contentType: opts.format,\n });\n printIngestResult(result);\n });\n },\n );\n\nfunction printIngestResult(result: Record<string, unknown>): void {\n const num = (k: string) => Number(result[k] ?? 0);\n process.stdout.write(` Entities extracted: ${num(\"entities_extracted\")}\\n`);\n process.stdout.write(` Entities resolved: ${num(\"entities_resolved\")}\\n`);\n process.stdout.write(` Triples inserted: ${num(\"triples_inserted\")}\\n`);\n const types = result.types_created;\n if (Array.isArray(types) && types.length) {\n process.stdout.write(` Types created: ${types.join(\", \")}\\n`);\n }\n const rejections = result.rejections;\n if (Array.isArray(rejections) && rejections.length) {\n process.stdout.write(` Rejections: ${rejections.length}\\n`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// ask\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ask <question>\")\n .description(\"Ask a natural language question\")\n .option(\"--kg <name>\", \"Knowledge graph to query\")\n .option(\"-d, --debug\", \"Show SPARQL and latency breakdown\")\n .option(\"-m, --model <model>\", \"Override query model\")\n .action(\n async (\n question: string,\n opts: { kg?: string; debug?: boolean; model?: string },\n ) => {\n await withErrors(async () => {\n if (opts.model) process.stdout.write(`Model: ${opts.model}\\n`);\n process.stdout.write(`Q: ${question}\\n`);\n process.stdout.write(\"Generating answer...\\n\");\n const t0 = Date.now();\n const result = await client().ask(question, {\n kg: opts.kg,\n model: opts.model,\n });\n const roundtripMs = Date.now() - t0;\n process.stdout.write(`\\nA: ${result.answer ?? \"No answer\"}\\n`);\n if (opts.debug) {\n process.stdout.write(`\\nSPARQL:\\n${result.sparql ?? \"\"}\\n`);\n const timing = (result.timing ?? {}) as Record<string, unknown>;\n if (Object.keys(timing).length) {\n process.stdout.write(`\\n${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Stage\".padEnd(25)} ${\"Time\".padStart(10)}\\n`,\n );\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n for (const [key, val] of Object.entries(timing)) {\n if (key === \"attempts\") {\n process.stdout.write(\n `${\"Attempts\".padEnd(25)} ${String(val).padStart(10)}\\n`,\n );\n } else if (typeof val === \"string\") {\n const label = key\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n process.stdout.write(\n `${label.padEnd(25)} ${val.padStart(10)}\\n`,\n );\n } else {\n const label = key\n .replace(/_ms$/, \"\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n const num = typeof val === \"number\" ? val : Number(val);\n process.stdout.write(\n `${label.padEnd(25)} ${num.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Client roundtrip\".padEnd(25)} ${roundtripMs.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n// ontology\n// ---------------------------------------------------------------------------\n\nconst onto = program.command(\"ontology\").description(\"View ontology\");\n\nonto\n .command(\"types\")\n .description(\"List ontology types\")\n .action(async () => {\n await withErrors(async () => {\n const types = await client().ontologyTypes();\n if (!types.length) {\n process.stdout.write(\"No ontology types defined.\\n\");\n return;\n }\n for (const t of types) {\n const parent = t.parent_type\n ? ` (subClassOf ${t.parent_type})`\n : \"\";\n const desc = t.description ? ` — ${t.description}` : \"\";\n process.stdout.write(` ${t.name}${parent}${desc}\\n`);\n const attrs = (t.attributes ?? []) as Array<Record<string, unknown>>;\n for (const a of attrs) {\n process.stdout.write(\n ` .${a.name} (${a.datatype ?? \"string\"})\\n`,\n );\n }\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// clear\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"clear\")\n .description(\"Clear data\")\n .option(\"--kg <name>\", \"Clear a specific knowledge graph\")\n .option(\n \"--include-ontology\",\n \"Also clear the ontology (only meaningful when --kg is omitted)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip confirmation\", false)\n .action(\n async (opts: { kg?: string; includeOntology?: boolean; yes?: boolean }) => {\n await withErrors(async () => {\n let msg: string;\n if (opts.kg) {\n msg = `Clear KG '${opts.kg}'?`;\n } else if (opts.includeOntology) {\n msg = \"Clear EVERYTHING including ontology?\";\n } else {\n msg = \"Clear all instance data (ontology preserved)?\";\n }\n\n if (!opts.yes) {\n const ok = await confirm(msg);\n if (!ok) {\n process.stdout.write(\"Cancelled.\\n\");\n return;\n }\n }\n\n const c = client();\n if (opts.kg) {\n await c.deleteKg(opts.kg);\n process.stdout.write(`Cleared KG: ${opts.kg}\\n`);\n return;\n }\n\n // Bulk-clear via /query + DELETE /triples — same loop the Python CLI uses.\n const tenant = c.tenant;\n const baseUrl = `${c.baseUrl}/graphs/${tenant}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (c.apiKey) headers[\"X-API-Key\"] = c.apiKey;\n\n const filters = opts.includeOntology\n ? \"\"\n : `FILTER(CONTAINS(STR(?s), '/entities/') || CONTAINS(STR(?s), '/onto/') || CONTAINS(STR(?s), '/kgs/'))`;\n const query = `SELECT ?s ?p ?o FROM <https://cograph.tech/graphs/${tenant}> WHERE { ?s ?p ?o . ${filters} } LIMIT 1000`;\n\n process.stdout.write(\"Clearing...\\n\");\n let deleted = 0;\n for (let i = 0; i < 50; i++) {\n const fetchRes = await fetch(`${baseUrl}/query`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query }),\n });\n if (!fetchRes.ok) break;\n const data = (await fetchRes.json()) as {\n bindings?: Array<Record<string, unknown>>;\n };\n const bindings = data.bindings ?? [];\n if (!bindings.length) break;\n const triples = bindings\n .filter((b) => b.s)\n .map((b) => ({\n subject: b.s,\n predicate: b.p,\n object: b.o,\n }));\n for (let j = 0; j < triples.length; j += 100) {\n await fetch(`${baseUrl}/triples`, {\n method: \"DELETE\",\n headers,\n body: JSON.stringify({ triples: triples.slice(j, j + 100) }),\n });\n }\n deleted += triples.length;\n }\n process.stdout.write(`Deleted ${deleted} triples\\n`);\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n// login\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"login\")\n .description(\"Sign in via your browser and save an API key\")\n .action(async () => {\n const { runLogin } = await import(\"./login.js\");\n await runLogin();\n });\n\n// ---------------------------------------------------------------------------\n// shell\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"shell\")\n .description(\"Start an interactive REPL\")\n .option(\"--kg <name>\", \"Knowledge graph to use\")\n .action(async (opts: { kg?: string }) => {\n const { runShell } = await import(\"./shell.js\");\n await runShell({ kg: opts.kg });\n });\n\n// ---------------------------------------------------------------------------\n\nprogram.parseAsync(process.argv).catch((err) => {\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n});\n\n// silence unused import warning if ever needed\nvoid printJson;\n"],"mappings":"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","names":["result"]}
|
package/dist/index.js
CHANGED
|
@@ -216,9 +216,16 @@ var Client = class {
|
|
|
216
216
|
const rows = parseCsv(content);
|
|
217
217
|
if (rows.length === 0) throw new CographError("CSV is empty");
|
|
218
218
|
const headers = Object.keys(rows[0]);
|
|
219
|
+
const sampleRows = rows.map((row, idx) => ({
|
|
220
|
+
row,
|
|
221
|
+
idx,
|
|
222
|
+
score: Object.values(row).filter(
|
|
223
|
+
(v) => v != null && String(v).trim() !== ""
|
|
224
|
+
).length
|
|
225
|
+
})).sort((a, b) => b.score - a.score || a.idx - b.idx).slice(0, 10).map((s) => s.row);
|
|
219
226
|
const schemaBody = {
|
|
220
227
|
headers,
|
|
221
|
-
sample_rows:
|
|
228
|
+
sample_rows: sampleRows,
|
|
222
229
|
total_rows: rows.length
|
|
223
230
|
};
|
|
224
231
|
const mapping = await this.request(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/config.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 /** Rows per batch for CSV ingest. Default 200. Larger = fewer round-trips\n * but higher per-request memory; 200 is a good balance for typical KGs. */\n batchSize?: number;\n /** Max number of batches in flight at once. Default 4. Higher saturates\n * the backend faster but risks 429s on large ingests. */\n concurrency?: number;\n /** Called after each batch completes during CSV ingest, in batch order.\n * Use for progress UI. Not invoked for text/json ingest. */\n onProgress?: (progress: IngestProgress) => void;\n}\n\nexport interface IngestProgress {\n rowsProcessed: number;\n totalRows: number;\n entitiesResolved: number;\n triplesInserted: number;\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);\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 opts: IngestOptions,\n ): Promise<Record<string, unknown>> {\n const kgName = opts.kg;\n const batchSize = opts.batchSize ?? 200;\n const concurrency = opts.concurrency ?? 4;\n\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 // Slice rows into batches up front so we can fire them off in a\n // bounded worker pool. Sequential 50-row batches over 891 rows took\n // ~60s end-to-end (18 round-trips); 200-row batches × 4 in flight\n // brings that to ~5s on the same backend.\n const batches: Array<Record<string, string>[]> = [];\n for (let i = 0; i < rows.length; i += batchSize) {\n batches.push(rows.slice(i, i + batchSize));\n }\n\n let totalEntities = 0;\n let totalTriples = 0;\n let rowsProcessed = 0;\n let nextBatch = 0;\n\n const postBatch = async (batch: Record<string, string>[]) => {\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 return {\n entities: result.entities_resolved ?? 0,\n triples: result.triples_inserted ?? 0,\n size: batch.length,\n };\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n const idx = nextBatch++;\n if (idx >= batches.length) return;\n const r = await postBatch(batches[idx]!);\n totalEntities += r.entities;\n totalTriples += r.triples;\n rowsProcessed += r.size;\n opts.onProgress?.({\n rowsProcessed,\n totalRows: rows.length,\n entitiesResolved: totalEntities,\n triplesInserted: totalTriples,\n });\n }\n };\n\n const workers: Array<Promise<void>> = [];\n for (let i = 0; i < Math.min(concurrency, batches.length); i++) {\n workers.push(worker());\n }\n await Promise.all(workers);\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","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,cAAAA,aAAY,gBAAAC,eAAc,gBAAgB;AACnD,SAAS,eAAe;;;ACDxB,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;;;ADhCO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAA2C;AACtE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,MAAM;AACpB,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;AAkCA,SAAS,OAAO,MAAc,UAAuC;AAEnE,SACE,QAAQ,IAAI,WAAW,IAAI,EAAE,KAC7B,QAAQ,IAAI,SAAS,IAAI,EAAE,KAC3B;AAEJ;AAEA,IAAM,aAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACV;AASO,SAAS,SAAS,SAA2C;AAClE,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAgB,CAAC;AACrB,MAAI,QAAQ;AACZ,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,KAAK,QAAQ,CAAC;AACpB,QAAI,UAAU;AACZ,UAAI,OAAO,KAAK;AACd,YAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,mBAAS;AACT;AAAA,QACF,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,UAAI,OAAO,KAAK;AACd,mBAAW;AAAA,MACb,WAAW,OAAO,KAAK;AACrB,YAAI,KAAK,KAAK;AACd,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AACtB,YAAI,KAAK,KAAK;AACd,aAAK,KAAK,GAAG;AACb,cAAM,CAAC;AACP,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AAEtB,YAAI,QAAQ,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAI,KAAK,KAAK;AACd,eAAK,KAAK,GAAG;AACb,gBAAM,CAAC;AACP,kBAAQ;AAAA,QACV;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,KAAK,IAAI,SAAS,GAAG;AACtC,QAAI,KAAK,KAAK;AACd,SAAK,KAAK,GAAG;AAAA,EACf;AAEA,MAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAC/B,QAAM,UAAU,KAAK,CAAC,EAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC5C,QAAM,MAAgC,CAAC;AACvC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,IAAI,WAAW,KAAK,IAAI,CAAC,MAAM,GAAI;AACvC,UAAM,MAA8B,CAAC;AACrC,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAI,QAAQ,CAAC,CAAE,IAAI,IAAI,CAAC,KAAK;AAAA,IAC/B;AACA,QAAI,KAAK,GAAG;AAAA,EACd;AACA,SAAO;AACT;AAEO,IAAM,SAAN,MAAa;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,OAAsB,CAAC,GAAG;AAIpC,UAAM,MAAM,WAAW;AACvB,SAAK,SAAS,KAAK,UAAU,OAAO,SAAS,KAAK,IAAI;AACtD,UAAM,MACJ,KAAK,WAAW,OAAO,SAAS,KAAK,IAAI,UAAU;AACrD,SAAK,UAAU,IAAI,QAAQ,QAAQ,EAAE;AACrC,SAAK,SAAS,KAAK,UAAU,OAAO,QAAQ,KAAK,IAAI,UAAU;AAAA,EACjE;AAAA,EAEQ,UAAkC;AACxC,UAAM,IAA4B,EAAE,gBAAgB,mBAAmB;AACvE,QAAI,KAAK,OAAQ,GAAE,WAAW,IAAI,KAAK;AACvC,WAAO;AAAA,EACT;AAAA,EAEQ,OAAe;AACrB,WAAO,GAAG,KAAK,OAAO,WAAW,KAAK,MAAM;AAAA,EAC9C;AAAA,EAEA,MAAc,QACZ,QACA,KACA,MACA,YAAoB,MACR;AACZ,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC5D,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA,SAAS,KAAK,QAAQ;AAAA,QACtB,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,QAC1D,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,KAAK;AAClB,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,aAAa,cAAc,GAAG,oBAAoB,SAAS,IAAI;AAAA,MAC3E;AACA,YAAM,IAAI;AAAA,QACR,4BAA4B,GAAG,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtF;AAAA,IACF;AACA,iBAAa,KAAK;AAElB,QAAI,CAAC,IAAI,IAAI;AACX,UAAIC,QAAO;AACX,UAAI;AACF,QAAAA,QAAO,MAAM,IAAI,KAAK;AAAA,MACxB,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,aAAa,QAAQ,IAAI,MAAM,KAAKA,KAAI,IAAI;AAAA,QACpD,QAAQ,IAAI;AAAA,QACZ,MAAMA;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,WAAW,IAAK,QAAO;AAE/B,UAAM,KAAK,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC9C,QAAI,GAAG,SAAS,kBAAkB,GAAG;AACnC,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OACJ,YACA,OAAsB,CAAC,GACW;AAClC,QAAI;AACJ,QAAI;AAEJ,QAAI,SAAS;AACb,QAAI;AACF,eAASC,YAAW,UAAU,KAAK,SAAS,UAAU,EAAE,OAAO;AAAA,IACjE,QAAQ;AACN,eAAS;AAAA,IACX;AAEA,QAAI,QAAQ;AACV,YAAM,MAAM,QAAQ,UAAU,EAAE,YAAY;AAC5C,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAUC,cAAa,YAAY,OAAO;AAC1C,YAAM,KAAK,eAAe,WAAW,GAAG,KAAK;AAC7C,UAAI,QAAQ,OAAO;AACjB,eAAO,KAAK,UAAU,SAAS,IAAI;AAAA,MACrC;AAAA,IACF,OAAO;AACL,gBAAU;AACV,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,UAAM,OAAgC;AAAA,MACpC;AAAA,MACA,cAAc;AAAA,MACd,QAAQ;AAAA,IACV;AACA,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,WAAW,MAAM,IAAO;AAAA,EACpE;AAAA,EAEA,MAAc,UACZ,SACA,MACkC;AAClC,UAAM,SAAS,KAAK;AACpB,UAAM,YAAY,KAAK,aAAa;AACpC,UAAM,cAAc,KAAK,eAAe;AAExC,UAAM,OAAO,SAAS,OAAO;AAC7B,QAAI,KAAK,WAAW,EAAG,OAAM,IAAI,aAAa,cAAc;AAC5D,UAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAE;AAEpC,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,aAAa,KAAK,MAAM,GAAG,CAAC;AAAA,MAC5B,YAAY,KAAK;AAAA,IACnB;AACA,UAAM,UAAU,MAAM,KAAK;AAAA,MACzB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAMA,UAAM,UAA2C,CAAC;AAClD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;AAC/C,cAAQ,KAAK,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC;AAAA,IAC3C;AAEA,QAAI,gBAAgB;AACpB,QAAI,eAAe;AACnB,QAAI,gBAAgB;AACpB,QAAI,YAAY;AAEhB,UAAM,YAAY,OAAO,UAAoC;AAC3D,YAAM,OAAgC;AAAA,QACpC;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AACA,UAAI,OAAQ,MAAK,UAAU;AAC3B,YAAM,SAAS,MAAM,KAAK,QAGvB,QAAQ,GAAG,KAAK,KAAK,CAAC,oBAAoB,MAAM,GAAO;AAC1D,aAAO;AAAA,QACL,UAAU,OAAO,qBAAqB;AAAA,QACtC,SAAS,OAAO,oBAAoB;AAAA,QACpC,MAAM,MAAM;AAAA,MACd;AAAA,IACF;AAEA,UAAM,SAAS,YAA2B;AACxC,aAAO,MAAM;AACX,cAAM,MAAM;AACZ,YAAI,OAAO,QAAQ,OAAQ;AAC3B,cAAM,IAAI,MAAM,UAAU,QAAQ,GAAG,CAAE;AACvC,yBAAiB,EAAE;AACnB,wBAAgB,EAAE;AAClB,yBAAiB,EAAE;AACnB,aAAK,aAAa;AAAA,UAChB;AAAA,UACA,WAAW,KAAK;AAAA,UAChB,kBAAkB;AAAA,UAClB,iBAAiB;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,UAAgC,CAAC;AACvC,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,aAAa,QAAQ,MAAM,GAAG,KAAK;AAC9D,cAAQ,KAAK,OAAO,CAAC;AAAA,IACvB;AACA,UAAM,QAAQ,IAAI,OAAO;AAEzB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,kBAAkB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,IACJ,UACA,OAAmB,CAAC,GACc;AAClC,UAAM,OAAgC,EAAE,SAAS;AACjD,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,QAAI,KAAK,MAAO,MAAK,QAAQ,KAAK;AAClC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,GAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,UAAmD;AACvD,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO;AAChC,QAAI,QAAQ,OAAO,SAAS,YAAY,SAAS,MAAM;AACrD,YAAM,MAAO,KAA2B;AACxC,UAAI,MAAM,QAAQ,GAAG,EAAG,QAAO;AAAA,IACjC;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,MAAM,SACJ,MACA,aACkC;AAClC,UAAM,OAAgC,EAAE,KAAK;AAC7C,QAAI,YAAa,MAAK,cAAc;AACpC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,IAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,SAAS,MAAgD;AAC7D,WAAO,KAAK;AAAA,MACV;AAAA,MACA,GAAG,KAAK,KAAK,CAAC,QAAQ,mBAAmB,IAAI,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBAAyD;AAC7D,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,QAAQ,IAAI,IAAK,OAA0C,CAAC;AAAA,EAC3E;AACF;","names":["existsSync","readFileSync","text","existsSync","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/config.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 /** Rows per batch for CSV ingest. Default 200. Larger = fewer round-trips\n * but higher per-request memory; 200 is a good balance for typical KGs. */\n batchSize?: number;\n /** Max number of batches in flight at once. Default 4. Higher saturates\n * the backend faster but risks 429s on large ingests. */\n concurrency?: number;\n /** Called after each batch completes during CSV ingest, in batch order.\n * Use for progress UI. Not invoked for text/json ingest. */\n onProgress?: (progress: IngestProgress) => void;\n}\n\nexport interface IngestProgress {\n rowsProcessed: number;\n totalRows: number;\n entitiesResolved: number;\n triplesInserted: number;\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);\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 opts: IngestOptions,\n ): Promise<Record<string, unknown>> {\n const kgName = opts.kg;\n const batchSize = opts.batchSize ?? 200;\n const concurrency = opts.concurrency ?? 4;\n\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 // Pick the rows with the most non-empty fields for schema inference.\n // Mostly-empty leading rows (e.g. soft-deleted records) otherwise feed\n // the LLM a near-blank sample and reliably produce malformed JSON.\n // Stable on ties — original order preserved within equal scores.\n const sampleRows = rows\n .map((row, idx) => ({\n row,\n idx,\n score: Object.values(row).filter(\n (v) => v != null && String(v).trim() !== \"\",\n ).length,\n }))\n .sort((a, b) => b.score - a.score || a.idx - b.idx)\n .slice(0, 10)\n .map((s) => s.row);\n\n const schemaBody = {\n headers,\n sample_rows: sampleRows,\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 // Slice rows into batches up front so we can fire them off in a\n // bounded worker pool. Sequential 50-row batches over 891 rows took\n // ~60s end-to-end (18 round-trips); 200-row batches × 4 in flight\n // brings that to ~5s on the same backend.\n const batches: Array<Record<string, string>[]> = [];\n for (let i = 0; i < rows.length; i += batchSize) {\n batches.push(rows.slice(i, i + batchSize));\n }\n\n let totalEntities = 0;\n let totalTriples = 0;\n let rowsProcessed = 0;\n let nextBatch = 0;\n\n const postBatch = async (batch: Record<string, string>[]) => {\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 return {\n entities: result.entities_resolved ?? 0,\n triples: result.triples_inserted ?? 0,\n size: batch.length,\n };\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n const idx = nextBatch++;\n if (idx >= batches.length) return;\n const r = await postBatch(batches[idx]!);\n totalEntities += r.entities;\n totalTriples += r.triples;\n rowsProcessed += r.size;\n opts.onProgress?.({\n rowsProcessed,\n totalRows: rows.length,\n entitiesResolved: totalEntities,\n triplesInserted: totalTriples,\n });\n }\n };\n\n const workers: Array<Promise<void>> = [];\n for (let i = 0; i < Math.min(concurrency, batches.length); i++) {\n workers.push(worker());\n }\n await Promise.all(workers);\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","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":"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","names":["existsSync","readFileSync","text","existsSync","readFileSync"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Client,
|
|
4
4
|
CographError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LHGNZB2L.js";
|
|
6
6
|
import "./chunk-7VVBEUZQ.js";
|
|
7
7
|
|
|
8
8
|
// src/shell.ts
|
|
@@ -529,4 +529,4 @@ async function runShell(opts) {
|
|
|
529
529
|
export {
|
|
530
530
|
runShell
|
|
531
531
|
};
|
|
532
|
-
//# sourceMappingURL=shell-
|
|
532
|
+
//# sourceMappingURL=shell-LPQMW4MI.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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 /** Rows per batch for CSV ingest. Default 200. Larger = fewer round-trips\n * but higher per-request memory; 200 is a good balance for typical KGs. */\n batchSize?: number;\n /** Max number of batches in flight at once. Default 4. Higher saturates\n * the backend faster but risks 429s on large ingests. */\n concurrency?: number;\n /** Called after each batch completes during CSV ingest, in batch order.\n * Use for progress UI. Not invoked for text/json ingest. */\n onProgress?: (progress: IngestProgress) => void;\n}\n\nexport interface IngestProgress {\n rowsProcessed: number;\n totalRows: number;\n entitiesResolved: number;\n triplesInserted: number;\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);\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 opts: IngestOptions,\n ): Promise<Record<string, unknown>> {\n const kgName = opts.kg;\n const batchSize = opts.batchSize ?? 200;\n const concurrency = opts.concurrency ?? 4;\n\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 // Slice rows into batches up front so we can fire them off in a\n // bounded worker pool. Sequential 50-row batches over 891 rows took\n // ~60s end-to-end (18 round-trips); 200-row batches × 4 in flight\n // brings that to ~5s on the same backend.\n const batches: Array<Record<string, string>[]> = [];\n for (let i = 0; i < rows.length; i += batchSize) {\n batches.push(rows.slice(i, i + batchSize));\n }\n\n let totalEntities = 0;\n let totalTriples = 0;\n let rowsProcessed = 0;\n let nextBatch = 0;\n\n const postBatch = async (batch: Record<string, string>[]) => {\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 return {\n entities: result.entities_resolved ?? 0,\n triples: result.triples_inserted ?? 0,\n size: batch.length,\n };\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n const idx = nextBatch++;\n if (idx >= batches.length) return;\n const r = await postBatch(batches[idx]!);\n totalEntities += r.entities;\n totalTriples += r.triples;\n rowsProcessed += r.size;\n opts.onProgress?.({\n rowsProcessed,\n totalRows: rows.length,\n entitiesResolved: totalEntities,\n triplesInserted: totalTriples,\n });\n }\n };\n\n const workers: Array<Promise<void>> = [];\n for (let i = 0; i < Math.min(concurrency, batches.length); i++) {\n workers.push(worker());\n }\n await Promise.all(workers);\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":";;;;;;AAAA,SAAS,YAAY,cAAc,gBAAgB;AACnD,SAAS,eAAe;AAGjB,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAA2C;AACtE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,MAAM;AACpB,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;AAkCA,SAAS,OAAO,MAAc,UAAuC;AAEnE,SACE,QAAQ,IAAI,WAAW,IAAI,EAAE,KAC7B,QAAQ,IAAI,SAAS,IAAI,EAAE,KAC3B;AAEJ;AAEA,IAAM,aAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACV;AASO,SAAS,SAAS,SAA2C;AAClE,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAgB,CAAC;AACrB,MAAI,QAAQ;AACZ,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,KAAK,QAAQ,CAAC;AACpB,QAAI,UAAU;AACZ,UAAI,OAAO,KAAK;AACd,YAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,mBAAS;AACT;AAAA,QACF,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,UAAI,OAAO,KAAK;AACd,mBAAW;AAAA,MACb,WAAW,OAAO,KAAK;AACrB,YAAI,KAAK,KAAK;AACd,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AACtB,YAAI,KAAK,KAAK;AACd,aAAK,KAAK,GAAG;AACb,cAAM,CAAC;AACP,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AAEtB,YAAI,QAAQ,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAI,KAAK,KAAK;AACd,eAAK,KAAK,GAAG;AACb,gBAAM,CAAC;AACP,kBAAQ;AAAA,QACV;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,KAAK,IAAI,SAAS,GAAG;AACtC,QAAI,KAAK,KAAK;AACd,SAAK,KAAK,GAAG;AAAA,EACf;AAEA,MAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAC/B,QAAM,UAAU,KAAK,CAAC,EAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC5C,QAAM,MAAgC,CAAC;AACvC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,IAAI,WAAW,KAAK,IAAI,CAAC,MAAM,GAAI;AACvC,UAAM,MAA8B,CAAC;AACrC,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAI,QAAQ,CAAC,CAAE,IAAI,IAAI,CAAC,KAAK;AAAA,IAC/B;AACA,QAAI,KAAK,GAAG;AAAA,EACd;AACA,SAAO;AACT;AAEO,IAAM,SAAN,MAAa;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,OAAsB,CAAC,GAAG;AAIpC,UAAM,MAAM,WAAW;AACvB,SAAK,SAAS,KAAK,UAAU,OAAO,SAAS,KAAK,IAAI;AACtD,UAAM,MACJ,KAAK,WAAW,OAAO,SAAS,KAAK,IAAI,UAAU;AACrD,SAAK,UAAU,IAAI,QAAQ,QAAQ,EAAE;AACrC,SAAK,SAAS,KAAK,UAAU,OAAO,QAAQ,KAAK,IAAI,UAAU;AAAA,EACjE;AAAA,EAEQ,UAAkC;AACxC,UAAM,IAA4B,EAAE,gBAAgB,mBAAmB;AACvE,QAAI,KAAK,OAAQ,GAAE,WAAW,IAAI,KAAK;AACvC,WAAO;AAAA,EACT;AAAA,EAEQ,OAAe;AACrB,WAAO,GAAG,KAAK,OAAO,WAAW,KAAK,MAAM;AAAA,EAC9C;AAAA,EAEA,MAAc,QACZ,QACA,KACA,MACA,YAAoB,MACR;AACZ,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC5D,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA,SAAS,KAAK,QAAQ;AAAA,QACtB,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,QAC1D,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,KAAK;AAClB,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,aAAa,cAAc,GAAG,oBAAoB,SAAS,IAAI;AAAA,MAC3E;AACA,YAAM,IAAI;AAAA,QACR,4BAA4B,GAAG,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtF;AAAA,IACF;AACA,iBAAa,KAAK;AAElB,QAAI,CAAC,IAAI,IAAI;AACX,UAAIA,QAAO;AACX,UAAI;AACF,QAAAA,QAAO,MAAM,IAAI,KAAK;AAAA,MACxB,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,aAAa,QAAQ,IAAI,MAAM,KAAKA,KAAI,IAAI;AAAA,QACpD,QAAQ,IAAI;AAAA,QACZ,MAAMA;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,WAAW,IAAK,QAAO;AAE/B,UAAM,KAAK,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC9C,QAAI,GAAG,SAAS,kBAAkB,GAAG;AACnC,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OACJ,YACA,OAAsB,CAAC,GACW;AAClC,QAAI;AACJ,QAAI;AAEJ,QAAI,SAAS;AACb,QAAI;AACF,eAAS,WAAW,UAAU,KAAK,SAAS,UAAU,EAAE,OAAO;AAAA,IACjE,QAAQ;AACN,eAAS;AAAA,IACX;AAEA,QAAI,QAAQ;AACV,YAAM,MAAM,QAAQ,UAAU,EAAE,YAAY;AAC5C,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAU,aAAa,YAAY,OAAO;AAC1C,YAAM,KAAK,eAAe,WAAW,GAAG,KAAK;AAC7C,UAAI,QAAQ,OAAO;AACjB,eAAO,KAAK,UAAU,SAAS,IAAI;AAAA,MACrC;AAAA,IACF,OAAO;AACL,gBAAU;AACV,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,UAAM,OAAgC;AAAA,MACpC;AAAA,MACA,cAAc;AAAA,MACd,QAAQ;AAAA,IACV;AACA,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,WAAW,MAAM,IAAO;AAAA,EACpE;AAAA,EAEA,MAAc,UACZ,SACA,MACkC;AAClC,UAAM,SAAS,KAAK;AACpB,UAAM,YAAY,KAAK,aAAa;AACpC,UAAM,cAAc,KAAK,eAAe;AAExC,UAAM,OAAO,SAAS,OAAO;AAC7B,QAAI,KAAK,WAAW,EAAG,OAAM,IAAI,aAAa,cAAc;AAC5D,UAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAE;AAEpC,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,aAAa,KAAK,MAAM,GAAG,CAAC;AAAA,MAC5B,YAAY,KAAK;AAAA,IACnB;AACA,UAAM,UAAU,MAAM,KAAK;AAAA,MACzB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAMA,UAAM,UAA2C,CAAC;AAClD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;AAC/C,cAAQ,KAAK,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC;AAAA,IAC3C;AAEA,QAAI,gBAAgB;AACpB,QAAI,eAAe;AACnB,QAAI,gBAAgB;AACpB,QAAI,YAAY;AAEhB,UAAM,YAAY,OAAO,UAAoC;AAC3D,YAAM,OAAgC;AAAA,QACpC;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AACA,UAAI,OAAQ,MAAK,UAAU;AAC3B,YAAM,SAAS,MAAM,KAAK,QAGvB,QAAQ,GAAG,KAAK,KAAK,CAAC,oBAAoB,MAAM,GAAO;AAC1D,aAAO;AAAA,QACL,UAAU,OAAO,qBAAqB;AAAA,QACtC,SAAS,OAAO,oBAAoB;AAAA,QACpC,MAAM,MAAM;AAAA,MACd;AAAA,IACF;AAEA,UAAM,SAAS,YAA2B;AACxC,aAAO,MAAM;AACX,cAAM,MAAM;AACZ,YAAI,OAAO,QAAQ,OAAQ;AAC3B,cAAM,IAAI,MAAM,UAAU,QAAQ,GAAG,CAAE;AACvC,yBAAiB,EAAE;AACnB,wBAAgB,EAAE;AAClB,yBAAiB,EAAE;AACnB,aAAK,aAAa;AAAA,UAChB;AAAA,UACA,WAAW,KAAK;AAAA,UAChB,kBAAkB;AAAA,UAClB,iBAAiB;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,UAAgC,CAAC;AACvC,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,aAAa,QAAQ,MAAM,GAAG,KAAK;AAC9D,cAAQ,KAAK,OAAO,CAAC;AAAA,IACvB;AACA,UAAM,QAAQ,IAAI,OAAO;AAEzB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,kBAAkB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,IACJ,UACA,OAAmB,CAAC,GACc;AAClC,UAAM,OAAgC,EAAE,SAAS;AACjD,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,QAAI,KAAK,MAAO,MAAK,QAAQ,KAAK;AAClC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,GAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,UAAmD;AACvD,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO;AAChC,QAAI,QAAQ,OAAO,SAAS,YAAY,SAAS,MAAM;AACrD,YAAM,MAAO,KAA2B;AACxC,UAAI,MAAM,QAAQ,GAAG,EAAG,QAAO;AAAA,IACjC;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,MAAM,SACJ,MACA,aACkC;AAClC,UAAM,OAAgC,EAAE,KAAK;AAC7C,QAAI,YAAa,MAAK,cAAc;AACpC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,IAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,SAAS,MAAgD;AAC7D,WAAO,KAAK;AAAA,MACV;AAAA,MACA,GAAG,KAAK,KAAK,CAAC,QAAQ,mBAAmB,IAAI,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBAAyD;AAC7D,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,QAAQ,IAAI,IAAK,OAA0C,CAAC;AAAA,EAC3E;AACF;","names":["text"]}
|
|
File without changes
|