@contextos/core 0.1.0 → 0.2.1
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.js +1215 -8
- package/package.json +46 -46
package/dist/index.js
CHANGED
|
@@ -993,9 +993,23 @@ var DependencyGraph = class {
|
|
|
993
993
|
* Deserialize graph from JSON
|
|
994
994
|
*/
|
|
995
995
|
fromJSON(data) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
996
|
+
if (!data) {
|
|
997
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
998
|
+
this.edges = [];
|
|
999
|
+
this.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (data.nodes instanceof Map) {
|
|
1003
|
+
this.nodes = data.nodes;
|
|
1004
|
+
} else if (Array.isArray(data.nodes)) {
|
|
1005
|
+
this.nodes = new Map(data.nodes);
|
|
1006
|
+
} else if (data.nodes && typeof data.nodes === "object") {
|
|
1007
|
+
this.nodes = new Map(Object.entries(data.nodes));
|
|
1008
|
+
} else {
|
|
1009
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
1010
|
+
}
|
|
1011
|
+
this.edges = Array.isArray(data.edges) ? data.edges : [];
|
|
1012
|
+
this.lastUpdated = data.lastUpdated || (/* @__PURE__ */ new Date()).toISOString();
|
|
999
1013
|
}
|
|
1000
1014
|
/**
|
|
1001
1015
|
* Get graph statistics
|
|
@@ -2049,11 +2063,11 @@ var ContextBuilder = class {
|
|
|
2049
2063
|
const graphPath = join4(rootDir, CONTEXTOS_DIR2, GRAPH_FILE);
|
|
2050
2064
|
const graphDir = join4(rootDir, CONTEXTOS_DIR2, "db");
|
|
2051
2065
|
if (!existsSync4(graphDir)) {
|
|
2052
|
-
const { mkdirSync:
|
|
2053
|
-
|
|
2066
|
+
const { mkdirSync: mkdirSync9 } = await import("fs");
|
|
2067
|
+
mkdirSync9(graphDir, { recursive: true });
|
|
2054
2068
|
}
|
|
2055
|
-
const { writeFileSync:
|
|
2056
|
-
|
|
2069
|
+
const { writeFileSync: writeFileSync9 } = await import("fs");
|
|
2070
|
+
writeFileSync9(graphPath, JSON.stringify(this.graph.toJSON(), null, 2));
|
|
2057
2071
|
return {
|
|
2058
2072
|
filesIndexed,
|
|
2059
2073
|
chunksCreated,
|
|
@@ -5534,6 +5548,1185 @@ var AuditLogger = class {
|
|
|
5534
5548
|
}
|
|
5535
5549
|
};
|
|
5536
5550
|
|
|
5551
|
+
// src/plugins/manager.ts
|
|
5552
|
+
import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync11, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync7 } from "fs";
|
|
5553
|
+
import { join as join10, resolve } from "path";
|
|
5554
|
+
var PluginManager = class {
|
|
5555
|
+
plugins = /* @__PURE__ */ new Map();
|
|
5556
|
+
pluginsDir;
|
|
5557
|
+
projectRoot;
|
|
5558
|
+
storage = /* @__PURE__ */ new Map();
|
|
5559
|
+
constructor(projectRoot) {
|
|
5560
|
+
this.projectRoot = projectRoot;
|
|
5561
|
+
this.pluginsDir = join10(projectRoot, ".contextos", "plugins");
|
|
5562
|
+
if (!existsSync11(this.pluginsDir)) {
|
|
5563
|
+
mkdirSync7(this.pluginsDir, { recursive: true });
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
/**
|
|
5567
|
+
* Load all plugins from plugins directory
|
|
5568
|
+
*/
|
|
5569
|
+
async loadAll() {
|
|
5570
|
+
const loaded = [];
|
|
5571
|
+
const errors = [];
|
|
5572
|
+
if (!existsSync11(this.pluginsDir)) {
|
|
5573
|
+
return { loaded, errors };
|
|
5574
|
+
}
|
|
5575
|
+
const entries = readdirSync(this.pluginsDir, { withFileTypes: true });
|
|
5576
|
+
for (const entry of entries) {
|
|
5577
|
+
if (!entry.isDirectory()) continue;
|
|
5578
|
+
const pluginPath = join10(this.pluginsDir, entry.name);
|
|
5579
|
+
try {
|
|
5580
|
+
await this.loadPlugin(pluginPath);
|
|
5581
|
+
loaded.push(entry.name);
|
|
5582
|
+
} catch (error) {
|
|
5583
|
+
errors.push({
|
|
5584
|
+
name: entry.name,
|
|
5585
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
return { loaded, errors };
|
|
5590
|
+
}
|
|
5591
|
+
/**
|
|
5592
|
+
* Load a single plugin from path
|
|
5593
|
+
*/
|
|
5594
|
+
async loadPlugin(pluginPath) {
|
|
5595
|
+
const manifestPath = join10(pluginPath, "package.json");
|
|
5596
|
+
if (!existsSync11(manifestPath)) {
|
|
5597
|
+
throw new Error(`Plugin manifest not found: ${manifestPath}`);
|
|
5598
|
+
}
|
|
5599
|
+
const manifestContent = readFileSync11(manifestPath, "utf-8");
|
|
5600
|
+
const manifest = JSON.parse(manifestContent);
|
|
5601
|
+
if (!manifest.name || !manifest.version || !manifest.main) {
|
|
5602
|
+
throw new Error(`Invalid plugin manifest: missing name, version, or main`);
|
|
5603
|
+
}
|
|
5604
|
+
const mainPath = join10(pluginPath, manifest.main);
|
|
5605
|
+
if (!existsSync11(mainPath)) {
|
|
5606
|
+
throw new Error(`Plugin main file not found: ${mainPath}`);
|
|
5607
|
+
}
|
|
5608
|
+
const pluginModule = await import(`file://${resolve(mainPath)}`);
|
|
5609
|
+
const plugin = pluginModule.default || pluginModule;
|
|
5610
|
+
if (!plugin.name || !plugin.version) {
|
|
5611
|
+
throw new Error(`Invalid plugin: missing name or version`);
|
|
5612
|
+
}
|
|
5613
|
+
const state = {
|
|
5614
|
+
manifest,
|
|
5615
|
+
instance: plugin,
|
|
5616
|
+
enabled: true,
|
|
5617
|
+
path: pluginPath,
|
|
5618
|
+
loadedAt: /* @__PURE__ */ new Date()
|
|
5619
|
+
};
|
|
5620
|
+
this.plugins.set(plugin.name, state);
|
|
5621
|
+
if (plugin.activate) {
|
|
5622
|
+
const context = this.createPluginContext(plugin.name);
|
|
5623
|
+
await plugin.activate(context);
|
|
5624
|
+
}
|
|
5625
|
+
return plugin;
|
|
5626
|
+
}
|
|
5627
|
+
/**
|
|
5628
|
+
* Unload a plugin
|
|
5629
|
+
*/
|
|
5630
|
+
async unloadPlugin(name) {
|
|
5631
|
+
const state = this.plugins.get(name);
|
|
5632
|
+
if (!state) return false;
|
|
5633
|
+
if (state.instance.deactivate) {
|
|
5634
|
+
await state.instance.deactivate();
|
|
5635
|
+
}
|
|
5636
|
+
this.storage.delete(name);
|
|
5637
|
+
this.plugins.delete(name);
|
|
5638
|
+
return true;
|
|
5639
|
+
}
|
|
5640
|
+
/**
|
|
5641
|
+
* Enable a disabled plugin
|
|
5642
|
+
*/
|
|
5643
|
+
async enablePlugin(name) {
|
|
5644
|
+
const state = this.plugins.get(name);
|
|
5645
|
+
if (!state) return false;
|
|
5646
|
+
if (state.enabled) return true;
|
|
5647
|
+
state.enabled = true;
|
|
5648
|
+
if (state.instance.activate) {
|
|
5649
|
+
const context = this.createPluginContext(name);
|
|
5650
|
+
await state.instance.activate(context);
|
|
5651
|
+
}
|
|
5652
|
+
return true;
|
|
5653
|
+
}
|
|
5654
|
+
/**
|
|
5655
|
+
* Disable a plugin (keep loaded but inactive)
|
|
5656
|
+
*/
|
|
5657
|
+
async disablePlugin(name) {
|
|
5658
|
+
const state = this.plugins.get(name);
|
|
5659
|
+
if (!state) return false;
|
|
5660
|
+
if (!state.enabled) return true;
|
|
5661
|
+
state.enabled = false;
|
|
5662
|
+
if (state.instance.deactivate) {
|
|
5663
|
+
await state.instance.deactivate();
|
|
5664
|
+
}
|
|
5665
|
+
return true;
|
|
5666
|
+
}
|
|
5667
|
+
/**
|
|
5668
|
+
* Install a plugin
|
|
5669
|
+
*/
|
|
5670
|
+
async install(source, options = {}) {
|
|
5671
|
+
const targetDir = join10(this.pluginsDir, source.split("/").pop() || source);
|
|
5672
|
+
if (existsSync11(targetDir) && !options.force) {
|
|
5673
|
+
throw new Error(`Plugin already installed: ${source}`);
|
|
5674
|
+
}
|
|
5675
|
+
if (options.local) {
|
|
5676
|
+
const sourcePath = resolve(source);
|
|
5677
|
+
if (!existsSync11(sourcePath)) {
|
|
5678
|
+
throw new Error(`Source path not found: ${sourcePath}`);
|
|
5679
|
+
}
|
|
5680
|
+
if (options.force && existsSync11(targetDir)) {
|
|
5681
|
+
rmSync(targetDir, { recursive: true });
|
|
5682
|
+
}
|
|
5683
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
5684
|
+
const manifest = JSON.parse(readFileSync11(join10(sourcePath, "package.json"), "utf-8"));
|
|
5685
|
+
writeFileSync7(join10(targetDir, "package.json"), JSON.stringify(manifest, null, 2));
|
|
5686
|
+
const mainContent = readFileSync11(join10(sourcePath, manifest.main), "utf-8");
|
|
5687
|
+
writeFileSync7(join10(targetDir, manifest.main), mainContent);
|
|
5688
|
+
} else {
|
|
5689
|
+
throw new Error("Remote plugin installation not yet implemented");
|
|
5690
|
+
}
|
|
5691
|
+
const plugin = await this.loadPlugin(targetDir);
|
|
5692
|
+
if (options.skipActivate) {
|
|
5693
|
+
await this.disablePlugin(plugin.name);
|
|
5694
|
+
}
|
|
5695
|
+
return plugin;
|
|
5696
|
+
}
|
|
5697
|
+
/**
|
|
5698
|
+
* Uninstall a plugin
|
|
5699
|
+
*/
|
|
5700
|
+
async uninstall(name) {
|
|
5701
|
+
const state = this.plugins.get(name);
|
|
5702
|
+
if (!state) return false;
|
|
5703
|
+
await this.unloadPlugin(name);
|
|
5704
|
+
if (existsSync11(state.path)) {
|
|
5705
|
+
rmSync(state.path, { recursive: true });
|
|
5706
|
+
}
|
|
5707
|
+
return true;
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Get all loaded plugins
|
|
5711
|
+
*/
|
|
5712
|
+
list() {
|
|
5713
|
+
return Array.from(this.plugins.values());
|
|
5714
|
+
}
|
|
5715
|
+
/**
|
|
5716
|
+
* Get a specific plugin
|
|
5717
|
+
*/
|
|
5718
|
+
get(name) {
|
|
5719
|
+
return this.plugins.get(name);
|
|
5720
|
+
}
|
|
5721
|
+
/**
|
|
5722
|
+
* Execute a hook across all enabled plugins
|
|
5723
|
+
*/
|
|
5724
|
+
async executeHook(hookName, ...args) {
|
|
5725
|
+
const result = {
|
|
5726
|
+
modified: false,
|
|
5727
|
+
value: args[0],
|
|
5728
|
+
pluginsExecuted: [],
|
|
5729
|
+
errors: []
|
|
5730
|
+
};
|
|
5731
|
+
for (const [name, state] of this.plugins) {
|
|
5732
|
+
if (!state.enabled) continue;
|
|
5733
|
+
const hook = state.instance.hooks?.[hookName];
|
|
5734
|
+
if (!hook) continue;
|
|
5735
|
+
try {
|
|
5736
|
+
const hookFn = hook;
|
|
5737
|
+
const hookResult = await hookFn(...args);
|
|
5738
|
+
if (hookResult !== void 0) {
|
|
5739
|
+
result.modified = true;
|
|
5740
|
+
result.value = hookResult;
|
|
5741
|
+
args[0] = hookResult;
|
|
5742
|
+
}
|
|
5743
|
+
result.pluginsExecuted.push(name);
|
|
5744
|
+
} catch (error) {
|
|
5745
|
+
result.errors.push({
|
|
5746
|
+
plugin: name,
|
|
5747
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
return result;
|
|
5752
|
+
}
|
|
5753
|
+
/**
|
|
5754
|
+
* Create a scaffold for new plugin
|
|
5755
|
+
*/
|
|
5756
|
+
createPluginScaffold(template) {
|
|
5757
|
+
const pluginDir = join10(this.pluginsDir, template.name);
|
|
5758
|
+
if (existsSync11(pluginDir)) {
|
|
5759
|
+
throw new Error(`Plugin directory already exists: ${template.name}`);
|
|
5760
|
+
}
|
|
5761
|
+
mkdirSync7(pluginDir, { recursive: true });
|
|
5762
|
+
const manifest = {
|
|
5763
|
+
name: template.name,
|
|
5764
|
+
version: "1.0.0",
|
|
5765
|
+
description: template.description,
|
|
5766
|
+
author: template.author,
|
|
5767
|
+
main: "index.js",
|
|
5768
|
+
engines: {
|
|
5769
|
+
contextos: ">=2.0.0"
|
|
5770
|
+
}
|
|
5771
|
+
};
|
|
5772
|
+
writeFileSync7(
|
|
5773
|
+
join10(pluginDir, "package.json"),
|
|
5774
|
+
JSON.stringify(manifest, null, 2)
|
|
5775
|
+
);
|
|
5776
|
+
const hookCode = template.hooks.map((h) => ` ${h}: async (value) => {
|
|
5777
|
+
// TODO: Implement ${h}
|
|
5778
|
+
return value;
|
|
5779
|
+
},`).join("\n");
|
|
5780
|
+
const commandsCode = template.withCommands ? `
|
|
5781
|
+
commands: {
|
|
5782
|
+
'my-command': {
|
|
5783
|
+
description: 'Example command',
|
|
5784
|
+
handler: async (args, context) => {
|
|
5785
|
+
context.log.info('Command executed with args:', args);
|
|
5786
|
+
},
|
|
5787
|
+
},
|
|
5788
|
+
},` : "";
|
|
5789
|
+
const pluginCode = `/**
|
|
5790
|
+
* ${template.name} - ContextOS Plugin
|
|
5791
|
+
* ${template.description}
|
|
5792
|
+
*/
|
|
5793
|
+
|
|
5794
|
+
module.exports = {
|
|
5795
|
+
name: '${template.name}',
|
|
5796
|
+
version: '1.0.0',
|
|
5797
|
+
description: '${template.description}',
|
|
5798
|
+
|
|
5799
|
+
hooks: {
|
|
5800
|
+
${hookCode}
|
|
5801
|
+
},
|
|
5802
|
+
${commandsCode}
|
|
5803
|
+
|
|
5804
|
+
activate: async (context) => {
|
|
5805
|
+
context.log.info('${template.name} activated');
|
|
5806
|
+
},
|
|
5807
|
+
|
|
5808
|
+
deactivate: async () => {
|
|
5809
|
+
// Cleanup if needed
|
|
5810
|
+
},
|
|
5811
|
+
};
|
|
5812
|
+
`;
|
|
5813
|
+
writeFileSync7(join10(pluginDir, "index.js"), pluginCode);
|
|
5814
|
+
return pluginDir;
|
|
5815
|
+
}
|
|
5816
|
+
/**
|
|
5817
|
+
* Create plugin context for API access
|
|
5818
|
+
*/
|
|
5819
|
+
createPluginContext(pluginName) {
|
|
5820
|
+
if (!this.storage.has(pluginName)) {
|
|
5821
|
+
this.storage.set(pluginName, /* @__PURE__ */ new Map());
|
|
5822
|
+
}
|
|
5823
|
+
const pluginStorage = this.storage.get(pluginName);
|
|
5824
|
+
return {
|
|
5825
|
+
projectRoot: this.projectRoot,
|
|
5826
|
+
configDir: join10(this.projectRoot, ".contextos"),
|
|
5827
|
+
log: {
|
|
5828
|
+
debug: (msg, ...args) => console.debug(`[${pluginName}]`, msg, ...args),
|
|
5829
|
+
info: (msg, ...args) => console.info(`[${pluginName}]`, msg, ...args),
|
|
5830
|
+
warn: (msg, ...args) => console.warn(`[${pluginName}]`, msg, ...args),
|
|
5831
|
+
error: (msg, ...args) => console.error(`[${pluginName}]`, msg, ...args)
|
|
5832
|
+
},
|
|
5833
|
+
query: async (_goal) => {
|
|
5834
|
+
return { files: [], context: "" };
|
|
5835
|
+
},
|
|
5836
|
+
readFile: async (path) => {
|
|
5837
|
+
const fullPath = join10(this.projectRoot, path);
|
|
5838
|
+
return readFileSync11(fullPath, "utf-8");
|
|
5839
|
+
},
|
|
5840
|
+
getDependencies: async (_path, _depth = 2) => {
|
|
5841
|
+
return [];
|
|
5842
|
+
},
|
|
5843
|
+
storage: {
|
|
5844
|
+
get: (key) => pluginStorage.get(key),
|
|
5845
|
+
set: (key, value) => {
|
|
5846
|
+
pluginStorage.set(key, value);
|
|
5847
|
+
},
|
|
5848
|
+
delete: (key) => pluginStorage.delete(key)
|
|
5849
|
+
}
|
|
5850
|
+
};
|
|
5851
|
+
}
|
|
5852
|
+
};
|
|
5853
|
+
function createPluginManager(projectRoot) {
|
|
5854
|
+
return new PluginManager(projectRoot);
|
|
5855
|
+
}
|
|
5856
|
+
|
|
5857
|
+
// src/plugins/registry.ts
|
|
5858
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "fs";
|
|
5859
|
+
import { join as join11 } from "path";
|
|
5860
|
+
var PluginRegistry = class {
|
|
5861
|
+
config;
|
|
5862
|
+
remoteCache = /* @__PURE__ */ new Map();
|
|
5863
|
+
constructor(config) {
|
|
5864
|
+
this.config = config;
|
|
5865
|
+
}
|
|
5866
|
+
/**
|
|
5867
|
+
* List all locally installed plugins
|
|
5868
|
+
*/
|
|
5869
|
+
listLocal() {
|
|
5870
|
+
const plugins = [];
|
|
5871
|
+
if (!existsSync12(this.config.localDir)) {
|
|
5872
|
+
return plugins;
|
|
5873
|
+
}
|
|
5874
|
+
const entries = readdirSync2(this.config.localDir, { withFileTypes: true });
|
|
5875
|
+
for (const entry of entries) {
|
|
5876
|
+
if (!entry.isDirectory()) continue;
|
|
5877
|
+
const pluginPath = join11(this.config.localDir, entry.name);
|
|
5878
|
+
const manifestPath = join11(pluginPath, "package.json");
|
|
5879
|
+
if (!existsSync12(manifestPath)) continue;
|
|
5880
|
+
try {
|
|
5881
|
+
const manifest = JSON.parse(
|
|
5882
|
+
readFileSync12(manifestPath, "utf-8")
|
|
5883
|
+
);
|
|
5884
|
+
const disabledPath = join11(pluginPath, ".disabled");
|
|
5885
|
+
const enabled = !existsSync12(disabledPath);
|
|
5886
|
+
plugins.push({
|
|
5887
|
+
name: manifest.name,
|
|
5888
|
+
version: manifest.version,
|
|
5889
|
+
description: manifest.description,
|
|
5890
|
+
path: pluginPath,
|
|
5891
|
+
enabled
|
|
5892
|
+
});
|
|
5893
|
+
} catch {
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
return plugins;
|
|
5897
|
+
}
|
|
5898
|
+
/**
|
|
5899
|
+
* Search remote registry for plugins
|
|
5900
|
+
*/
|
|
5901
|
+
async searchRemote(query) {
|
|
5902
|
+
if (!this.config.remoteUrl) {
|
|
5903
|
+
return [];
|
|
5904
|
+
}
|
|
5905
|
+
const cacheKey = `search:${query}`;
|
|
5906
|
+
if (this.remoteCache.has(cacheKey)) {
|
|
5907
|
+
return this.remoteCache.get(cacheKey);
|
|
5908
|
+
}
|
|
5909
|
+
try {
|
|
5910
|
+
const searchUrl = `${this.config.remoteUrl}/-/v1/search?text=${encodeURIComponent(query)}&scope=contextos-plugin`;
|
|
5911
|
+
const response = await fetch(searchUrl);
|
|
5912
|
+
if (!response.ok) {
|
|
5913
|
+
throw new Error(`Registry search failed: ${response.statusText}`);
|
|
5914
|
+
}
|
|
5915
|
+
const data = await response.json();
|
|
5916
|
+
const results = data.objects.map((obj) => obj.package);
|
|
5917
|
+
this.remoteCache.set(cacheKey, results);
|
|
5918
|
+
setTimeout(() => this.remoteCache.delete(cacheKey), 5 * 60 * 1e3);
|
|
5919
|
+
return results;
|
|
5920
|
+
} catch (error) {
|
|
5921
|
+
console.error("Remote registry search failed:", error);
|
|
5922
|
+
return [];
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
/**
|
|
5926
|
+
* Get plugin info from remote registry
|
|
5927
|
+
*/
|
|
5928
|
+
async getRemoteInfo(name, version) {
|
|
5929
|
+
if (!this.config.remoteUrl) {
|
|
5930
|
+
return null;
|
|
5931
|
+
}
|
|
5932
|
+
try {
|
|
5933
|
+
const infoUrl = version ? `${this.config.remoteUrl}/${name}/${version}` : `${this.config.remoteUrl}/${name}/latest`;
|
|
5934
|
+
const response = await fetch(infoUrl);
|
|
5935
|
+
if (!response.ok) {
|
|
5936
|
+
return null;
|
|
5937
|
+
}
|
|
5938
|
+
return await response.json();
|
|
5939
|
+
} catch {
|
|
5940
|
+
return null;
|
|
5941
|
+
}
|
|
5942
|
+
}
|
|
5943
|
+
/**
|
|
5944
|
+
* Check if a plugin is installed locally
|
|
5945
|
+
*/
|
|
5946
|
+
isInstalled(name) {
|
|
5947
|
+
const plugins = this.listLocal();
|
|
5948
|
+
return plugins.some((p) => p.name === name);
|
|
5949
|
+
}
|
|
5950
|
+
/**
|
|
5951
|
+
* Get local plugin by name
|
|
5952
|
+
*/
|
|
5953
|
+
getLocal(name) {
|
|
5954
|
+
const plugins = this.listLocal();
|
|
5955
|
+
return plugins.find((p) => p.name === name) || null;
|
|
5956
|
+
}
|
|
5957
|
+
/**
|
|
5958
|
+
* Get featured/popular plugins from remote
|
|
5959
|
+
*/
|
|
5960
|
+
async getFeatured() {
|
|
5961
|
+
if (!this.config.remoteUrl) {
|
|
5962
|
+
return [
|
|
5963
|
+
{
|
|
5964
|
+
name: "@contextos/plugin-docker",
|
|
5965
|
+
version: "1.0.0",
|
|
5966
|
+
description: "Docker context integration - scan Dockerfiles and compose files",
|
|
5967
|
+
author: "ContextOS Team",
|
|
5968
|
+
downloads: 1200,
|
|
5969
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5970
|
+
tarball: "",
|
|
5971
|
+
keywords: ["docker", "container", "devops"]
|
|
5972
|
+
},
|
|
5973
|
+
{
|
|
5974
|
+
name: "@contextos/plugin-kubernetes",
|
|
5975
|
+
version: "1.0.0",
|
|
5976
|
+
description: "Kubernetes context integration - scan K8s manifests",
|
|
5977
|
+
author: "ContextOS Team",
|
|
5978
|
+
downloads: 800,
|
|
5979
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5980
|
+
tarball: "",
|
|
5981
|
+
keywords: ["kubernetes", "k8s", "devops"]
|
|
5982
|
+
},
|
|
5983
|
+
{
|
|
5984
|
+
name: "@contextos/plugin-graphql",
|
|
5985
|
+
version: "1.0.0",
|
|
5986
|
+
description: "GraphQL schema and resolver context",
|
|
5987
|
+
author: "ContextOS Team",
|
|
5988
|
+
downloads: 650,
|
|
5989
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5990
|
+
tarball: "",
|
|
5991
|
+
keywords: ["graphql", "api", "schema"]
|
|
5992
|
+
},
|
|
5993
|
+
{
|
|
5994
|
+
name: "@contextos/plugin-prisma",
|
|
5995
|
+
version: "1.0.0",
|
|
5996
|
+
description: "Prisma ORM schema integration",
|
|
5997
|
+
author: "ContextOS Team",
|
|
5998
|
+
downloads: 500,
|
|
5999
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6000
|
+
tarball: "",
|
|
6001
|
+
keywords: ["prisma", "database", "orm"]
|
|
6002
|
+
}
|
|
6003
|
+
];
|
|
6004
|
+
}
|
|
6005
|
+
return this.searchRemote("contextos-plugin featured");
|
|
6006
|
+
}
|
|
6007
|
+
/**
|
|
6008
|
+
* Clear remote cache
|
|
6009
|
+
*/
|
|
6010
|
+
clearCache() {
|
|
6011
|
+
this.remoteCache.clear();
|
|
6012
|
+
}
|
|
6013
|
+
};
|
|
6014
|
+
function createPluginRegistry(projectRoot) {
|
|
6015
|
+
return new PluginRegistry({
|
|
6016
|
+
localDir: join11(projectRoot, ".contextos", "plugins"),
|
|
6017
|
+
remoteUrl: process.env.CONTEXTOS_REGISTRY_URL || void 0,
|
|
6018
|
+
cacheDir: join11(projectRoot, ".contextos", "cache", "plugins")
|
|
6019
|
+
});
|
|
6020
|
+
}
|
|
6021
|
+
|
|
6022
|
+
// src/finetuning/collector.ts
|
|
6023
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
|
|
6024
|
+
import { join as join12 } from "path";
|
|
6025
|
+
import { createHash as createHash4 } from "crypto";
|
|
6026
|
+
var TrainingDataCollector = class {
|
|
6027
|
+
dataDir;
|
|
6028
|
+
examples = [];
|
|
6029
|
+
loaded = false;
|
|
6030
|
+
constructor(projectRoot) {
|
|
6031
|
+
this.dataDir = join12(projectRoot, ".contextos", "training");
|
|
6032
|
+
if (!existsSync13(this.dataDir)) {
|
|
6033
|
+
mkdirSync8(this.dataDir, { recursive: true });
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
/**
|
|
6037
|
+
* Load existing training data
|
|
6038
|
+
*/
|
|
6039
|
+
load() {
|
|
6040
|
+
if (this.loaded) return;
|
|
6041
|
+
const dataFile = join12(this.dataDir, "examples.json");
|
|
6042
|
+
if (existsSync13(dataFile)) {
|
|
6043
|
+
try {
|
|
6044
|
+
const data = JSON.parse(readFileSync13(dataFile, "utf-8"));
|
|
6045
|
+
this.examples = data.examples || [];
|
|
6046
|
+
} catch {
|
|
6047
|
+
this.examples = [];
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
this.loaded = true;
|
|
6051
|
+
}
|
|
6052
|
+
/**
|
|
6053
|
+
* Save training data
|
|
6054
|
+
*/
|
|
6055
|
+
save() {
|
|
6056
|
+
const dataFile = join12(this.dataDir, "examples.json");
|
|
6057
|
+
writeFileSync8(dataFile, JSON.stringify({
|
|
6058
|
+
version: "1.0",
|
|
6059
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6060
|
+
examples: this.examples
|
|
6061
|
+
}, null, 2));
|
|
6062
|
+
}
|
|
6063
|
+
/**
|
|
6064
|
+
* Record a context build for training
|
|
6065
|
+
*/
|
|
6066
|
+
record(goal, selectedFiles, context, tokenCount, meta) {
|
|
6067
|
+
this.load();
|
|
6068
|
+
const example = {
|
|
6069
|
+
id: this.generateId(goal, meta.timestamp),
|
|
6070
|
+
goal,
|
|
6071
|
+
selectedFiles,
|
|
6072
|
+
context,
|
|
6073
|
+
tokenCount,
|
|
6074
|
+
meta
|
|
6075
|
+
};
|
|
6076
|
+
this.examples.push(example);
|
|
6077
|
+
this.save();
|
|
6078
|
+
return example;
|
|
6079
|
+
}
|
|
6080
|
+
/**
|
|
6081
|
+
* Add feedback to an existing example
|
|
6082
|
+
*/
|
|
6083
|
+
addFeedback(exampleId, rating, comment) {
|
|
6084
|
+
this.load();
|
|
6085
|
+
const example = this.examples.find((e) => e.id === exampleId);
|
|
6086
|
+
if (!example) return false;
|
|
6087
|
+
example.feedback = { rating, comment };
|
|
6088
|
+
this.save();
|
|
6089
|
+
return true;
|
|
6090
|
+
}
|
|
6091
|
+
/**
|
|
6092
|
+
* Get all examples
|
|
6093
|
+
*/
|
|
6094
|
+
getAll() {
|
|
6095
|
+
this.load();
|
|
6096
|
+
return [...this.examples];
|
|
6097
|
+
}
|
|
6098
|
+
/**
|
|
6099
|
+
* Get example by ID
|
|
6100
|
+
*/
|
|
6101
|
+
get(id) {
|
|
6102
|
+
this.load();
|
|
6103
|
+
return this.examples.find((e) => e.id === id);
|
|
6104
|
+
}
|
|
6105
|
+
/**
|
|
6106
|
+
* Get recent examples
|
|
6107
|
+
*/
|
|
6108
|
+
getRecent(limit = 10) {
|
|
6109
|
+
this.load();
|
|
6110
|
+
return this.examples.sort(
|
|
6111
|
+
(a, b) => new Date(b.meta.timestamp).getTime() - new Date(a.meta.timestamp).getTime()
|
|
6112
|
+
).slice(0, limit);
|
|
6113
|
+
}
|
|
6114
|
+
/**
|
|
6115
|
+
* Filter examples
|
|
6116
|
+
*/
|
|
6117
|
+
filter(predicate) {
|
|
6118
|
+
this.load();
|
|
6119
|
+
return this.examples.filter(predicate);
|
|
6120
|
+
}
|
|
6121
|
+
/**
|
|
6122
|
+
* Get dataset statistics
|
|
6123
|
+
*/
|
|
6124
|
+
getStats() {
|
|
6125
|
+
this.load();
|
|
6126
|
+
const stats = {
|
|
6127
|
+
totalExamples: this.examples.length,
|
|
6128
|
+
ratingDistribution: {
|
|
6129
|
+
good: 0,
|
|
6130
|
+
neutral: 0,
|
|
6131
|
+
bad: 0,
|
|
6132
|
+
unrated: 0
|
|
6133
|
+
},
|
|
6134
|
+
languageDistribution: {},
|
|
6135
|
+
avgTokenCount: 0,
|
|
6136
|
+
avgFilesPerExample: 0,
|
|
6137
|
+
dateRange: {
|
|
6138
|
+
earliest: /* @__PURE__ */ new Date(),
|
|
6139
|
+
latest: /* @__PURE__ */ new Date(0)
|
|
6140
|
+
}
|
|
6141
|
+
};
|
|
6142
|
+
if (this.examples.length === 0) {
|
|
6143
|
+
return stats;
|
|
6144
|
+
}
|
|
6145
|
+
let totalTokens = 0;
|
|
6146
|
+
let totalFiles = 0;
|
|
6147
|
+
for (const example of this.examples) {
|
|
6148
|
+
if (example.feedback) {
|
|
6149
|
+
stats.ratingDistribution[example.feedback.rating]++;
|
|
6150
|
+
} else {
|
|
6151
|
+
stats.ratingDistribution.unrated++;
|
|
6152
|
+
}
|
|
6153
|
+
const lang = example.meta.language;
|
|
6154
|
+
stats.languageDistribution[lang] = (stats.languageDistribution[lang] || 0) + 1;
|
|
6155
|
+
totalTokens += example.tokenCount;
|
|
6156
|
+
totalFiles += example.selectedFiles.length;
|
|
6157
|
+
const date = new Date(example.meta.timestamp);
|
|
6158
|
+
if (date < stats.dateRange.earliest) {
|
|
6159
|
+
stats.dateRange.earliest = date;
|
|
6160
|
+
}
|
|
6161
|
+
if (date > stats.dateRange.latest) {
|
|
6162
|
+
stats.dateRange.latest = date;
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
stats.avgTokenCount = Math.round(totalTokens / this.examples.length);
|
|
6166
|
+
stats.avgFilesPerExample = Math.round(totalFiles / this.examples.length * 10) / 10;
|
|
6167
|
+
return stats;
|
|
6168
|
+
}
|
|
6169
|
+
/**
|
|
6170
|
+
* Delete an example
|
|
6171
|
+
*/
|
|
6172
|
+
delete(id) {
|
|
6173
|
+
this.load();
|
|
6174
|
+
const index = this.examples.findIndex((e) => e.id === id);
|
|
6175
|
+
if (index === -1) return false;
|
|
6176
|
+
this.examples.splice(index, 1);
|
|
6177
|
+
this.save();
|
|
6178
|
+
return true;
|
|
6179
|
+
}
|
|
6180
|
+
/**
|
|
6181
|
+
* Clear all examples
|
|
6182
|
+
*/
|
|
6183
|
+
clear() {
|
|
6184
|
+
this.examples = [];
|
|
6185
|
+
this.save();
|
|
6186
|
+
}
|
|
6187
|
+
/**
|
|
6188
|
+
* Generate unique ID
|
|
6189
|
+
*/
|
|
6190
|
+
generateId(goal, timestamp) {
|
|
6191
|
+
const hash = createHash4("sha256").update(goal + timestamp.toISOString()).digest("hex").substring(0, 12);
|
|
6192
|
+
return `ex_${hash}`;
|
|
6193
|
+
}
|
|
6194
|
+
};
|
|
6195
|
+
function createTrainingDataCollector(projectRoot) {
|
|
6196
|
+
return new TrainingDataCollector(projectRoot);
|
|
6197
|
+
}
|
|
6198
|
+
|
|
6199
|
+
// src/finetuning/formatter.ts
|
|
6200
|
+
import { createReadStream, createWriteStream, existsSync as existsSync14 } from "fs";
|
|
6201
|
+
import { createInterface } from "readline";
|
|
6202
|
+
var DatasetFormatter = class {
|
|
6203
|
+
/**
|
|
6204
|
+
* Export examples to file
|
|
6205
|
+
*/
|
|
6206
|
+
async export(examples, outputPath, format, config = {}) {
|
|
6207
|
+
let filtered = this.filterExamples(examples, config);
|
|
6208
|
+
if (config.shuffle) {
|
|
6209
|
+
filtered = this.shuffle(filtered);
|
|
6210
|
+
}
|
|
6211
|
+
if (config.maxExamples && filtered.length > config.maxExamples) {
|
|
6212
|
+
filtered = filtered.slice(0, config.maxExamples);
|
|
6213
|
+
}
|
|
6214
|
+
const stream = createWriteStream(outputPath);
|
|
6215
|
+
for (const example of filtered) {
|
|
6216
|
+
const formatted = this.formatExample(example, format);
|
|
6217
|
+
stream.write(formatted + "\n");
|
|
6218
|
+
}
|
|
6219
|
+
stream.end();
|
|
6220
|
+
return { exported: filtered.length, path: outputPath };
|
|
6221
|
+
}
|
|
6222
|
+
/**
|
|
6223
|
+
* Export with train/validation split
|
|
6224
|
+
*/
|
|
6225
|
+
async exportWithSplit(examples, outputDir, format, config = {}) {
|
|
6226
|
+
const split = config.validationSplit || 0.1;
|
|
6227
|
+
let filtered = this.filterExamples(examples, config);
|
|
6228
|
+
if (config.shuffle) {
|
|
6229
|
+
filtered = this.shuffle(filtered);
|
|
6230
|
+
}
|
|
6231
|
+
const splitIndex = Math.floor(filtered.length * (1 - split));
|
|
6232
|
+
const trainExamples = filtered.slice(0, splitIndex);
|
|
6233
|
+
const validationExamples = filtered.slice(splitIndex);
|
|
6234
|
+
await this.export(trainExamples, `${outputDir}/train.jsonl`, format, {});
|
|
6235
|
+
await this.export(validationExamples, `${outputDir}/validation.jsonl`, format, {});
|
|
6236
|
+
return {
|
|
6237
|
+
train: trainExamples.length,
|
|
6238
|
+
validation: validationExamples.length
|
|
6239
|
+
};
|
|
6240
|
+
}
|
|
6241
|
+
/**
|
|
6242
|
+
* Validate a dataset file
|
|
6243
|
+
*/
|
|
6244
|
+
async validate(filePath) {
|
|
6245
|
+
const result = {
|
|
6246
|
+
valid: true,
|
|
6247
|
+
errors: [],
|
|
6248
|
+
warnings: [],
|
|
6249
|
+
stats: {
|
|
6250
|
+
totalExamples: 0,
|
|
6251
|
+
ratingDistribution: { good: 0, neutral: 0, bad: 0, unrated: 0 },
|
|
6252
|
+
languageDistribution: {},
|
|
6253
|
+
avgTokenCount: 0,
|
|
6254
|
+
avgFilesPerExample: 0,
|
|
6255
|
+
dateRange: { earliest: /* @__PURE__ */ new Date(), latest: /* @__PURE__ */ new Date(0) }
|
|
6256
|
+
}
|
|
6257
|
+
};
|
|
6258
|
+
if (!existsSync14(filePath)) {
|
|
6259
|
+
result.valid = false;
|
|
6260
|
+
result.errors.push({
|
|
6261
|
+
line: 0,
|
|
6262
|
+
field: "file",
|
|
6263
|
+
message: `File not found: ${filePath}`
|
|
6264
|
+
});
|
|
6265
|
+
return result;
|
|
6266
|
+
}
|
|
6267
|
+
const fileStream = createReadStream(filePath);
|
|
6268
|
+
const rl = createInterface({
|
|
6269
|
+
input: fileStream,
|
|
6270
|
+
crlfDelay: Infinity
|
|
6271
|
+
});
|
|
6272
|
+
let lineNumber = 0;
|
|
6273
|
+
let totalTokens = 0;
|
|
6274
|
+
let totalFiles = 0;
|
|
6275
|
+
for await (const line of rl) {
|
|
6276
|
+
lineNumber++;
|
|
6277
|
+
if (!line.trim()) continue;
|
|
6278
|
+
try {
|
|
6279
|
+
const example = JSON.parse(line);
|
|
6280
|
+
if (!example.goal) {
|
|
6281
|
+
result.errors.push({
|
|
6282
|
+
line: lineNumber,
|
|
6283
|
+
field: "goal",
|
|
6284
|
+
message: "Missing required field: goal"
|
|
6285
|
+
});
|
|
6286
|
+
result.valid = false;
|
|
6287
|
+
}
|
|
6288
|
+
if (!example.context) {
|
|
6289
|
+
result.errors.push({
|
|
6290
|
+
line: lineNumber,
|
|
6291
|
+
field: "context",
|
|
6292
|
+
message: "Missing required field: context"
|
|
6293
|
+
});
|
|
6294
|
+
result.valid = false;
|
|
6295
|
+
}
|
|
6296
|
+
result.stats.totalExamples++;
|
|
6297
|
+
totalTokens += example.tokenCount || 0;
|
|
6298
|
+
totalFiles += example.selectedFiles?.length || 0;
|
|
6299
|
+
if (example.feedback) {
|
|
6300
|
+
result.stats.ratingDistribution[example.feedback.rating]++;
|
|
6301
|
+
} else {
|
|
6302
|
+
result.stats.ratingDistribution.unrated++;
|
|
6303
|
+
}
|
|
6304
|
+
if (example.meta?.language) {
|
|
6305
|
+
const lang = example.meta.language;
|
|
6306
|
+
result.stats.languageDistribution[lang] = (result.stats.languageDistribution[lang] || 0) + 1;
|
|
6307
|
+
}
|
|
6308
|
+
if (example.meta?.timestamp) {
|
|
6309
|
+
const date = new Date(example.meta.timestamp);
|
|
6310
|
+
if (date < result.stats.dateRange.earliest) {
|
|
6311
|
+
result.stats.dateRange.earliest = date;
|
|
6312
|
+
}
|
|
6313
|
+
if (date > result.stats.dateRange.latest) {
|
|
6314
|
+
result.stats.dateRange.latest = date;
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
} catch (error) {
|
|
6318
|
+
result.errors.push({
|
|
6319
|
+
line: lineNumber,
|
|
6320
|
+
field: "json",
|
|
6321
|
+
message: `Invalid JSON: ${error instanceof Error ? error.message : "Parse error"}`
|
|
6322
|
+
});
|
|
6323
|
+
result.valid = false;
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
if (result.stats.totalExamples > 0) {
|
|
6327
|
+
result.stats.avgTokenCount = Math.round(totalTokens / result.stats.totalExamples);
|
|
6328
|
+
result.stats.avgFilesPerExample = Math.round(totalFiles / result.stats.totalExamples * 10) / 10;
|
|
6329
|
+
}
|
|
6330
|
+
if (result.stats.totalExamples < 100) {
|
|
6331
|
+
result.warnings.push("Dataset has fewer than 100 examples. Consider collecting more data.");
|
|
6332
|
+
}
|
|
6333
|
+
if (result.stats.ratingDistribution.bad > result.stats.totalExamples * 0.2) {
|
|
6334
|
+
result.warnings.push("More than 20% of examples have negative ratings.");
|
|
6335
|
+
}
|
|
6336
|
+
return result;
|
|
6337
|
+
}
|
|
6338
|
+
/**
|
|
6339
|
+
* Filter examples based on config
|
|
6340
|
+
*/
|
|
6341
|
+
filterExamples(examples, config) {
|
|
6342
|
+
return examples.filter((example) => {
|
|
6343
|
+
if (config.minRating) {
|
|
6344
|
+
if (!example.feedback && !config.includeUnrated) {
|
|
6345
|
+
return false;
|
|
6346
|
+
}
|
|
6347
|
+
if (example.feedback) {
|
|
6348
|
+
const ratingOrder = { bad: 0, neutral: 1, good: 2 };
|
|
6349
|
+
if (ratingOrder[example.feedback.rating] < ratingOrder[config.minRating]) {
|
|
6350
|
+
return false;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
if (config.projectFilter && example.meta.projectName !== config.projectFilter) {
|
|
6355
|
+
return false;
|
|
6356
|
+
}
|
|
6357
|
+
if (config.languageFilter && example.meta.language !== config.languageFilter) {
|
|
6358
|
+
return false;
|
|
6359
|
+
}
|
|
6360
|
+
return true;
|
|
6361
|
+
});
|
|
6362
|
+
}
|
|
6363
|
+
/**
|
|
6364
|
+
* Format a single example
|
|
6365
|
+
*/
|
|
6366
|
+
formatExample(example, format) {
|
|
6367
|
+
switch (format.format) {
|
|
6368
|
+
case "openai":
|
|
6369
|
+
return JSON.stringify({
|
|
6370
|
+
messages: [
|
|
6371
|
+
{
|
|
6372
|
+
role: "system",
|
|
6373
|
+
content: "You are a code context selection assistant. Given a goal, select the most relevant files from a codebase."
|
|
6374
|
+
},
|
|
6375
|
+
{
|
|
6376
|
+
role: "user",
|
|
6377
|
+
content: `Goal: ${example.goal}
|
|
6378
|
+
|
|
6379
|
+
Project: ${example.meta.projectName} (${example.meta.language})`
|
|
6380
|
+
},
|
|
6381
|
+
{
|
|
6382
|
+
role: "assistant",
|
|
6383
|
+
content: `Selected files:
|
|
6384
|
+
${example.selectedFiles.map((f) => `- ${f}`).join("\n")}
|
|
6385
|
+
|
|
6386
|
+
${example.context}`
|
|
6387
|
+
}
|
|
6388
|
+
]
|
|
6389
|
+
});
|
|
6390
|
+
case "anthropic":
|
|
6391
|
+
return JSON.stringify({
|
|
6392
|
+
prompt: `
|
|
6393
|
+
|
|
6394
|
+
Human: Goal: ${example.goal}
|
|
6395
|
+
|
|
6396
|
+
Project: ${example.meta.projectName} (${example.meta.language})
|
|
6397
|
+
|
|
6398
|
+
Assistant:`,
|
|
6399
|
+
completion: ` Selected files:
|
|
6400
|
+
${example.selectedFiles.map((f) => `- ${f}`).join("\n")}
|
|
6401
|
+
|
|
6402
|
+
${example.context}`
|
|
6403
|
+
});
|
|
6404
|
+
case "csv":
|
|
6405
|
+
const escapeCsv = (s) => `"${s.replace(/"/g, '""')}"`;
|
|
6406
|
+
return [
|
|
6407
|
+
escapeCsv(example.id),
|
|
6408
|
+
escapeCsv(example.goal),
|
|
6409
|
+
escapeCsv(example.selectedFiles.join(";")),
|
|
6410
|
+
example.tokenCount,
|
|
6411
|
+
example.feedback?.rating || "",
|
|
6412
|
+
escapeCsv(example.meta.language)
|
|
6413
|
+
].join(",");
|
|
6414
|
+
case "jsonl":
|
|
6415
|
+
default:
|
|
6416
|
+
return JSON.stringify(example);
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6419
|
+
/**
|
|
6420
|
+
* Shuffle array
|
|
6421
|
+
*/
|
|
6422
|
+
shuffle(array) {
|
|
6423
|
+
const shuffled = [...array];
|
|
6424
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
6425
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
6426
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
6427
|
+
}
|
|
6428
|
+
return shuffled;
|
|
6429
|
+
}
|
|
6430
|
+
};
|
|
6431
|
+
function createDatasetFormatter() {
|
|
6432
|
+
return new DatasetFormatter();
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6435
|
+
// src/deployment/docker.ts
|
|
6436
|
+
function generateDockerCompose(config) {
|
|
6437
|
+
const services = {
|
|
6438
|
+
contextos: {
|
|
6439
|
+
image: "contextos/server:latest",
|
|
6440
|
+
container_name: config.name,
|
|
6441
|
+
ports: [`${config.port}:${config.port}`],
|
|
6442
|
+
environment: {
|
|
6443
|
+
NODE_ENV: config.environment,
|
|
6444
|
+
PORT: config.port.toString(),
|
|
6445
|
+
HOST: config.host,
|
|
6446
|
+
LOG_LEVEL: config.logging.level,
|
|
6447
|
+
LOG_FORMAT: config.logging.format,
|
|
6448
|
+
...config.environment
|
|
6449
|
+
},
|
|
6450
|
+
volumes: config.volumes?.map(
|
|
6451
|
+
(v) => `${v.source}:${v.target}${v.readonly ? ":ro" : ""}`
|
|
6452
|
+
) || [
|
|
6453
|
+
"./data:/data",
|
|
6454
|
+
"./.contextos:/app/.contextos"
|
|
6455
|
+
],
|
|
6456
|
+
restart: "unless-stopped",
|
|
6457
|
+
healthcheck: {
|
|
6458
|
+
test: ["CMD", "curl", "-f", `http://localhost:${config.port}/health`],
|
|
6459
|
+
interval: "30s",
|
|
6460
|
+
timeout: "10s",
|
|
6461
|
+
retries: 3,
|
|
6462
|
+
start_period: "10s"
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
};
|
|
6466
|
+
if (config.resources) {
|
|
6467
|
+
services.contextos = {
|
|
6468
|
+
...services.contextos,
|
|
6469
|
+
deploy: {
|
|
6470
|
+
resources: {
|
|
6471
|
+
limits: {
|
|
6472
|
+
memory: config.resources.memory,
|
|
6473
|
+
cpus: config.resources.cpus
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
};
|
|
6478
|
+
}
|
|
6479
|
+
if (config.redis) {
|
|
6480
|
+
services.redis = {
|
|
6481
|
+
image: "redis:7-alpine",
|
|
6482
|
+
container_name: `${config.name}-redis`,
|
|
6483
|
+
ports: [`${config.redis.port}:6379`],
|
|
6484
|
+
volumes: ["redis-data:/data"],
|
|
6485
|
+
restart: "unless-stopped"
|
|
6486
|
+
};
|
|
6487
|
+
services.contextos.depends_on = ["redis"];
|
|
6488
|
+
const env = services.contextos.environment;
|
|
6489
|
+
env.REDIS_HOST = "redis";
|
|
6490
|
+
env.REDIS_PORT = "6379";
|
|
6491
|
+
}
|
|
6492
|
+
const compose = {
|
|
6493
|
+
version: "3.8",
|
|
6494
|
+
services,
|
|
6495
|
+
volumes: {
|
|
6496
|
+
"contextos-data": {},
|
|
6497
|
+
...config.redis ? { "redis-data": {} } : {}
|
|
6498
|
+
}
|
|
6499
|
+
};
|
|
6500
|
+
return generateYaml(compose);
|
|
6501
|
+
}
|
|
6502
|
+
function generateDockerfile() {
|
|
6503
|
+
return `# ContextOS Server Dockerfile
|
|
6504
|
+
FROM node:20-alpine AS base
|
|
6505
|
+
|
|
6506
|
+
# Install dependencies only when needed
|
|
6507
|
+
FROM base AS deps
|
|
6508
|
+
WORKDIR /app
|
|
6509
|
+
COPY package*.json pnpm-lock.yaml ./
|
|
6510
|
+
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
|
6511
|
+
|
|
6512
|
+
# Build the application
|
|
6513
|
+
FROM base AS builder
|
|
6514
|
+
WORKDIR /app
|
|
6515
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
6516
|
+
COPY . .
|
|
6517
|
+
RUN npm run build
|
|
6518
|
+
|
|
6519
|
+
# Production image
|
|
6520
|
+
FROM base AS runner
|
|
6521
|
+
WORKDIR /app
|
|
6522
|
+
|
|
6523
|
+
ENV NODE_ENV=production
|
|
6524
|
+
|
|
6525
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
6526
|
+
RUN adduser --system --uid 1001 contextos
|
|
6527
|
+
|
|
6528
|
+
COPY --from=builder --chown=contextos:nodejs /app/dist ./dist
|
|
6529
|
+
COPY --from=builder --chown=contextos:nodejs /app/node_modules ./node_modules
|
|
6530
|
+
COPY --from=builder --chown=contextos:nodejs /app/package.json ./
|
|
6531
|
+
|
|
6532
|
+
USER contextos
|
|
6533
|
+
|
|
6534
|
+
EXPOSE 3000
|
|
6535
|
+
|
|
6536
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
|
|
6537
|
+
CMD curl -f http://localhost:3000/health || exit 1
|
|
6538
|
+
|
|
6539
|
+
CMD ["node", "dist/server.js"]
|
|
6540
|
+
`;
|
|
6541
|
+
}
|
|
6542
|
+
function generateHelmValues(config) {
|
|
6543
|
+
const values = {
|
|
6544
|
+
replicaCount: config.replicas,
|
|
6545
|
+
image: {
|
|
6546
|
+
repository: "contextos/server",
|
|
6547
|
+
tag: "latest",
|
|
6548
|
+
pullPolicy: "IfNotPresent"
|
|
6549
|
+
},
|
|
6550
|
+
service: {
|
|
6551
|
+
type: "ClusterIP",
|
|
6552
|
+
port: config.port
|
|
6553
|
+
},
|
|
6554
|
+
ingress: {
|
|
6555
|
+
enabled: config.ingress?.enabled || false,
|
|
6556
|
+
className: "nginx",
|
|
6557
|
+
annotations: config.ingress?.annotations || {},
|
|
6558
|
+
hosts: config.ingress ? [{
|
|
6559
|
+
host: config.ingress.host,
|
|
6560
|
+
paths: [{ path: "/", pathType: "Prefix" }]
|
|
6561
|
+
}] : [],
|
|
6562
|
+
tls: config.ingress?.tls ? [{
|
|
6563
|
+
secretName: `${config.name}-tls`,
|
|
6564
|
+
hosts: [config.ingress.host]
|
|
6565
|
+
}] : []
|
|
6566
|
+
},
|
|
6567
|
+
resources: config.resources,
|
|
6568
|
+
env: {
|
|
6569
|
+
NODE_ENV: config.environment,
|
|
6570
|
+
LOG_LEVEL: config.logging.level
|
|
6571
|
+
},
|
|
6572
|
+
serviceAccount: {
|
|
6573
|
+
create: !!config.serviceAccount,
|
|
6574
|
+
name: config.serviceAccount || ""
|
|
6575
|
+
},
|
|
6576
|
+
podAnnotations: config.podAnnotations || {}
|
|
6577
|
+
};
|
|
6578
|
+
return generateYaml(values);
|
|
6579
|
+
}
|
|
6580
|
+
function generateK8sDeployment(config) {
|
|
6581
|
+
const deployment = {
|
|
6582
|
+
apiVersion: "apps/v1",
|
|
6583
|
+
kind: "Deployment",
|
|
6584
|
+
metadata: {
|
|
6585
|
+
name: config.name,
|
|
6586
|
+
namespace: config.namespace,
|
|
6587
|
+
labels: {
|
|
6588
|
+
"app.kubernetes.io/name": config.name,
|
|
6589
|
+
"app.kubernetes.io/instance": config.name
|
|
6590
|
+
}
|
|
6591
|
+
},
|
|
6592
|
+
spec: {
|
|
6593
|
+
replicas: config.replicas,
|
|
6594
|
+
selector: {
|
|
6595
|
+
matchLabels: {
|
|
6596
|
+
"app.kubernetes.io/name": config.name,
|
|
6597
|
+
"app.kubernetes.io/instance": config.name
|
|
6598
|
+
}
|
|
6599
|
+
},
|
|
6600
|
+
template: {
|
|
6601
|
+
metadata: {
|
|
6602
|
+
labels: {
|
|
6603
|
+
"app.kubernetes.io/name": config.name,
|
|
6604
|
+
"app.kubernetes.io/instance": config.name
|
|
6605
|
+
},
|
|
6606
|
+
annotations: config.podAnnotations || {}
|
|
6607
|
+
},
|
|
6608
|
+
spec: {
|
|
6609
|
+
serviceAccountName: config.serviceAccount,
|
|
6610
|
+
containers: [{
|
|
6611
|
+
name: config.name,
|
|
6612
|
+
image: "contextos/server:latest",
|
|
6613
|
+
ports: [{ containerPort: config.port }],
|
|
6614
|
+
env: [
|
|
6615
|
+
{ name: "NODE_ENV", value: config.environment },
|
|
6616
|
+
{ name: "PORT", value: config.port.toString() },
|
|
6617
|
+
{ name: "LOG_LEVEL", value: config.logging.level }
|
|
6618
|
+
],
|
|
6619
|
+
resources: config.resources,
|
|
6620
|
+
livenessProbe: {
|
|
6621
|
+
httpGet: { path: "/health", port: config.port },
|
|
6622
|
+
initialDelaySeconds: 10,
|
|
6623
|
+
periodSeconds: 30
|
|
6624
|
+
},
|
|
6625
|
+
readinessProbe: {
|
|
6626
|
+
httpGet: { path: "/health", port: config.port },
|
|
6627
|
+
initialDelaySeconds: 5,
|
|
6628
|
+
periodSeconds: 10
|
|
6629
|
+
}
|
|
6630
|
+
}]
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
};
|
|
6635
|
+
return generateYaml(deployment);
|
|
6636
|
+
}
|
|
6637
|
+
function checkHealth(config) {
|
|
6638
|
+
const checks = [];
|
|
6639
|
+
let overallHealthy = true;
|
|
6640
|
+
try {
|
|
6641
|
+
checks.push({
|
|
6642
|
+
name: "database",
|
|
6643
|
+
status: "pass",
|
|
6644
|
+
message: `${config.database.type} connection OK`
|
|
6645
|
+
});
|
|
6646
|
+
} catch (error) {
|
|
6647
|
+
checks.push({
|
|
6648
|
+
name: "database",
|
|
6649
|
+
status: "fail",
|
|
6650
|
+
message: error instanceof Error ? error.message : "Connection failed"
|
|
6651
|
+
});
|
|
6652
|
+
overallHealthy = false;
|
|
6653
|
+
}
|
|
6654
|
+
checks.push({
|
|
6655
|
+
name: "filesystem",
|
|
6656
|
+
status: "pass",
|
|
6657
|
+
message: "Write access OK"
|
|
6658
|
+
});
|
|
6659
|
+
return {
|
|
6660
|
+
status: overallHealthy ? "healthy" : "unhealthy",
|
|
6661
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
6662
|
+
version: "2.0.0",
|
|
6663
|
+
uptime: process.uptime(),
|
|
6664
|
+
checks
|
|
6665
|
+
};
|
|
6666
|
+
}
|
|
6667
|
+
function validateLicense(key) {
|
|
6668
|
+
if (!key || key.length < 10) {
|
|
6669
|
+
return null;
|
|
6670
|
+
}
|
|
6671
|
+
const parts = key.split("-");
|
|
6672
|
+
if (parts.length < 6 || parts[0] !== "CTXOS") {
|
|
6673
|
+
return null;
|
|
6674
|
+
}
|
|
6675
|
+
const typeMap = {
|
|
6676
|
+
"T": "trial",
|
|
6677
|
+
"TM": "team",
|
|
6678
|
+
"E": "enterprise"
|
|
6679
|
+
};
|
|
6680
|
+
return {
|
|
6681
|
+
key,
|
|
6682
|
+
type: typeMap[parts[1]] || "trial",
|
|
6683
|
+
organization: parts[2],
|
|
6684
|
+
maxSeats: parseInt(parts[3]) || 5,
|
|
6685
|
+
expiresAt: new Date(parseInt(parts[4]) * 1e3),
|
|
6686
|
+
features: ["core", "analytics", "sync"],
|
|
6687
|
+
signature: parts.slice(5).join("-")
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
function generateYaml(obj, indent = 0) {
|
|
6691
|
+
const spaces = " ".repeat(indent);
|
|
6692
|
+
if (obj === null || obj === void 0) {
|
|
6693
|
+
return "null";
|
|
6694
|
+
}
|
|
6695
|
+
if (typeof obj === "string") {
|
|
6696
|
+
return obj.includes(":") || obj.includes("#") ? `"${obj}"` : obj;
|
|
6697
|
+
}
|
|
6698
|
+
if (typeof obj === "number" || typeof obj === "boolean") {
|
|
6699
|
+
return String(obj);
|
|
6700
|
+
}
|
|
6701
|
+
if (Array.isArray(obj)) {
|
|
6702
|
+
if (obj.length === 0) return "[]";
|
|
6703
|
+
return obj.map((item) => {
|
|
6704
|
+
if (typeof item === "object" && item !== null) {
|
|
6705
|
+
const yaml = generateYaml(item, indent + 1);
|
|
6706
|
+
return `${spaces}- ${yaml.trim().replace(/\n/g, `
|
|
6707
|
+
${spaces} `)}`;
|
|
6708
|
+
}
|
|
6709
|
+
return `${spaces}- ${generateYaml(item, indent)}`;
|
|
6710
|
+
}).join("\n");
|
|
6711
|
+
}
|
|
6712
|
+
if (typeof obj === "object") {
|
|
6713
|
+
const entries = Object.entries(obj);
|
|
6714
|
+
if (entries.length === 0) return "{}";
|
|
6715
|
+
return entries.map(([key, value]) => {
|
|
6716
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
6717
|
+
return `${spaces}${key}:
|
|
6718
|
+
${generateYaml(value, indent + 1)}`;
|
|
6719
|
+
}
|
|
6720
|
+
if (Array.isArray(value)) {
|
|
6721
|
+
return `${spaces}${key}:
|
|
6722
|
+
${generateYaml(value, indent + 1)}`;
|
|
6723
|
+
}
|
|
6724
|
+
return `${spaces}${key}: ${generateYaml(value, indent)}`;
|
|
6725
|
+
}).join("\n");
|
|
6726
|
+
}
|
|
6727
|
+
return String(obj);
|
|
6728
|
+
}
|
|
6729
|
+
|
|
5537
6730
|
// src/errors.ts
|
|
5538
6731
|
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
5539
6732
|
ErrorCode2["CONFIG_NOT_FOUND"] = "E1001";
|
|
@@ -5880,6 +7073,7 @@ export {
|
|
|
5880
7073
|
ContextYamlSchema,
|
|
5881
7074
|
DEFAULT_RLM_CONFIG,
|
|
5882
7075
|
DEFAULT_SYNC_CONFIG,
|
|
7076
|
+
DatasetFormatter,
|
|
5883
7077
|
DependencyGraph,
|
|
5884
7078
|
DriftDetector,
|
|
5885
7079
|
E2EEncryption,
|
|
@@ -5896,6 +7090,8 @@ export {
|
|
|
5896
7090
|
MODEL_SPECIFIC_ADDENDUM,
|
|
5897
7091
|
MetaSchema,
|
|
5898
7092
|
OpenAIAdapter,
|
|
7093
|
+
PluginManager,
|
|
7094
|
+
PluginRegistry,
|
|
5899
7095
|
ProjectConfigSchema,
|
|
5900
7096
|
ProposalManager,
|
|
5901
7097
|
RBACManager,
|
|
@@ -5907,24 +7103,30 @@ export {
|
|
|
5907
7103
|
TeamSync,
|
|
5908
7104
|
TemplateManager,
|
|
5909
7105
|
TokenBudget,
|
|
7106
|
+
TrainingDataCollector,
|
|
5910
7107
|
VectorStore,
|
|
5911
7108
|
Watchdog,
|
|
5912
7109
|
calculateCost,
|
|
7110
|
+
checkHealth,
|
|
5913
7111
|
chunkCode,
|
|
5914
7112
|
createAnthropicAdapter,
|
|
5915
7113
|
createBlackboard,
|
|
5916
7114
|
createContextAPI,
|
|
7115
|
+
createDatasetFormatter,
|
|
5917
7116
|
createGeminiClient,
|
|
5918
7117
|
createInitialUserMessage,
|
|
5919
7118
|
createLogger,
|
|
5920
7119
|
createObservationMessage,
|
|
5921
7120
|
createOpenAIAdapter,
|
|
7121
|
+
createPluginManager,
|
|
7122
|
+
createPluginRegistry,
|
|
5922
7123
|
createProposalManager,
|
|
5923
7124
|
createRLMEngine,
|
|
5924
7125
|
createSandbox,
|
|
5925
7126
|
createSandboxContext,
|
|
5926
7127
|
createScopeManager,
|
|
5927
7128
|
createSubAgentResultMessage,
|
|
7129
|
+
createTrainingDataCollector,
|
|
5928
7130
|
createWatchdog,
|
|
5929
7131
|
detectDrift,
|
|
5930
7132
|
detectProjectType,
|
|
@@ -5934,6 +7136,10 @@ export {
|
|
|
5934
7136
|
extractFunctionsRegex,
|
|
5935
7137
|
extractImportsRegex,
|
|
5936
7138
|
findContextosRoot,
|
|
7139
|
+
generateDockerCompose,
|
|
7140
|
+
generateDockerfile,
|
|
7141
|
+
generateHelmValues,
|
|
7142
|
+
generateK8sDeployment,
|
|
5937
7143
|
generateSystemPrompt,
|
|
5938
7144
|
getContextBuilder,
|
|
5939
7145
|
getGlobalBlackboard,
|
|
@@ -5961,5 +7167,6 @@ export {
|
|
|
5961
7167
|
saveContextYaml,
|
|
5962
7168
|
setGlobalLogger,
|
|
5963
7169
|
splitContextToFiles,
|
|
5964
|
-
validateCode
|
|
7170
|
+
validateCode,
|
|
7171
|
+
validateLicense
|
|
5965
7172
|
};
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
2
|
+
"name": "@contextos/core",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Core engine for ContextOS - context management, parsing, and ranking",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@xenova/transformers": "^2.17.0",
|
|
16
|
+
"better-sqlite3": "^11.0.0",
|
|
17
|
+
"chokidar": "^3.6.0",
|
|
18
|
+
"glob": "^10.3.0",
|
|
19
|
+
"tiktoken": "^1.0.15",
|
|
20
|
+
"web-tree-sitter": "^0.22.0",
|
|
21
|
+
"yaml": "^2.4.0",
|
|
22
|
+
"zod": "^3.23.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/better-sqlite3": "^7.6.9",
|
|
26
|
+
"eslint": "^8.57.0",
|
|
27
|
+
"rimraf": "^5.0.5",
|
|
28
|
+
"tsup": "^8.0.2",
|
|
29
|
+
"typescript": "^5.4.0",
|
|
30
|
+
"vitest": "^1.4.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
41
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:coverage": "vitest run --coverage",
|
|
45
|
+
"lint": "eslint src/",
|
|
46
|
+
"clean": "rimraf dist"
|
|
47
|
+
}
|
|
48
48
|
}
|