@runcontext/ui 0.5.10 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +381 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +381 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/static/app.js +16 -0
- package/static/setup.css +1069 -47
- package/static/uxd.css +286 -1
- package/static/setup.js +0 -1
package/dist/index.cjs
CHANGED
|
@@ -364,7 +364,6 @@ var _crypto = require('crypto');
|
|
|
364
364
|
var _util = require('util');
|
|
365
365
|
|
|
366
366
|
|
|
367
|
-
|
|
368
367
|
// src/events.ts
|
|
369
368
|
var _events = require('events');
|
|
370
369
|
|
|
@@ -399,6 +398,10 @@ function resolveCliBin() {
|
|
|
399
398
|
}
|
|
400
399
|
} catch (e10) {
|
|
401
400
|
}
|
|
401
|
+
const cwdCli = _path.join.call(void 0, process.cwd(), "packages", "cli", "dist", "index.js");
|
|
402
|
+
if (_fs.existsSync.call(void 0, cwdCli)) {
|
|
403
|
+
return { cmd: process.execPath, prefix: [cwdCli] };
|
|
404
|
+
}
|
|
402
405
|
if (process.argv[1] && _fs.existsSync.call(void 0, process.argv[1])) {
|
|
403
406
|
return { cmd: process.execPath, prefix: [process.argv[1]] };
|
|
404
407
|
}
|
|
@@ -469,35 +472,47 @@ function pipelineRoutes(rootDir, contextDir) {
|
|
|
469
472
|
if (!run) return c.json({ error: "Not found" }, 404);
|
|
470
473
|
return c.json(run);
|
|
471
474
|
});
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const configRaw = _fs.readFileSync.call(void 0, configPath, "utf-8");
|
|
477
|
-
const config = _yaml.parse.call(void 0, configRaw);
|
|
478
|
-
const dataSources = _optionalChain([config, 'optionalAccess', _2 => _2.data_sources]);
|
|
479
|
-
if (dataSources) {
|
|
480
|
-
const firstKey = Object.keys(dataSources)[0];
|
|
481
|
-
if (firstKey) {
|
|
482
|
-
connection = dataSources[firstKey].connection || dataSources[firstKey].path;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
} catch (e11) {
|
|
475
|
+
let mcpProcess = null;
|
|
476
|
+
app.post("/api/mcp/start", (c) => {
|
|
477
|
+
if (mcpProcess && !mcpProcess.killed) {
|
|
478
|
+
return c.json({ ok: true, status: "already_running" });
|
|
486
479
|
}
|
|
487
480
|
const cli = resolveCliBin();
|
|
481
|
+
mcpProcess = _child_process.spawn.call(void 0, cli.cmd, [...cli.prefix, "serve"], {
|
|
482
|
+
cwd: rootDir,
|
|
483
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
484
|
+
detached: false,
|
|
485
|
+
env: { ...process.env, NODE_OPTIONS: "--no-deprecation" }
|
|
486
|
+
});
|
|
487
|
+
mcpProcess.on("exit", () => {
|
|
488
|
+
mcpProcess = null;
|
|
489
|
+
});
|
|
490
|
+
mcpProcess.on("error", () => {
|
|
491
|
+
mcpProcess = null;
|
|
492
|
+
});
|
|
493
|
+
return c.json({ ok: true, status: "started" });
|
|
494
|
+
});
|
|
495
|
+
app.post("/api/mcp/stop", (c) => {
|
|
496
|
+
if (mcpProcess && !mcpProcess.killed) {
|
|
497
|
+
mcpProcess.kill();
|
|
498
|
+
mcpProcess = null;
|
|
499
|
+
}
|
|
500
|
+
return c.json({ ok: true, status: "stopped" });
|
|
501
|
+
});
|
|
502
|
+
app.get("/api/mcp/status", (c) => {
|
|
503
|
+
const running = mcpProcess !== null && !mcpProcess.killed;
|
|
504
|
+
return c.json({ running });
|
|
505
|
+
});
|
|
506
|
+
app.get("/api/mcp-config", (c) => {
|
|
507
|
+
const cli = resolveCliBin();
|
|
508
|
+
const absRoot = _path.resolve.call(void 0, rootDir);
|
|
488
509
|
const mcpServers = {
|
|
489
510
|
runcontext: {
|
|
490
511
|
command: cli.cmd,
|
|
491
512
|
args: [...cli.prefix, "serve"],
|
|
492
|
-
cwd:
|
|
513
|
+
cwd: absRoot
|
|
493
514
|
}
|
|
494
515
|
};
|
|
495
|
-
if (connection) {
|
|
496
|
-
mcpServers["runcontext-db"] = {
|
|
497
|
-
command: "npx",
|
|
498
|
-
args: ["--yes", "@runcontext/db", "--url", connection]
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
516
|
return c.json({ mcpServers });
|
|
502
517
|
});
|
|
503
518
|
return app;
|
|
@@ -521,10 +536,16 @@ function buildCliArgs(stage, dataSource) {
|
|
|
521
536
|
if (dataSource) args.push("--source", dataSource);
|
|
522
537
|
return args;
|
|
523
538
|
}
|
|
524
|
-
case "verify":
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return
|
|
539
|
+
case "verify": {
|
|
540
|
+
const args = ["verify"];
|
|
541
|
+
if (dataSource) args.push("--source", dataSource);
|
|
542
|
+
return args;
|
|
543
|
+
}
|
|
544
|
+
case "autofix": {
|
|
545
|
+
const args = ["fix"];
|
|
546
|
+
if (dataSource) args.push("--source", dataSource);
|
|
547
|
+
return args;
|
|
548
|
+
}
|
|
528
549
|
case "agent-instructions":
|
|
529
550
|
return ["build"];
|
|
530
551
|
}
|
|
@@ -550,7 +571,7 @@ async function executePipeline(run, rootDir, contextDir, dataSource, sessionId)
|
|
|
550
571
|
const cli = resolveCliBin();
|
|
551
572
|
const { stdout } = await execFile(cli.cmd, [...cli.prefix, ...cliArgs], {
|
|
552
573
|
cwd: rootDir,
|
|
553
|
-
timeout:
|
|
574
|
+
timeout: 3e5,
|
|
554
575
|
env: {
|
|
555
576
|
...process.env,
|
|
556
577
|
NODE_OPTIONS: "--max-old-space-size=4096 --no-deprecation"
|
|
@@ -582,8 +603,10 @@ async function executePipeline(run, rootDir, contextDir, dataSource, sessionId)
|
|
|
582
603
|
continue;
|
|
583
604
|
}
|
|
584
605
|
stage.status = "error";
|
|
585
|
-
|
|
606
|
+
const errDetail = execErr.stderr || (err instanceof Error ? err.message : String(err));
|
|
607
|
+
stage.error = errDetail;
|
|
586
608
|
stage.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
609
|
+
console.error(`[pipeline] Stage ${stage.stage} failed:`, errDetail);
|
|
587
610
|
if (sessionId) {
|
|
588
611
|
setupBus.emitEvent({
|
|
589
612
|
type: "pipeline:stage",
|
|
@@ -603,35 +626,235 @@ async function executePipeline(run, rootDir, contextDir, dataSource, sessionId)
|
|
|
603
626
|
|
|
604
627
|
|
|
605
628
|
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
var execFile2 = _util.promisify.call(void 0, _child_process.execFile);
|
|
632
|
+
function detectTier(contextDir, sourceName) {
|
|
633
|
+
const rulesPath = path4.join(contextDir, "rules", `${sourceName}.rules.yaml`);
|
|
634
|
+
const modelPath = path4.join(contextDir, "models", `${sourceName}.osi.yaml`);
|
|
635
|
+
if (fs4.existsSync(rulesPath)) {
|
|
636
|
+
try {
|
|
637
|
+
const rules = _yaml.parse.call(void 0, fs4.readFileSync(rulesPath, "utf-8"));
|
|
638
|
+
const realQueries = (_optionalChain([rules, 'optionalAccess', _2 => _2.golden_queries]) || []).filter(
|
|
639
|
+
(q) => q.sql && !q.sql.includes("TODO") && !q.sql.includes("table_name") && q.question && !q.question.includes("TODO")
|
|
640
|
+
);
|
|
641
|
+
const realGuardrails = (_optionalChain([rules, 'optionalAccess', _3 => _3.guardrail_filters]) || _optionalChain([rules, 'optionalAccess', _4 => _4.guardrails]) || []).filter(
|
|
642
|
+
(g) => g.name && !g.name.includes("TODO")
|
|
643
|
+
);
|
|
644
|
+
if (realQueries.length >= 3 && realGuardrails.length >= 1) return "gold";
|
|
645
|
+
} catch (e11) {
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (fs4.existsSync(modelPath)) {
|
|
649
|
+
try {
|
|
650
|
+
const model = _yaml.parse.call(void 0, fs4.readFileSync(modelPath, "utf-8"));
|
|
651
|
+
let datasets = _optionalChain([model, 'optionalAccess', _5 => _5.tables]) || _optionalChain([model, 'optionalAccess', _6 => _6.models]) || [];
|
|
652
|
+
if (datasets.length === 0 && Array.isArray(_optionalChain([model, 'optionalAccess', _7 => _7.semantic_model]))) {
|
|
653
|
+
for (const sm of model.semantic_model) {
|
|
654
|
+
if (Array.isArray(sm.datasets)) datasets.push(...sm.datasets);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
let fieldsWithSamples = 0;
|
|
658
|
+
for (const d of datasets) {
|
|
659
|
+
for (const f of d.columns || d.fields || []) {
|
|
660
|
+
if (_optionalChain([f, 'access', _8 => _8.sample_values, 'optionalAccess', _9 => _9.length]) > 0) fieldsWithSamples++;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (fieldsWithSamples >= 2) return "silver";
|
|
664
|
+
} catch (e12) {
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return "bronze";
|
|
668
|
+
}
|
|
669
|
+
function countTablesAndColumns(contextDir, sourceName) {
|
|
670
|
+
const modelPath = path4.join(contextDir, "models", `${sourceName}.osi.yaml`);
|
|
671
|
+
if (!fs4.existsSync(modelPath)) return { tables: 0, columns: 0 };
|
|
672
|
+
try {
|
|
673
|
+
const model = _yaml.parse.call(void 0, fs4.readFileSync(modelPath, "utf-8"));
|
|
674
|
+
let datasets = _optionalChain([model, 'optionalAccess', _10 => _10.tables]) || _optionalChain([model, 'optionalAccess', _11 => _11.models]) || [];
|
|
675
|
+
if (datasets.length === 0 && Array.isArray(_optionalChain([model, 'optionalAccess', _12 => _12.semantic_model]))) {
|
|
676
|
+
for (const sm of model.semantic_model) {
|
|
677
|
+
if (Array.isArray(sm.datasets)) datasets.push(...sm.datasets);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
const columns = datasets.reduce((sum, t) => sum + (_optionalChain([t, 'access', _13 => _13.columns, 'optionalAccess', _14 => _14.length]) || _optionalChain([t, 'access', _15 => _15.fields, 'optionalAccess', _16 => _16.length]) || 0), 0);
|
|
681
|
+
return { tables: datasets.length, columns };
|
|
682
|
+
} catch (e13) {
|
|
683
|
+
return { tables: 0, columns: 0 };
|
|
684
|
+
}
|
|
685
|
+
}
|
|
606
686
|
function productsRoutes(contextDir) {
|
|
607
687
|
const app = new (0, _hono.Hono)();
|
|
608
688
|
app.get("/api/products", (c) => {
|
|
609
|
-
|
|
610
|
-
if (!fs4.existsSync(productsDir)) {
|
|
689
|
+
if (!fs4.existsSync(contextDir)) {
|
|
611
690
|
return c.json([]);
|
|
612
691
|
}
|
|
613
692
|
const products = [];
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
693
|
+
const briefFiles = fs4.readdirSync(contextDir).filter((f) => f.endsWith(".context-brief.yaml"));
|
|
694
|
+
for (const briefFile of briefFiles) {
|
|
695
|
+
const briefPath = path4.join(contextDir, briefFile);
|
|
696
|
+
try {
|
|
697
|
+
const brief = _yaml.parse.call(void 0, fs4.readFileSync(briefPath, "utf-8"));
|
|
698
|
+
const name = _optionalChain([brief, 'optionalAccess', _17 => _17.product_name]) || briefFile.replace(".context-brief.yaml", "");
|
|
699
|
+
const modelsDir = path4.join(contextDir, "models");
|
|
700
|
+
let sourceName = "";
|
|
701
|
+
const briefSource = _optionalChain([brief, 'optionalAccess', _18 => _18.data_sources, 'optionalAccess', _19 => _19[0], 'optionalAccess', _20 => _20.name]) || _optionalChain([brief, 'optionalAccess', _21 => _21.data_source]) || "";
|
|
702
|
+
if (briefSource && fs4.existsSync(path4.join(modelsDir, `${briefSource}.osi.yaml`))) {
|
|
703
|
+
sourceName = briefSource;
|
|
704
|
+
} else if (fs4.existsSync(modelsDir)) {
|
|
705
|
+
const modelFiles = fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".osi.yaml"));
|
|
706
|
+
if (modelFiles.length > 0) {
|
|
707
|
+
sourceName = modelFiles[0].replace(".osi.yaml", "");
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const { tables, columns } = sourceName ? countTablesAndColumns(contextDir, sourceName) : { tables: 0, columns: 0 };
|
|
711
|
+
const tier = sourceName ? detectTier(contextDir, sourceName) : "bronze";
|
|
712
|
+
products.push({
|
|
713
|
+
name,
|
|
714
|
+
description: _optionalChain([brief, 'optionalAccess', _22 => _22.description]),
|
|
715
|
+
sensitivity: _optionalChain([brief, 'optionalAccess', _23 => _23.sensitivity]),
|
|
716
|
+
tier,
|
|
717
|
+
tables,
|
|
718
|
+
columns,
|
|
719
|
+
hasBrief: true
|
|
720
|
+
});
|
|
721
|
+
} catch (e14) {
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
725
|
+
const unique = products.filter((p) => {
|
|
726
|
+
if (seen.has(p.name)) return false;
|
|
727
|
+
seen.add(p.name);
|
|
728
|
+
return true;
|
|
617
729
|
});
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
730
|
+
return c.json(unique);
|
|
731
|
+
});
|
|
732
|
+
app.get("/api/products/:name/detail", (c) => {
|
|
733
|
+
const modelsDir = path4.join(contextDir, "models");
|
|
734
|
+
const rulesDir = path4.join(contextDir, "rules");
|
|
735
|
+
const govDir = path4.join(contextDir, "governance");
|
|
736
|
+
const glossaryDir = path4.join(contextDir, "glossary");
|
|
737
|
+
const ownersDir = path4.join(contextDir, "owners");
|
|
738
|
+
const modelFiles = fs4.existsSync(modelsDir) ? fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".osi.yaml")) : [];
|
|
739
|
+
const sourceName = modelFiles.length > 0 ? modelFiles[0].replace(".osi.yaml", "") : "";
|
|
740
|
+
let tables = [];
|
|
741
|
+
let modelYaml = "";
|
|
742
|
+
if (sourceName) {
|
|
743
|
+
const modelPath = path4.join(modelsDir, modelFiles[0]);
|
|
744
|
+
modelYaml = fs4.readFileSync(modelPath, "utf-8");
|
|
745
|
+
try {
|
|
746
|
+
const model = _yaml.parse.call(void 0, modelYaml);
|
|
747
|
+
let datasets = _optionalChain([model, 'optionalAccess', _24 => _24.tables]) || _optionalChain([model, 'optionalAccess', _25 => _25.models]) || [];
|
|
748
|
+
if (datasets.length === 0 && Array.isArray(_optionalChain([model, 'optionalAccess', _26 => _26.semantic_model]))) {
|
|
749
|
+
for (const sm of model.semantic_model) {
|
|
750
|
+
if (Array.isArray(sm.datasets)) datasets.push(...sm.datasets);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
tables = datasets.map((d) => {
|
|
754
|
+
const fields = d.columns || d.fields || [];
|
|
755
|
+
return {
|
|
756
|
+
name: d.name,
|
|
757
|
+
description: d.description || "",
|
|
758
|
+
fields: fields.map((f) => ({
|
|
759
|
+
name: f.name,
|
|
760
|
+
type: f.type || f.data_type || "",
|
|
761
|
+
description: f.description || "",
|
|
762
|
+
sampleValues: f.sample_values || [],
|
|
763
|
+
semanticRole: f.semantic_role || ""
|
|
764
|
+
}))
|
|
765
|
+
};
|
|
766
|
+
});
|
|
767
|
+
} catch (e15) {
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
let rules = {};
|
|
771
|
+
let rulesYaml = "";
|
|
772
|
+
if (sourceName) {
|
|
773
|
+
const rulesPath = path4.join(rulesDir, `${sourceName}.rules.yaml`);
|
|
774
|
+
if (fs4.existsSync(rulesPath)) {
|
|
775
|
+
rulesYaml = fs4.readFileSync(rulesPath, "utf-8");
|
|
776
|
+
try {
|
|
777
|
+
rules = _yaml.parse.call(void 0, rulesYaml) || {};
|
|
778
|
+
} catch (e16) {
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
let governance = {};
|
|
783
|
+
let govYaml = "";
|
|
784
|
+
if (sourceName) {
|
|
785
|
+
const govPath = path4.join(govDir, `${sourceName}.governance.yaml`);
|
|
786
|
+
if (fs4.existsSync(govPath)) {
|
|
787
|
+
govYaml = fs4.readFileSync(govPath, "utf-8");
|
|
788
|
+
try {
|
|
789
|
+
governance = _yaml.parse.call(void 0, govYaml) || {};
|
|
790
|
+
} catch (e17) {
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const glossary = [];
|
|
795
|
+
if (fs4.existsSync(glossaryDir)) {
|
|
796
|
+
for (const f of fs4.readdirSync(glossaryDir).filter((f2) => f2.endsWith(".term.yaml"))) {
|
|
797
|
+
try {
|
|
798
|
+
const term = _yaml.parse.call(void 0, fs4.readFileSync(path4.join(glossaryDir, f), "utf-8"));
|
|
799
|
+
if (term) glossary.push(term);
|
|
800
|
+
} catch (e18) {
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
const owners = [];
|
|
805
|
+
if (fs4.existsSync(ownersDir)) {
|
|
806
|
+
for (const f of fs4.readdirSync(ownersDir).filter((f2) => f2.endsWith(".owner.yaml"))) {
|
|
625
807
|
try {
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
} catch (e12) {
|
|
808
|
+
const owner = _yaml.parse.call(void 0, fs4.readFileSync(path4.join(ownersDir, f), "utf-8"));
|
|
809
|
+
if (owner) owners.push(owner);
|
|
810
|
+
} catch (e19) {
|
|
630
811
|
}
|
|
631
812
|
}
|
|
632
|
-
products.push({ name, description, sensitivity, hasBrief });
|
|
633
813
|
}
|
|
634
|
-
return c.json(
|
|
814
|
+
return c.json({
|
|
815
|
+
tables,
|
|
816
|
+
rules: {
|
|
817
|
+
joinRules: rules.join_rules || [],
|
|
818
|
+
goldenQueries: rules.golden_queries || [],
|
|
819
|
+
guardrails: rules.guardrail_filters || rules.guardrails || [],
|
|
820
|
+
grainStatements: rules.grain_statements || []
|
|
821
|
+
},
|
|
822
|
+
governance,
|
|
823
|
+
glossary,
|
|
824
|
+
owners,
|
|
825
|
+
yaml: {
|
|
826
|
+
model: modelYaml.slice(0, 5e4),
|
|
827
|
+
rules: rulesYaml.slice(0, 2e4),
|
|
828
|
+
governance: govYaml.slice(0, 1e4)
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
app.get("/api/tier", async (c) => {
|
|
833
|
+
const cwdCli = path4.join(process.cwd(), "packages", "cli", "dist", "index.js");
|
|
834
|
+
const cliPath = fs4.existsSync(cwdCli) ? cwdCli : null;
|
|
835
|
+
if (!cliPath) return c.json({ tier: "unknown", output: "CLI not found" });
|
|
836
|
+
try {
|
|
837
|
+
const { stdout } = await execFile2(process.execPath, [cliPath, "tier"], {
|
|
838
|
+
cwd: process.cwd(),
|
|
839
|
+
timeout: 15e3,
|
|
840
|
+
env: { ...process.env, NODE_OPTIONS: "--no-deprecation" }
|
|
841
|
+
});
|
|
842
|
+
const tierMatch = stdout.match(/(BRONZE|SILVER|GOLD)/i);
|
|
843
|
+
return c.json({ tier: tierMatch ? tierMatch[1].toLowerCase() : "unknown", output: stdout });
|
|
844
|
+
} catch (err) {
|
|
845
|
+
const stdout = _optionalChain([err, 'optionalAccess', _27 => _27.stdout]) || "";
|
|
846
|
+
const tierMatch = stdout.match(/(BRONZE|SILVER|GOLD)/i);
|
|
847
|
+
return c.json({ tier: tierMatch ? tierMatch[1].toLowerCase() : "unknown", output: stdout || err.message });
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
app.get("/api/agent-instructions", (c) => {
|
|
851
|
+
const distInstructions = path4.join(process.cwd(), "dist", "AGENT_INSTRUCTIONS.md");
|
|
852
|
+
const cliInstructions = path4.join(process.cwd(), "packages", "cli", "assets", "AGENT_INSTRUCTIONS.md");
|
|
853
|
+
const instrPath = fs4.existsSync(distInstructions) ? distInstructions : fs4.existsSync(cliInstructions) ? cliInstructions : null;
|
|
854
|
+
if (!instrPath) {
|
|
855
|
+
return c.json({ instructions: null, error: "Agent instructions not found" });
|
|
856
|
+
}
|
|
857
|
+
return c.json({ instructions: fs4.readFileSync(instrPath, "utf-8") });
|
|
635
858
|
});
|
|
636
859
|
return app;
|
|
637
860
|
}
|
|
@@ -675,7 +898,7 @@ function authRoutes(rootDir) {
|
|
|
675
898
|
if (databases2.length > 0) {
|
|
676
899
|
return c.json({ ok: true, provider: providerId, databases: databases2 });
|
|
677
900
|
}
|
|
678
|
-
} catch (
|
|
901
|
+
} catch (e20) {
|
|
679
902
|
}
|
|
680
903
|
const result = await provider.authenticate();
|
|
681
904
|
if (!result.ok) {
|
|
@@ -694,7 +917,7 @@ function authRoutes(rootDir) {
|
|
|
694
917
|
if (!provider) {
|
|
695
918
|
return c.json({ error: `Unknown provider: ${providerId}` }, 400);
|
|
696
919
|
}
|
|
697
|
-
let token = _optionalChain([database, 'access',
|
|
920
|
+
let token = _optionalChain([database, 'access', _28 => _28.metadata, 'optionalAccess', _29 => _29.token]);
|
|
698
921
|
if (!token) {
|
|
699
922
|
const authResult = await provider.authenticate();
|
|
700
923
|
if (!authResult.ok) {
|
|
@@ -735,10 +958,16 @@ function authRoutes(rootDir) {
|
|
|
735
958
|
if (fs5.existsSync(configPath)) {
|
|
736
959
|
try {
|
|
737
960
|
config = _nullishCoalesce(_yaml.parse.call(void 0, fs5.readFileSync(configPath, "utf-8")), () => ( {}));
|
|
738
|
-
} catch (
|
|
961
|
+
} catch (e21) {
|
|
739
962
|
}
|
|
740
963
|
}
|
|
741
|
-
|
|
964
|
+
const existingSources = _nullishCoalesce(config.data_sources, () => ( {}));
|
|
965
|
+
for (const [key, val] of Object.entries(existingSources)) {
|
|
966
|
+
if (val && typeof val === "object" && !val.connection && !val.path) {
|
|
967
|
+
delete existingSources[key];
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
config.data_sources = existingSources;
|
|
742
971
|
const sourceName = (database.name || database.database || "default").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
743
972
|
config.data_sources[sourceName] = { adapter: database.adapter, connection: connStr };
|
|
744
973
|
fs5.writeFileSync(configPath, _yaml.stringify.call(void 0, config), "utf-8");
|
|
@@ -755,7 +984,7 @@ function authRoutes(rootDir) {
|
|
|
755
984
|
|
|
756
985
|
|
|
757
986
|
|
|
758
|
-
var
|
|
987
|
+
var execFile3 = _util.promisify.call(void 0, _child_process.execFile);
|
|
759
988
|
function suggestBriefRoutes(rootDir) {
|
|
760
989
|
const app = new (0, _hono.Hono)();
|
|
761
990
|
app.post("/api/suggest-brief", async (c) => {
|
|
@@ -778,20 +1007,20 @@ function suggestBriefRoutes(rootDir) {
|
|
|
778
1007
|
let ownerEmail = "";
|
|
779
1008
|
let ownerTeam = "";
|
|
780
1009
|
try {
|
|
781
|
-
const { stdout: name } = await
|
|
1010
|
+
const { stdout: name } = await execFile3("git", ["config", "user.name"], {
|
|
782
1011
|
cwd: rootDir,
|
|
783
1012
|
timeout: 3e3
|
|
784
1013
|
});
|
|
785
1014
|
ownerName = name.trim();
|
|
786
|
-
} catch (
|
|
1015
|
+
} catch (e22) {
|
|
787
1016
|
}
|
|
788
1017
|
try {
|
|
789
|
-
const { stdout: email } = await
|
|
1018
|
+
const { stdout: email } = await execFile3("git", ["config", "user.email"], {
|
|
790
1019
|
cwd: rootDir,
|
|
791
1020
|
timeout: 3e3
|
|
792
1021
|
});
|
|
793
1022
|
ownerEmail = email.trim();
|
|
794
|
-
} catch (
|
|
1023
|
+
} catch (e23) {
|
|
795
1024
|
}
|
|
796
1025
|
if (org && org !== "Personal") {
|
|
797
1026
|
ownerTeam = org;
|
|
@@ -841,7 +1070,7 @@ function attachWebSocket(server) {
|
|
|
841
1070
|
const msg = JSON.parse(raw.toString());
|
|
842
1071
|
msg.sessionId = sessionId;
|
|
843
1072
|
setupBus.emitEvent(msg);
|
|
844
|
-
} catch (
|
|
1073
|
+
} catch (e24) {
|
|
845
1074
|
}
|
|
846
1075
|
});
|
|
847
1076
|
ws.on("close", () => {
|
|
@@ -890,26 +1119,38 @@ function createApp(opts) {
|
|
|
890
1119
|
app.get("/setup", (c) => {
|
|
891
1120
|
return c.html(setupPageHTML());
|
|
892
1121
|
});
|
|
1122
|
+
app.get("/planes", (c) => {
|
|
1123
|
+
return c.html(pageHTML({
|
|
1124
|
+
title: "Semantic Planes",
|
|
1125
|
+
activePage: "planes",
|
|
1126
|
+
contentId: "page-content"
|
|
1127
|
+
}));
|
|
1128
|
+
});
|
|
1129
|
+
app.get("/analytics", (c) => {
|
|
1130
|
+
return c.html(pageHTML({
|
|
1131
|
+
title: "Analytics",
|
|
1132
|
+
activePage: "analytics",
|
|
1133
|
+
contentId: "page-content"
|
|
1134
|
+
}));
|
|
1135
|
+
});
|
|
1136
|
+
app.get("/settings", (c) => {
|
|
1137
|
+
return c.html(pageHTML({
|
|
1138
|
+
title: "Settings",
|
|
1139
|
+
activePage: "settings",
|
|
1140
|
+
contentId: "page-content"
|
|
1141
|
+
}));
|
|
1142
|
+
});
|
|
893
1143
|
app.get("/", (c) => c.redirect("/setup"));
|
|
894
1144
|
return app;
|
|
895
1145
|
}
|
|
896
|
-
function
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
905
|
-
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
906
|
-
<link rel="stylesheet" href="/static/uxd.css" />
|
|
907
|
-
<link rel="stylesheet" href="/static/setup.css" />
|
|
908
|
-
</head>
|
|
909
|
-
<body>
|
|
910
|
-
<div class="app-shell">
|
|
911
|
-
<!-- Sidebar -->
|
|
912
|
-
<aside class="sidebar">
|
|
1146
|
+
function sidebarHTML(activePage) {
|
|
1147
|
+
const nav = (page, href, label) => {
|
|
1148
|
+
const isActive = activePage === page;
|
|
1149
|
+
return `<a class="nav-item${isActive ? " active" : ""}" href="${href}">
|
|
1150
|
+
<span>${label}</span>
|
|
1151
|
+
</a>`;
|
|
1152
|
+
};
|
|
1153
|
+
return `<aside class="sidebar">
|
|
913
1154
|
<div class="sidebar-brand">
|
|
914
1155
|
<svg class="brand-chevron" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
915
1156
|
<path d="M4 4l8 8-8 8" stroke="#c9a55a" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -921,42 +1162,22 @@ function setupPageHTML() {
|
|
|
921
1162
|
<span class="brand-badge">Local</span>
|
|
922
1163
|
</div>
|
|
923
1164
|
<nav class="sidebar-nav">
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
<
|
|
928
|
-
<span>Semantic Planes</span>
|
|
929
|
-
<svg class="lock-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
930
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
|
931
|
-
<path d="M7 11V7a5 5 0 0110 0v4"/>
|
|
932
|
-
</svg>
|
|
933
|
-
</a>
|
|
934
|
-
<a class="nav-item locked" data-nav="analytics">
|
|
935
|
-
<span>Analytics</span>
|
|
936
|
-
<svg class="lock-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
937
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
|
938
|
-
<path d="M7 11V7a5 5 0 0110 0v4"/>
|
|
939
|
-
</svg>
|
|
940
|
-
</a>
|
|
941
|
-
<a class="nav-item" data-nav="mcp">
|
|
1165
|
+
${nav("setup", "/setup", "Setup")}
|
|
1166
|
+
${nav("planes", "/planes", "Semantic Planes")}
|
|
1167
|
+
${nav("analytics", "/analytics", "Analytics")}
|
|
1168
|
+
<div class="nav-item mcp-toggle" id="mcp-nav-toggle" title="Click to start/stop MCP server" style="cursor:pointer">
|
|
942
1169
|
<span class="status-dot" id="mcp-status-dot"></span>
|
|
943
1170
|
<span>MCP Server</span>
|
|
944
1171
|
<span class="nav-detail" id="mcp-status-text">checking...</span>
|
|
945
|
-
</
|
|
946
|
-
|
|
947
|
-
<span>Settings</span>
|
|
948
|
-
<svg class="lock-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
949
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
|
950
|
-
<path d="M7 11V7a5 5 0 0110 0v4"/>
|
|
951
|
-
</svg>
|
|
952
|
-
</a>
|
|
1172
|
+
</div>
|
|
1173
|
+
${nav("settings", "/settings", "Settings")}
|
|
953
1174
|
</nav>
|
|
954
1175
|
<div class="sidebar-status">
|
|
955
1176
|
<div class="status-row">
|
|
956
1177
|
<span class="status-dot" id="db-status-dot"></span>
|
|
957
1178
|
<span id="db-status-text">No database</span>
|
|
958
1179
|
</div>
|
|
959
|
-
<div class="status-row">
|
|
1180
|
+
<div class="status-row mcp-toggle" id="mcp-toggle-row" title="Click to start/stop MCP server">
|
|
960
1181
|
<span class="status-dot" id="mcp-server-dot"></span>
|
|
961
1182
|
<span id="mcp-server-text">MCP stopped</span>
|
|
962
1183
|
</div>
|
|
@@ -964,36 +1185,80 @@ function setupPageHTML() {
|
|
|
964
1185
|
<span class="tier-badge" id="tier-badge">Free</span>
|
|
965
1186
|
</div>
|
|
966
1187
|
</div>
|
|
967
|
-
|
|
1188
|
+
<div class="sidebar-security">
|
|
1189
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--rc-color-status-success)" stroke-width="2">
|
|
1190
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
1191
|
+
</svg>
|
|
1192
|
+
<span>Local-only processing</span>
|
|
1193
|
+
</div>
|
|
1194
|
+
</aside>`;
|
|
1195
|
+
}
|
|
1196
|
+
function footerHTML() {
|
|
1197
|
+
return `<footer class="app-footer">
|
|
1198
|
+
<span>Powered by <a href="https://runcontext.dev" target="_blank" rel="noopener">RunContext</a></span>
|
|
1199
|
+
<span class="footer-links">
|
|
1200
|
+
<a href="https://docs.runcontext.dev" target="_blank" rel="noopener">Docs</a>
|
|
1201
|
+
<span class="footer-sep">·</span>
|
|
1202
|
+
<a href="https://runcontext.dev/pricing" target="_blank" rel="noopener">Cloud</a>
|
|
1203
|
+
<span class="footer-sep">·</span>
|
|
1204
|
+
<a href="https://github.com/Quiet-Victory-Labs/runcontext" target="_blank" rel="noopener">GitHub</a>
|
|
1205
|
+
</span>
|
|
1206
|
+
</footer>`;
|
|
1207
|
+
}
|
|
1208
|
+
function pageHTML(opts) {
|
|
1209
|
+
const isSetup = opts.activePage === "setup";
|
|
1210
|
+
const headerContent = isSetup ? `<div class="header-stepper" id="stepper"></div>` : `<h1 class="header-title">${opts.title}</h1>`;
|
|
1211
|
+
const lockedTooltip = isSetup ? `
|
|
1212
|
+
<!-- Locked tooltip (hidden by default) -->
|
|
1213
|
+
<div class="locked-tooltip" id="locked-tooltip" style="display:none">
|
|
1214
|
+
<p><strong>Cloud Feature</strong></p>
|
|
1215
|
+
<p>This feature is available on RunContext Cloud with team collaboration, hosted endpoints, and analytics.</p>
|
|
1216
|
+
<a href="https://runcontext.dev/pricing" target="_blank" rel="noopener">View plans \u2192</a>
|
|
1217
|
+
</div>` : "";
|
|
1218
|
+
return `<!DOCTYPE html>
|
|
1219
|
+
<html lang="en">
|
|
1220
|
+
<head>
|
|
1221
|
+
<meta charset="utf-8" />
|
|
1222
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1223
|
+
<title>RunContext \u2014 ${opts.title}</title>
|
|
1224
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1225
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1226
|
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
1227
|
+
<link rel="stylesheet" href="/static/uxd.css" />
|
|
1228
|
+
<link rel="stylesheet" href="/static/setup.css" />
|
|
1229
|
+
</head>
|
|
1230
|
+
<body data-page="${opts.activePage}">
|
|
1231
|
+
<div class="app-shell">
|
|
1232
|
+
<!-- Sidebar -->
|
|
1233
|
+
${sidebarHTML(opts.activePage)}
|
|
968
1234
|
|
|
969
1235
|
<!-- Header -->
|
|
970
1236
|
<header class="app-header">
|
|
971
|
-
|
|
1237
|
+
${headerContent}
|
|
972
1238
|
</header>
|
|
973
1239
|
|
|
974
1240
|
<!-- Main Content -->
|
|
975
1241
|
<main class="main-content">
|
|
976
|
-
<div class="content-wrapper" id="
|
|
1242
|
+
<div class="content-wrapper" id="${opts.contentId}"></div>
|
|
977
1243
|
</main>
|
|
978
1244
|
|
|
979
1245
|
<!-- Footer -->
|
|
980
|
-
|
|
981
|
-
<span>Powered by RunContext · Open Semantic Interchange</span>
|
|
982
|
-
</footer>
|
|
1246
|
+
${footerHTML()}
|
|
983
1247
|
</div>
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
<div class="locked-tooltip" id="locked-tooltip" style="display:none">
|
|
987
|
-
<p>Available on RunContext Cloud</p>
|
|
988
|
-
<a href="https://runcontext.dev/pricing" target="_blank" rel="noopener">Learn more</a>
|
|
989
|
-
</div>
|
|
990
|
-
|
|
991
|
-
<script src="/static/setup.js"></script>
|
|
1248
|
+
${lockedTooltip}
|
|
1249
|
+
<script src="/static/app.js"></script>
|
|
992
1250
|
</body>
|
|
993
1251
|
</html>`;
|
|
994
1252
|
}
|
|
1253
|
+
function setupPageHTML() {
|
|
1254
|
+
return pageHTML({
|
|
1255
|
+
title: "Build Your Context Layer",
|
|
1256
|
+
activePage: "setup",
|
|
1257
|
+
contentId: "wizard-content"
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
995
1260
|
function startUIServer(opts) {
|
|
996
|
-
return new Promise((
|
|
1261
|
+
return new Promise((resolve3, reject) => {
|
|
997
1262
|
const app = createApp(opts);
|
|
998
1263
|
const server = _nodeserver.serve.call(void 0, {
|
|
999
1264
|
fetch: app.fetch,
|
|
@@ -1001,7 +1266,7 @@ function startUIServer(opts) {
|
|
|
1001
1266
|
hostname: opts.host
|
|
1002
1267
|
}, (info) => {
|
|
1003
1268
|
console.log(`RunContext UI running at http://${opts.host === "0.0.0.0" ? "localhost" : opts.host}:${info.port}/setup`);
|
|
1004
|
-
|
|
1269
|
+
resolve3();
|
|
1005
1270
|
});
|
|
1006
1271
|
attachWebSocket(server);
|
|
1007
1272
|
server.on("error", reject);
|