cograph 0.1.14 → 0.1.16
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/README.md +92 -8
- package/dist/{chunk-IHKVOXYV.js → chunk-K7B4PPX2.js} +94 -4
- package/dist/chunk-K7B4PPX2.js.map +1 -0
- package/dist/cli.js +31 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +103 -3
- package/dist/index.js +93 -3
- package/dist/index.js.map +1 -1
- package/dist/{shell-2QXHLD4W.js → shell-VPPIRJSS.js} +387 -21
- package/dist/shell-VPPIRJSS.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-IHKVOXYV.js.map +0 -1
- package/dist/shell-2QXHLD4W.js.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Client,
|
|
4
4
|
CographError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-K7B4PPX2.js";
|
|
6
6
|
import "./chunk-7VVBEUZQ.js";
|
|
7
7
|
|
|
8
8
|
// src/shell.ts
|
|
@@ -59,6 +59,11 @@ function showCommands() {
|
|
|
59
59
|
["/kg delete <name>", "Delete a KG (irreversible)"],
|
|
60
60
|
["/types [query]", "List types in the current KG (with entity counts)"],
|
|
61
61
|
["/type <name>", "Drill into one type \u2014 attributes, relationships, samples"],
|
|
62
|
+
["/type <name> --system", "\u2026also include auto-attached system attributes"],
|
|
63
|
+
["/enrich <Type> <attr> ...", "Plan + run an enrichment job (interactive)"],
|
|
64
|
+
["/enrich watch <job_id>", "Live progress for a running job"],
|
|
65
|
+
["/enrich jobs", "List recent enrichment jobs"],
|
|
66
|
+
["/enrich review <job_id>", "Walk through conflicts and accept/reject"],
|
|
62
67
|
["/login", "Re-authenticate (browser)"],
|
|
63
68
|
["/status", "Show graph stats"],
|
|
64
69
|
["/reset", "Clear the current KG"],
|
|
@@ -376,17 +381,21 @@ async function resolveTypeName(client, kg, rl, input) {
|
|
|
376
381
|
return null;
|
|
377
382
|
}
|
|
378
383
|
async function cmdType(client, kg, rl, input) {
|
|
379
|
-
|
|
380
|
-
|
|
384
|
+
const tokens = splitArgs(input.trim());
|
|
385
|
+
const includeSystem = tokens.includes("--system");
|
|
386
|
+
const nameTokens = tokens.filter((t) => t !== "--system");
|
|
387
|
+
const nameInput = nameTokens.join(" ").trim();
|
|
388
|
+
if (!nameInput) {
|
|
389
|
+
stdout.write(` ${YELLOW}Usage:${RESET} /type <name> [--system]
|
|
381
390
|
`);
|
|
382
391
|
return;
|
|
383
392
|
}
|
|
384
|
-
const name = await resolveTypeName(client, kg, rl,
|
|
393
|
+
const name = await resolveTypeName(client, kg, rl, nameInput);
|
|
385
394
|
if (!name) return;
|
|
386
395
|
const sp = startSpinner(`Loading ${name}...`);
|
|
387
396
|
let usage;
|
|
388
397
|
try {
|
|
389
|
-
usage = await client.typeUsage(kg, name);
|
|
398
|
+
usage = await client.typeUsage(kg, name, { includeSystem });
|
|
390
399
|
} catch (err) {
|
|
391
400
|
sp.stop();
|
|
392
401
|
if (err instanceof CographError) printError(err.message);
|
|
@@ -396,6 +405,9 @@ async function cmdType(client, kg, rl, input) {
|
|
|
396
405
|
sp.stop();
|
|
397
406
|
const total = usage.entity_count;
|
|
398
407
|
const pct = (n) => total > 0 ? `${Math.round(n / total * 100).toString().padStart(3)}%` : " \u2014";
|
|
408
|
+
const relNames = new Set(usage.relationships.map((r) => r.name));
|
|
409
|
+
const attrLitByName = new Map(usage.attributes.map((a) => [a.name, a]));
|
|
410
|
+
const litOnlyAttrs = usage.attributes.filter((a) => !relNames.has(a.name));
|
|
399
411
|
stdout.write("\n");
|
|
400
412
|
stdout.write(
|
|
401
413
|
` ${BOLD}${usage.name}${RESET} ${DIM}${fmtNum(total)} entities${RESET}
|
|
@@ -409,25 +421,25 @@ async function cmdType(client, kg, rl, input) {
|
|
|
409
421
|
stdout.write(` ${DIM}subClassOf ${usage.parent_type}${RESET}
|
|
410
422
|
`);
|
|
411
423
|
}
|
|
412
|
-
if (
|
|
424
|
+
if (litOnlyAttrs.length > 0) {
|
|
413
425
|
stdout.write(
|
|
414
426
|
`
|
|
415
|
-
${BOLD}Attributes (${
|
|
427
|
+
${BOLD}Attributes (${litOnlyAttrs.length})${RESET}
|
|
416
428
|
`
|
|
417
429
|
);
|
|
418
430
|
const nameW = Math.max(
|
|
419
|
-
...
|
|
431
|
+
...litOnlyAttrs.map((a) => a.name.length + 1),
|
|
420
432
|
8
|
|
421
433
|
);
|
|
422
434
|
const typeW = Math.max(
|
|
423
|
-
...
|
|
435
|
+
...litOnlyAttrs.map((a) => a.datatype.length),
|
|
424
436
|
8
|
|
425
437
|
);
|
|
426
438
|
const cntW = Math.max(
|
|
427
|
-
...
|
|
439
|
+
...litOnlyAttrs.map((a) => fmtNum(a.count).length),
|
|
428
440
|
4
|
|
429
441
|
);
|
|
430
|
-
for (const a of
|
|
442
|
+
for (const a of litOnlyAttrs) {
|
|
431
443
|
const dotName = `.${a.name}`;
|
|
432
444
|
stdout.write(
|
|
433
445
|
` ${CYAN}${dotName.padEnd(nameW)}${RESET} ${DIM}${a.datatype.padEnd(typeW)}${RESET} ${fmtNum(a.count).padStart(cntW)} ${DIM}(${pct(a.count)})${RESET}
|
|
@@ -452,8 +464,10 @@ async function cmdType(client, kg, rl, input) {
|
|
|
452
464
|
for (const r of usage.relationships) {
|
|
453
465
|
const dotName = `.${r.name}`;
|
|
454
466
|
const tgt = r.target_type ?? "?";
|
|
467
|
+
const lit = attrLitByName.get(r.name);
|
|
468
|
+
const litNote = lit ? ` ${DIM}(+${fmtNum(lit.count)} ${lit.datatype})${RESET}` : "";
|
|
455
469
|
stdout.write(
|
|
456
|
-
` ${CYAN}${dotName.padEnd(nameW)}${RESET} ${DIM}\u2192${RESET} ${BOLD}${tgt.padEnd(tgtW)}${RESET} ${fmtNum(r.count).padStart(6)} ${DIM}(${pct(r.count)})${RESET}
|
|
470
|
+
` ${CYAN}${dotName.padEnd(nameW)}${RESET} ${DIM}\u2192${RESET} ${BOLD}${tgt.padEnd(tgtW)}${RESET} ${fmtNum(r.count).padStart(6)} ${DIM}(${pct(r.count)})${RESET}${litNote}
|
|
457
471
|
`
|
|
458
472
|
);
|
|
459
473
|
}
|
|
@@ -477,12 +491,321 @@ async function cmdType(client, kg, rl, input) {
|
|
|
477
491
|
}
|
|
478
492
|
stdout.write("\n");
|
|
479
493
|
}
|
|
480
|
-
function
|
|
494
|
+
function lastUriSegment(uri) {
|
|
495
|
+
if (!uri) return uri;
|
|
496
|
+
const hash = uri.lastIndexOf("#");
|
|
497
|
+
if (hash >= 0 && hash < uri.length - 1) return uri.slice(hash + 1);
|
|
498
|
+
const slash = uri.lastIndexOf("/");
|
|
499
|
+
if (slash >= 0 && slash < uri.length - 1) return uri.slice(slash + 1);
|
|
500
|
+
return uri;
|
|
501
|
+
}
|
|
502
|
+
function relativeTime(iso) {
|
|
503
|
+
if (!iso) return "\u2014";
|
|
504
|
+
const t = Date.parse(iso);
|
|
505
|
+
if (!Number.isFinite(t)) return "\u2014";
|
|
506
|
+
const diffMs = Date.now() - t;
|
|
507
|
+
const s = Math.max(0, Math.floor(diffMs / 1e3));
|
|
508
|
+
if (s < 60) return `${s}s ago`;
|
|
509
|
+
const m = Math.floor(s / 60);
|
|
510
|
+
if (m < 60) return `${m}m ago`;
|
|
511
|
+
const h = Math.floor(m / 60);
|
|
512
|
+
if (h < 24) return `${h}h ago`;
|
|
513
|
+
const d = Math.floor(h / 24);
|
|
514
|
+
return `${d}d ago`;
|
|
515
|
+
}
|
|
516
|
+
function progressBar(processed, total, width = 20) {
|
|
517
|
+
if (!total || total <= 0) return "[" + " ".repeat(width) + "]";
|
|
518
|
+
const ratio = Math.max(0, Math.min(1, processed / total));
|
|
519
|
+
const filled = Math.round(ratio * width);
|
|
520
|
+
return "[" + "\u2588".repeat(filled) + "\u2591".repeat(width - filled) + "]";
|
|
521
|
+
}
|
|
522
|
+
function statusColor(status) {
|
|
523
|
+
switch (status) {
|
|
524
|
+
case "applied":
|
|
525
|
+
return GREEN;
|
|
526
|
+
case "failed":
|
|
527
|
+
return RED;
|
|
528
|
+
case "review":
|
|
529
|
+
return YELLOW;
|
|
530
|
+
case "cancelled":
|
|
531
|
+
return DIM;
|
|
532
|
+
default:
|
|
533
|
+
return CYAN;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
async function cmdEnrichRun(client, kg, rl, args) {
|
|
537
|
+
if (args.length < 2) {
|
|
538
|
+
stdout.write(
|
|
539
|
+
` ${YELLOW}Usage:${RESET} /enrich <Type> <attr1> [<attr2> ...]
|
|
540
|
+
`
|
|
541
|
+
);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const typeInput = args[0];
|
|
545
|
+
const attrs = args.slice(1).map((a) => a.replace(/^\./, ""));
|
|
546
|
+
const typeName = await resolveTypeName(client, kg, rl, typeInput);
|
|
547
|
+
if (!typeName) return;
|
|
548
|
+
const tier = "lite";
|
|
549
|
+
const policy = "stage";
|
|
550
|
+
stdout.write(
|
|
551
|
+
`
|
|
552
|
+
${BOLD}Plan:${RESET} enrich ${CYAN}${typeName}${RESET}.${attrs.map((a) => `${CYAN}${a}${RESET}`).join(`, .`)} in ${BOLD}${kg}${RESET} ${DIM}\xB7${RESET} tier: ${tier} ${DIM}\xB7${RESET} policy: ${policy}
|
|
553
|
+
|
|
554
|
+
`
|
|
555
|
+
);
|
|
556
|
+
const sp = startSpinner(`Queueing enrichment for ${typeName}...`);
|
|
557
|
+
let created;
|
|
558
|
+
try {
|
|
559
|
+
created = await client.enrichRun({
|
|
560
|
+
type_name: typeName,
|
|
561
|
+
attributes: attrs,
|
|
562
|
+
tier,
|
|
563
|
+
kg_name: kg,
|
|
564
|
+
conflict_policy: policy
|
|
565
|
+
});
|
|
566
|
+
} catch (err) {
|
|
567
|
+
sp.stop();
|
|
568
|
+
if (err instanceof CographError) printError(err.message);
|
|
569
|
+
else printError(err instanceof Error ? err.message : String(err));
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
sp.stop();
|
|
573
|
+
const cost = (created.estimated_cost_usd ?? 0).toFixed(4);
|
|
574
|
+
stdout.write(
|
|
575
|
+
` ${GREEN}\u2713${RESET} Job queued: ${CYAN_BOLD}${created.job_id}${RESET} ${DIM}\xB7${RESET} estimated cost ${BOLD}$${cost}${RESET} ${DIM}\xB7${RESET} ${fmtNum(created.total_entities ?? 0)} entities
|
|
576
|
+
`
|
|
577
|
+
);
|
|
578
|
+
const watch = (await ask(rl, ` Watch progress? [Y/n]: `)).trim().toLowerCase();
|
|
579
|
+
if (watch === "" || watch === "y" || watch === "yes") {
|
|
580
|
+
await watchJob(client, created.job_id);
|
|
581
|
+
} else {
|
|
582
|
+
stdout.write(
|
|
583
|
+
` ${DIM}Tip: /enrich watch ${created.job_id} to follow it.${RESET}
|
|
584
|
+
`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function watchJob(client, jobId) {
|
|
589
|
+
const startedAt = Date.now();
|
|
590
|
+
let lastJob = null;
|
|
591
|
+
const draw = (job) => {
|
|
592
|
+
const p2 = job.progress;
|
|
593
|
+
const bar = progressBar(p2.processed, p2.total);
|
|
594
|
+
const elapsed = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
595
|
+
const rate = p2.processed / elapsed;
|
|
596
|
+
let etaStr = "\u2014";
|
|
597
|
+
if (rate > 0 && p2.total > p2.processed) {
|
|
598
|
+
const remaining = Math.ceil((p2.total - p2.processed) / rate);
|
|
599
|
+
etaStr = remaining < 60 ? `${remaining}s` : remaining < 3600 ? `${Math.floor(remaining / 60)}m` : `${Math.floor(remaining / 3600)}h`;
|
|
600
|
+
}
|
|
601
|
+
const sc = statusColor(job.status);
|
|
602
|
+
stdout.write(
|
|
603
|
+
`\r\x1B[2K ${sc}${job.status}${RESET} ${bar} ${fmtNum(p2.processed)}/${fmtNum(p2.total)} ${DIM}\xB7${RESET} filled ${GREEN}${fmtNum(p2.filled)}${RESET} ${DIM}\xB7${RESET} verified ${CYAN}${fmtNum(p2.verified)}${RESET} ${DIM}\xB7${RESET} conflicts ${YELLOW}${fmtNum(p2.conflicts)}${RESET} ${DIM}\xB7${RESET} ETA ${etaStr}`
|
|
604
|
+
);
|
|
605
|
+
};
|
|
606
|
+
while (true) {
|
|
607
|
+
let job;
|
|
608
|
+
try {
|
|
609
|
+
job = await client.enrichJob(jobId);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
stdout.write("\r\x1B[2K");
|
|
612
|
+
if (err instanceof CographError) printError(err.message);
|
|
613
|
+
else printError(err instanceof Error ? err.message : String(err));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
lastJob = job;
|
|
617
|
+
draw(job);
|
|
618
|
+
if (job.status !== "running" && job.status !== "queued") break;
|
|
619
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
620
|
+
}
|
|
621
|
+
stdout.write("\n");
|
|
622
|
+
if (!lastJob) return;
|
|
623
|
+
const p = lastJob.progress;
|
|
624
|
+
if (lastJob.status === "review") {
|
|
625
|
+
stdout.write(
|
|
626
|
+
` ${YELLOW}\u2726${RESET} ${fmtNum(p.conflicts)} conflict${p.conflicts === 1 ? "" : "s"} need review. ${DIM}Run${RESET} /enrich review ${lastJob.id}${DIM} to walk through them.${RESET}
|
|
627
|
+
`
|
|
628
|
+
);
|
|
629
|
+
} else if (lastJob.status === "applied") {
|
|
630
|
+
stdout.write(
|
|
631
|
+
` ${GREEN}\u2713${RESET} Applied ${DIM}\xB7${RESET} filled ${fmtNum(p.filled)}, verified ${fmtNum(p.verified)}, skipped ${fmtNum(p.skipped)}
|
|
632
|
+
`
|
|
633
|
+
);
|
|
634
|
+
} else if (lastJob.status === "failed") {
|
|
635
|
+
printError(`Job failed: ${lastJob.error ?? "(no error message)"}`);
|
|
636
|
+
} else if (lastJob.status === "cancelled") {
|
|
637
|
+
stdout.write(` ${DIM}Job cancelled.${RESET}
|
|
638
|
+
`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async function cmdEnrichJobs(client) {
|
|
642
|
+
const sp = startSpinner("Loading enrichment jobs...");
|
|
643
|
+
let jobs;
|
|
644
|
+
try {
|
|
645
|
+
jobs = await client.enrichJobs();
|
|
646
|
+
} catch (err) {
|
|
647
|
+
sp.stop();
|
|
648
|
+
if (err instanceof CographError) printError(err.message);
|
|
649
|
+
else printError(err instanceof Error ? err.message : String(err));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
sp.stop();
|
|
653
|
+
if (jobs.length === 0) {
|
|
654
|
+
stdout.write(` ${DIM}No enrichment jobs yet.${RESET}
|
|
655
|
+
`);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const truncAttrs = (attrs) => {
|
|
659
|
+
const max = 30;
|
|
660
|
+
const joined = attrs.join(", ");
|
|
661
|
+
if (joined.length <= max) return joined;
|
|
662
|
+
return joined.slice(0, max - 1) + "\u2026";
|
|
663
|
+
};
|
|
664
|
+
const rows = jobs.map((j) => ({
|
|
665
|
+
id: j.id,
|
|
666
|
+
type: j.type_name,
|
|
667
|
+
attrs: truncAttrs(j.attributes ?? []),
|
|
668
|
+
status: j.status,
|
|
669
|
+
progress: `${fmtNum(j.progress?.processed ?? 0)}/${fmtNum(j.progress?.total ?? 0)}`,
|
|
670
|
+
created: relativeTime(j.created_at)
|
|
671
|
+
}));
|
|
672
|
+
const w = {
|
|
673
|
+
id: Math.max("ID".length, ...rows.map((r) => r.id.length)),
|
|
674
|
+
type: Math.max("Type".length, ...rows.map((r) => r.type.length)),
|
|
675
|
+
attrs: Math.max("Attrs".length, ...rows.map((r) => r.attrs.length)),
|
|
676
|
+
status: Math.max("Status".length, ...rows.map((r) => r.status.length)),
|
|
677
|
+
progress: Math.max("Progress".length, ...rows.map((r) => r.progress.length))
|
|
678
|
+
};
|
|
679
|
+
stdout.write("\n");
|
|
680
|
+
stdout.write(
|
|
681
|
+
` ${BOLD}${"ID".padEnd(w.id)} ${"Type".padEnd(w.type)} ${"Attrs".padEnd(w.attrs)} ${"Status".padEnd(w.status)} ${"Progress".padEnd(w.progress)} Created${RESET}
|
|
682
|
+
`
|
|
683
|
+
);
|
|
684
|
+
for (const r of rows) {
|
|
685
|
+
const sc = statusColor(r.status);
|
|
686
|
+
stdout.write(
|
|
687
|
+
` ${CYAN}${r.id.padEnd(w.id)}${RESET} ${r.type.padEnd(w.type)} ${DIM}${r.attrs.padEnd(w.attrs)}${RESET} ${sc}${r.status.padEnd(w.status)}${RESET} ${r.progress.padEnd(w.progress)} ${DIM}${r.created}${RESET}
|
|
688
|
+
`
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
stdout.write("\n");
|
|
692
|
+
}
|
|
693
|
+
async function cmdEnrichReview(client, rl, jobId) {
|
|
694
|
+
if (!jobId) {
|
|
695
|
+
stdout.write(` ${YELLOW}Usage:${RESET} /enrich review <job_id>
|
|
696
|
+
`);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const sp = startSpinner(`Loading conflicts for ${jobId}...`);
|
|
700
|
+
let conflicts;
|
|
701
|
+
try {
|
|
702
|
+
conflicts = await client.enrichConflicts(jobId);
|
|
703
|
+
} catch (err) {
|
|
704
|
+
sp.stop();
|
|
705
|
+
if (err instanceof CographError) printError(err.message);
|
|
706
|
+
else printError(err instanceof Error ? err.message : String(err));
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
sp.stop();
|
|
710
|
+
if (conflicts.length === 0) {
|
|
711
|
+
stdout.write(` ${DIM}No conflicts to review.${RESET}
|
|
712
|
+
`);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const decisions = [];
|
|
716
|
+
let acceptAll = false;
|
|
717
|
+
let quitEarly = false;
|
|
718
|
+
for (let i = 0; i < conflicts.length; i++) {
|
|
719
|
+
const c = conflicts[i];
|
|
720
|
+
const entity = lastUriSegment(c.entity_uri);
|
|
721
|
+
const conf = (c.proposed?.confidence ?? 0).toFixed(2);
|
|
722
|
+
stdout.write("\n");
|
|
723
|
+
stdout.write(
|
|
724
|
+
` ${DIM}[${i + 1}/${conflicts.length}]${RESET} ${BOLD}${entity}${RESET}.${CYAN}${c.attribute}${RESET}
|
|
725
|
+
`
|
|
726
|
+
);
|
|
727
|
+
stdout.write(
|
|
728
|
+
` ${DIM}existing \u2192${RESET} ${c.existing_value}
|
|
729
|
+
${DIM}proposed \u2192${RESET} ${BOLD}${c.proposed?.value ?? ""}${RESET} ${DIM}(conf ${conf}, src ${c.proposed?.source ?? "?"})${RESET}
|
|
730
|
+
`
|
|
731
|
+
);
|
|
732
|
+
if (c.proposed?.source_url) {
|
|
733
|
+
stdout.write(` ${DIM}url \u2192${RESET} ${c.proposed.source_url}
|
|
734
|
+
`);
|
|
735
|
+
}
|
|
736
|
+
let decision;
|
|
737
|
+
if (acceptAll) {
|
|
738
|
+
decision = "accept";
|
|
739
|
+
stdout.write(` ${GREEN}auto-accepted${RESET}
|
|
740
|
+
`);
|
|
741
|
+
} else {
|
|
742
|
+
const ans = (await ask(
|
|
743
|
+
rl,
|
|
744
|
+
` [a]ccept / [r]eject / [s]kip / [A]ccept all remaining / [q]uit (saves progress) [s]: `
|
|
745
|
+
)).trim();
|
|
746
|
+
if (ans === "A") {
|
|
747
|
+
acceptAll = true;
|
|
748
|
+
decision = "accept";
|
|
749
|
+
} else if (ans === "a") {
|
|
750
|
+
decision = "accept";
|
|
751
|
+
} else if (ans === "r") {
|
|
752
|
+
decision = "reject";
|
|
753
|
+
} else if (ans === "q") {
|
|
754
|
+
quitEarly = true;
|
|
755
|
+
break;
|
|
756
|
+
} else {
|
|
757
|
+
decision = "skip";
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
decisions.push({ ...c, decision });
|
|
761
|
+
}
|
|
762
|
+
if (quitEarly) {
|
|
763
|
+
if (decisions.length === 0) {
|
|
764
|
+
stdout.write(` ${DIM}No decisions made \u2014 nothing to save.${RESET}
|
|
765
|
+
`);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const save = (await ask(rl, ` Save ${decisions.length} decision(s) so far? [Y/n]: `)).trim().toLowerCase();
|
|
769
|
+
if (save !== "" && save !== "y" && save !== "yes") {
|
|
770
|
+
stdout.write(` ${DIM}Discarded.${RESET}
|
|
771
|
+
`);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (decisions.length === 0) {
|
|
776
|
+
stdout.write(` ${DIM}No decisions to apply.${RESET}
|
|
777
|
+
`);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
const sp2 = startSpinner(`Applying ${decisions.length} decision(s)...`);
|
|
781
|
+
try {
|
|
782
|
+
const res = await client.enrichApply(jobId, decisions);
|
|
783
|
+
sp2.stop();
|
|
784
|
+
stdout.write(
|
|
785
|
+
` ${GREEN}\u2713${RESET} Applied ${BOLD}${fmtNum(res.applied)}${RESET} change${res.applied === 1 ? "" : "s"}.
|
|
786
|
+
`
|
|
787
|
+
);
|
|
788
|
+
} catch (err) {
|
|
789
|
+
sp2.stop();
|
|
790
|
+
if (err instanceof CographError) printError(err.message);
|
|
791
|
+
else printError(err instanceof Error ? err.message : String(err));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function urlHost(url) {
|
|
795
|
+
try {
|
|
796
|
+
return new URL(url).host;
|
|
797
|
+
} catch {
|
|
798
|
+
return url.replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function makePrompt(kg, triples, mode = "cloud", baseUrl) {
|
|
481
802
|
const kgPart = `${DIM}(${kg})${RESET}`;
|
|
482
|
-
|
|
483
|
-
|
|
803
|
+
const triplePart = triples > 0 ? `${DIM}[${fmtNum(triples)}]${RESET} ` : "";
|
|
804
|
+
if (mode === "self-hosted" && baseUrl) {
|
|
805
|
+
const host = urlHost(baseUrl);
|
|
806
|
+
return ` ${CYAN_BOLD}cograph${RESET}${DIM}@${host}${RESET} ${kgPart} ${triplePart}${CYAN_BOLD}\u25B8${RESET} `;
|
|
484
807
|
}
|
|
485
|
-
return ` ${CYAN_BOLD}cograph${RESET} ${kgPart} ${CYAN_BOLD}\u25B8${RESET} `;
|
|
808
|
+
return ` ${CYAN_BOLD}cograph${RESET} ${kgPart} ${triplePart}${CYAN_BOLD}\u25B8${RESET} `;
|
|
486
809
|
}
|
|
487
810
|
function splitArgs(s) {
|
|
488
811
|
const out = [];
|
|
@@ -507,8 +830,21 @@ function splitArgs(s) {
|
|
|
507
830
|
return out;
|
|
508
831
|
}
|
|
509
832
|
async function runShell(opts) {
|
|
510
|
-
|
|
511
|
-
|
|
833
|
+
const CLOUD_DEFAULT = "https://api.cograph.cloud";
|
|
834
|
+
const envUrl = process.env.COGRAPH_API_URL || process.env.OMNIX_API_URL;
|
|
835
|
+
const envIsSelfHosted = !!envUrl && envUrl !== CLOUD_DEFAULT;
|
|
836
|
+
const selfHostedHint = !!opts.local || !!opts.noLogin || envIsSelfHosted;
|
|
837
|
+
let client = opts.local ? new Client({ baseUrl: "http://localhost:8000", tenant: "default" }) : selfHostedHint ? new Client({ tenant: "default" }) : new Client();
|
|
838
|
+
const health = await client.healthCheck();
|
|
839
|
+
if (!health.ok) {
|
|
840
|
+
printError(
|
|
841
|
+
`Could not reach ${health.url}. Is the server running?`
|
|
842
|
+
);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const selfHosted = selfHostedHint || !health.requiresAuth;
|
|
846
|
+
const mode = selfHosted ? "self-hosted" : "cloud";
|
|
847
|
+
if (!selfHosted && health.requiresAuth && !client.apiKey) {
|
|
512
848
|
stdout.write(
|
|
513
849
|
`
|
|
514
850
|
${DIM}Not signed in \u2014 opening your browser to log in...${RESET}
|
|
@@ -528,6 +864,13 @@ async function runShell(opts) {
|
|
|
528
864
|
terminal: true
|
|
529
865
|
});
|
|
530
866
|
showBanner();
|
|
867
|
+
if (selfHosted) {
|
|
868
|
+
stdout.write(
|
|
869
|
+
`${DIM} Self-hosted mode \xB7 ${client.baseUrl} \xB7 tenant=${client.tenant}${RESET}
|
|
870
|
+
|
|
871
|
+
`
|
|
872
|
+
);
|
|
873
|
+
}
|
|
531
874
|
let kg = opts.kg;
|
|
532
875
|
if (!kg) {
|
|
533
876
|
const picked = await selectKg(client, rl);
|
|
@@ -564,7 +907,7 @@ async function runShell(opts) {
|
|
|
564
907
|
while (running) {
|
|
565
908
|
let line;
|
|
566
909
|
try {
|
|
567
|
-
line = (await ask(rl, makePrompt(kg, triples))).trim();
|
|
910
|
+
line = (await ask(rl, makePrompt(kg, triples, mode, client.baseUrl))).trim();
|
|
568
911
|
} catch {
|
|
569
912
|
break;
|
|
570
913
|
}
|
|
@@ -594,6 +937,29 @@ async function runShell(opts) {
|
|
|
594
937
|
} else if (line.startsWith("/type ") || line === "/type") {
|
|
595
938
|
const arg = line === "/type" ? "" : line.slice("/type ".length);
|
|
596
939
|
await cmdType(client, kg, rl, arg);
|
|
940
|
+
} else if (line === "/enrich" || line.startsWith("/enrich ")) {
|
|
941
|
+
const args = splitArgs(line.slice("/enrich".length).trim());
|
|
942
|
+
if (args.length === 0) {
|
|
943
|
+
stdout.write(
|
|
944
|
+
` ${YELLOW}Usage:${RESET} /enrich <Type> <attr> ... | /enrich watch <id> | /enrich jobs | /enrich review <id>
|
|
945
|
+
`
|
|
946
|
+
);
|
|
947
|
+
} else if (args[0] === "jobs") {
|
|
948
|
+
await cmdEnrichJobs(client);
|
|
949
|
+
} else if (args[0] === "watch") {
|
|
950
|
+
const jid = args[1];
|
|
951
|
+
if (!jid) {
|
|
952
|
+
stdout.write(` ${YELLOW}Usage:${RESET} /enrich watch <job_id>
|
|
953
|
+
`);
|
|
954
|
+
} else {
|
|
955
|
+
await watchJob(client, jid);
|
|
956
|
+
}
|
|
957
|
+
} else if (args[0] === "review") {
|
|
958
|
+
await cmdEnrichReview(client, rl, args[1] ?? "");
|
|
959
|
+
} else {
|
|
960
|
+
await cmdEnrichRun(client, kg, rl, args);
|
|
961
|
+
await refresh();
|
|
962
|
+
}
|
|
597
963
|
} else if (line === "/status") {
|
|
598
964
|
await cmdStatus(client, kg);
|
|
599
965
|
await refresh();
|
|
@@ -718,7 +1084,7 @@ async function runShell(opts) {
|
|
|
718
1084
|
}
|
|
719
1085
|
} else if (line.startsWith("/")) {
|
|
720
1086
|
stdout.write(
|
|
721
|
-
` ${YELLOW}Unknown command.${RESET} Try /ingest, /ask, /kg, /types, /type, /login, /status, /reset, /help, /quit
|
|
1087
|
+
` ${YELLOW}Unknown command.${RESET} Try /ingest, /ask, /kg, /types, /type, /enrich, /login, /status, /reset, /help, /quit
|
|
722
1088
|
`
|
|
723
1089
|
);
|
|
724
1090
|
} else {
|
|
@@ -734,4 +1100,4 @@ async function runShell(opts) {
|
|
|
734
1100
|
export {
|
|
735
1101
|
runShell
|
|
736
1102
|
};
|
|
737
|
-
//# sourceMappingURL=shell-
|
|
1103
|
+
//# sourceMappingURL=shell-VPPIRJSS.js.map
|