@olane/os 0.8.16 → 0.8.17
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/src/address/address-factory.d.ts +23 -0
- package/dist/src/address/address-factory.d.ts.map +1 -0
- package/dist/src/address/address-factory.js +42 -0
- package/dist/src/address-book/address-book.d.ts +32 -0
- package/dist/src/address-book/address-book.d.ts.map +1 -0
- package/dist/src/address-book/address-book.js +65 -0
- package/dist/src/auth/os-auth-guard.d.ts +20 -0
- package/dist/src/auth/os-auth-guard.d.ts.map +1 -0
- package/dist/src/auth/os-auth-guard.js +39 -0
- package/dist/src/identity/copass-id.d.ts +23 -0
- package/dist/src/identity/copass-id.d.ts.map +1 -0
- package/dist/src/identity/copass-id.js +37 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -0
- package/dist/src/lifecycle/os-lifecycle.d.ts +36 -0
- package/dist/src/lifecycle/os-lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle/os-lifecycle.js +99 -0
- package/dist/src/mcp/os-mcp-server.d.ts +29 -0
- package/dist/src/mcp/os-mcp-server.d.ts.map +1 -0
- package/dist/src/mcp/os-mcp-server.js +244 -0
- package/dist/src/memory/memory-harness.d.ts +38 -0
- package/dist/src/memory/memory-harness.d.ts.map +1 -0
- package/dist/src/memory/memory-harness.js +100 -0
- package/dist/src/nodes/relay-node.d.ts +31 -0
- package/dist/src/nodes/relay-node.d.ts.map +1 -0
- package/dist/src/nodes/relay-node.js +85 -0
- package/dist/src/o-olane-os/interfaces/o-os.config.d.ts +15 -1
- package/dist/src/o-olane-os/interfaces/o-os.config.d.ts.map +1 -1
- package/dist/src/o-olane-os/o-os.d.ts +22 -0
- package/dist/src/o-olane-os/o-os.d.ts.map +1 -1
- package/dist/src/o-olane-os/o-os.js +109 -9
- package/dist/src/tools/filesystem.tool.d.ts +24 -0
- package/dist/src/tools/filesystem.tool.d.ts.map +1 -0
- package/dist/src/tools/filesystem.tool.js +163 -0
- package/dist/src/utils/config.d.ts +7 -1
- package/dist/src/utils/config.d.ts.map +1 -1
- package/dist/src/utils/config.js +40 -19
- package/dist/src/vector-store/copass-vector-store.tool.d.ts +29 -0
- package/dist/src/vector-store/copass-vector-store.tool.d.ts.map +1 -0
- package/dist/src/vector-store/copass-vector-store.tool.js +152 -0
- package/dist/src/worlds/world-instance.tool.d.ts +30 -0
- package/dist/src/worlds/world-instance.tool.d.ts.map +1 -0
- package/dist/src/worlds/world-instance.tool.js +179 -0
- package/dist/src/worlds/world-manager.tool.d.ts +61 -0
- package/dist/src/worlds/world-manager.tool.d.ts.map +1 -0
- package/dist/src/worlds/world-manager.tool.js +270 -0
- package/dist/src/worlds/world.types.d.ts +23 -0
- package/dist/src/worlds/world.types.d.ts.map +1 -0
- package/dist/src/worlds/world.types.js +1 -0
- package/package.json +15 -14
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { OlaneOSSystemStatus } from './enum/o-os.status-enum.js';
|
|
2
2
|
import touch from 'touch';
|
|
3
3
|
import { readFile } from 'fs/promises';
|
|
4
|
+
import { mkdir } from 'fs/promises';
|
|
5
|
+
import * as path from 'path';
|
|
4
6
|
import { oLeaderNode } from '@olane/o-leader';
|
|
5
7
|
import { oAddress, oObject } from '@olane/o-core';
|
|
6
8
|
import { NodeType } from '@olane/o-core';
|
|
@@ -10,6 +12,10 @@ import { ConfigManager } from '../utils/config.js';
|
|
|
10
12
|
import { oLaneTool } from '@olane/o-lane';
|
|
11
13
|
import { oLaneStorage } from '@olane/o-storage';
|
|
12
14
|
import { oNodeAddress } from '@olane/o-node';
|
|
15
|
+
import { WorldManagerTool } from '../worlds/world-manager.tool.js';
|
|
16
|
+
import { AddressBook } from '../address-book/address-book.js';
|
|
17
|
+
import { MemoryHarness } from '../memory/memory-harness.js';
|
|
18
|
+
import { CopassVectorStoreTool } from '../vector-store/copass-vector-store.tool.js';
|
|
13
19
|
export class OlaneOS extends oObject {
|
|
14
20
|
constructor(config) {
|
|
15
21
|
super();
|
|
@@ -17,6 +23,9 @@ export class OlaneOS extends oObject {
|
|
|
17
23
|
this.nodes = []; // clones of node for scale
|
|
18
24
|
this.rootLeader = null; // the root leader node
|
|
19
25
|
this.roundRobinIndex = 0;
|
|
26
|
+
this.worldManager = null;
|
|
27
|
+
this.addressBook = null;
|
|
28
|
+
this.memory = null;
|
|
20
29
|
this.config = config;
|
|
21
30
|
}
|
|
22
31
|
entryNode() {
|
|
@@ -50,14 +59,17 @@ export class OlaneOS extends oObject {
|
|
|
50
59
|
// Then we'll migrate to using o://os-config for persistence
|
|
51
60
|
if (this.config.configFilePath) {
|
|
52
61
|
try {
|
|
62
|
+
// Ensure parent directory exists before touch
|
|
63
|
+
await mkdir(path.dirname(this.config.configFilePath), { recursive: true });
|
|
53
64
|
await touch(this.config.configFilePath);
|
|
54
65
|
this.logger.debug('Config file path: ' + this.config.configFilePath);
|
|
55
66
|
// let's load the config
|
|
56
67
|
const config = await readFile(this.config.configFilePath, 'utf8');
|
|
57
68
|
this.logger.debug('Config file contents: ' + config);
|
|
58
|
-
if (config
|
|
59
|
-
//
|
|
60
|
-
|
|
69
|
+
if (!config || config.trim().length === 0) {
|
|
70
|
+
// Empty file (fresh OS) — use the in-memory config as-is
|
|
71
|
+
this.logger.debug('Empty config file, using constructor config');
|
|
72
|
+
return;
|
|
61
73
|
}
|
|
62
74
|
const json = JSON.parse(config);
|
|
63
75
|
this.logger.debug('Config file parsed: ' + json);
|
|
@@ -246,6 +258,94 @@ export class OlaneOS extends oObject {
|
|
|
246
258
|
// Don't throw - allow OS to continue even if replay fails
|
|
247
259
|
}
|
|
248
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Initialize world manager, address book, and memory harness
|
|
263
|
+
* after the leader and nodes are started.
|
|
264
|
+
*/
|
|
265
|
+
async initOSServices() {
|
|
266
|
+
const instanceName = (await this.getOSInstanceName()) ||
|
|
267
|
+
this.config.network?.name ||
|
|
268
|
+
'default';
|
|
269
|
+
// Address book
|
|
270
|
+
this.addressBook = new AddressBook(instanceName);
|
|
271
|
+
await this.addressBook.load();
|
|
272
|
+
// OS service nodes (children of root leader)
|
|
273
|
+
if (this.rootLeader) {
|
|
274
|
+
// Memory harness — shells out to `olane ingest/copass` CLI
|
|
275
|
+
this.memory = new MemoryHarness();
|
|
276
|
+
this.logger.debug('Memory harness initialized');
|
|
277
|
+
// Vector store — routes to Copass backend so index_network
|
|
278
|
+
// and any o://vector-store calls go through the memory harness
|
|
279
|
+
const vectorStore = new CopassVectorStoreTool({
|
|
280
|
+
address: new oAddress('o://vector-store'),
|
|
281
|
+
leader: this.rootLeader.address,
|
|
282
|
+
parent: this.rootLeader.address,
|
|
283
|
+
_allowNestedAddress: true,
|
|
284
|
+
});
|
|
285
|
+
this.rootLeader.addChildNode(vectorStore);
|
|
286
|
+
await vectorStore.start();
|
|
287
|
+
this.logger.debug('Copass vector store started');
|
|
288
|
+
// World manager
|
|
289
|
+
this.worldManager = new WorldManagerTool({
|
|
290
|
+
leader: this.rootLeader.address,
|
|
291
|
+
parent: this.rootLeader.address,
|
|
292
|
+
systemName: instanceName,
|
|
293
|
+
_allowNestedAddress: true,
|
|
294
|
+
});
|
|
295
|
+
this.rootLeader.addChildNode(this.worldManager);
|
|
296
|
+
await this.worldManager.start();
|
|
297
|
+
this.logger.debug('World manager started');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Only index if `indexCompletedAt` is not set in the instance config.
|
|
302
|
+
* Records the timestamp after successful indexing.
|
|
303
|
+
*/
|
|
304
|
+
async indexOnceInBackground() {
|
|
305
|
+
const osName = await this.getOSInstanceName();
|
|
306
|
+
if (osName) {
|
|
307
|
+
const existing = await ConfigManager.getOSConfig(osName);
|
|
308
|
+
if (existing?.indexCompletedAt) {
|
|
309
|
+
this.logger.debug('Network already indexed, skipping');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
await this.indexNetworkParallel();
|
|
314
|
+
// Record completion
|
|
315
|
+
if (osName) {
|
|
316
|
+
const config = await ConfigManager.getOSConfig(osName);
|
|
317
|
+
if (config) {
|
|
318
|
+
await ConfigManager.saveOSConfig({
|
|
319
|
+
...config,
|
|
320
|
+
indexCompletedAt: new Date().toISOString(),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Index all nodes in the network in parallel.
|
|
327
|
+
* Each node indexes itself + its children concurrently,
|
|
328
|
+
* rather than the default sequential tree walk.
|
|
329
|
+
*/
|
|
330
|
+
async indexNetworkParallel() {
|
|
331
|
+
const allNodes = [
|
|
332
|
+
...this.leaders,
|
|
333
|
+
...this.nodes,
|
|
334
|
+
];
|
|
335
|
+
this.logger.debug(`Indexing ${allNodes.length} node(s) in parallel...`);
|
|
336
|
+
await Promise.allSettled(allNodes.map(async (node) => {
|
|
337
|
+
try {
|
|
338
|
+
await node.use(node.address, {
|
|
339
|
+
method: 'index_network',
|
|
340
|
+
params: {},
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
this.logger.warn(`Failed to index ${node.address.toString()}: ${err}`);
|
|
345
|
+
}
|
|
346
|
+
}));
|
|
347
|
+
this.logger.debug('Parallel network indexing complete');
|
|
348
|
+
}
|
|
249
349
|
async use(oAddress, params) {
|
|
250
350
|
const entryNode = this.entryNode();
|
|
251
351
|
if (!entryNode) {
|
|
@@ -265,6 +365,9 @@ export class OlaneOS extends oObject {
|
|
|
265
365
|
this.logger.debug('Leaders started...');
|
|
266
366
|
await this.startNodes(NodeType.NODE);
|
|
267
367
|
this.logger.debug('Nodes started...');
|
|
368
|
+
// Initialize world manager, address book, and memory harness
|
|
369
|
+
await this.initOSServices();
|
|
370
|
+
this.logger.debug('OS services initialized...');
|
|
268
371
|
// Load config from o://os-config storage backend (after tools are initialized)
|
|
269
372
|
// This merges any saved lanes from persistent storage
|
|
270
373
|
await this.loadConfigFromStorage();
|
|
@@ -273,14 +376,11 @@ export class OlaneOS extends oObject {
|
|
|
273
376
|
await this.runSavedPlans();
|
|
274
377
|
this.logger.debug('Saved plans run...');
|
|
275
378
|
this.logger.debug('OS instance started...');
|
|
276
|
-
|
|
379
|
+
this.status = OlaneOSSystemStatus.RUNNING;
|
|
380
|
+
// Index the network once (first boot only), in the background.
|
|
277
381
|
if (!this.config.noIndexNetwork) {
|
|
278
|
-
|
|
279
|
-
method: 'index_network',
|
|
280
|
-
params: {},
|
|
281
|
-
});
|
|
382
|
+
this.indexOnceInBackground().catch((err) => this.logger.error('Background network indexing failed:', err));
|
|
282
383
|
}
|
|
283
|
-
this.status = OlaneOSSystemStatus.RUNNING;
|
|
284
384
|
return {
|
|
285
385
|
peerId: this.rootLeader?.peerId.toString() || '',
|
|
286
386
|
transports: this.rootLeader?.transports || [],
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { oNodeConfig } from '@olane/o-node';
|
|
2
|
+
import { oLaneTool } from '@olane/o-lane';
|
|
3
|
+
import { oRequest } from '@olane/o-core';
|
|
4
|
+
import type { ToolResult } from '@olane/o-tool';
|
|
5
|
+
/**
|
|
6
|
+
* FilesystemTool — an oLaneTool that provides scoped filesystem access
|
|
7
|
+
* for a world. Each world gets its own FilesystemTool as a child node.
|
|
8
|
+
*
|
|
9
|
+
* The tool maintains a list of allowed directory paths (addresses).
|
|
10
|
+
* All file operations are restricted to these directories.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FilesystemTool extends oLaneTool {
|
|
13
|
+
private allowedPaths;
|
|
14
|
+
constructor(config: oNodeConfig, allowedPaths?: string[]);
|
|
15
|
+
getAllowedPaths(): string[];
|
|
16
|
+
_tool_add_path(request: oRequest): Promise<ToolResult>;
|
|
17
|
+
_tool_remove_path(request: oRequest): Promise<ToolResult>;
|
|
18
|
+
_tool_list_paths(_request: oRequest): Promise<ToolResult>;
|
|
19
|
+
_tool_read_file(request: oRequest): Promise<ToolResult>;
|
|
20
|
+
_tool_list_files(request: oRequest): Promise<ToolResult>;
|
|
21
|
+
_tool_stat_path(request: oRequest): Promise<ToolResult>;
|
|
22
|
+
private isWithinAllowedPath;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=filesystem.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/filesystem.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAY,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,OAAO,CAAC,YAAY,CAAgB;gBAExB,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAyDxD,eAAe,IAAI,MAAM,EAAE;IAMrB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBtD,iBAAiB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAQzD,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAMzD,eAAe,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBvD,gBAAgB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAwBxD,eAAe,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAyB7D,OAAO,CAAC,mBAAmB;CAK5B"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { oLaneTool } from '@olane/o-lane';
|
|
2
|
+
import { readFile, readdir, stat, access } from 'fs/promises';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* FilesystemTool — an oLaneTool that provides scoped filesystem access
|
|
6
|
+
* for a world. Each world gets its own FilesystemTool as a child node.
|
|
7
|
+
*
|
|
8
|
+
* The tool maintains a list of allowed directory paths (addresses).
|
|
9
|
+
* All file operations are restricted to these directories.
|
|
10
|
+
*/
|
|
11
|
+
export class FilesystemTool extends oLaneTool {
|
|
12
|
+
constructor(config, allowedPaths) {
|
|
13
|
+
super({
|
|
14
|
+
...config,
|
|
15
|
+
description: 'Scoped filesystem access for a world',
|
|
16
|
+
methods: {
|
|
17
|
+
add_path: {
|
|
18
|
+
name: 'add_path',
|
|
19
|
+
description: 'Add a directory path to the allowed list',
|
|
20
|
+
parameters: [
|
|
21
|
+
{ name: 'path', type: 'string', required: true, description: 'Absolute directory path' },
|
|
22
|
+
],
|
|
23
|
+
dependencies: [],
|
|
24
|
+
},
|
|
25
|
+
remove_path: {
|
|
26
|
+
name: 'remove_path',
|
|
27
|
+
description: 'Remove a directory path from the allowed list',
|
|
28
|
+
parameters: [
|
|
29
|
+
{ name: 'path', type: 'string', required: true, description: 'Path to remove' },
|
|
30
|
+
],
|
|
31
|
+
dependencies: [],
|
|
32
|
+
},
|
|
33
|
+
list_paths: {
|
|
34
|
+
name: 'list_paths',
|
|
35
|
+
description: 'List all allowed directory paths',
|
|
36
|
+
parameters: [],
|
|
37
|
+
dependencies: [],
|
|
38
|
+
},
|
|
39
|
+
read_file: {
|
|
40
|
+
name: 'read_file',
|
|
41
|
+
description: 'Read a file within an allowed path',
|
|
42
|
+
parameters: [
|
|
43
|
+
{ name: 'path', type: 'string', required: true, description: 'File path to read' },
|
|
44
|
+
],
|
|
45
|
+
dependencies: [],
|
|
46
|
+
},
|
|
47
|
+
list_files: {
|
|
48
|
+
name: 'list_files',
|
|
49
|
+
description: 'List files in a directory within an allowed path',
|
|
50
|
+
parameters: [
|
|
51
|
+
{ name: 'path', type: 'string', required: true, description: 'Directory path' },
|
|
52
|
+
{ name: 'recursive', type: 'boolean', required: false, description: 'List recursively' },
|
|
53
|
+
],
|
|
54
|
+
dependencies: [],
|
|
55
|
+
},
|
|
56
|
+
stat_path: {
|
|
57
|
+
name: 'stat_path',
|
|
58
|
+
description: 'Get file/directory info within an allowed path',
|
|
59
|
+
parameters: [
|
|
60
|
+
{ name: 'path', type: 'string', required: true, description: 'Path to stat' },
|
|
61
|
+
],
|
|
62
|
+
dependencies: [],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
this.allowedPaths = [];
|
|
67
|
+
this.allowedPaths = allowedPaths || [];
|
|
68
|
+
}
|
|
69
|
+
getAllowedPaths() {
|
|
70
|
+
return [...this.allowedPaths];
|
|
71
|
+
}
|
|
72
|
+
// ── Path management ────────────────────────────────────────
|
|
73
|
+
async _tool_add_path(request) {
|
|
74
|
+
const { path: dirPath } = request.params;
|
|
75
|
+
const resolved = path.resolve(dirPath);
|
|
76
|
+
try {
|
|
77
|
+
await access(resolved);
|
|
78
|
+
const stats = await stat(resolved);
|
|
79
|
+
if (!stats.isDirectory()) {
|
|
80
|
+
return { success: false, error: `'${resolved}' is not a directory` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return { success: false, error: `'${resolved}' does not exist or is not accessible` };
|
|
85
|
+
}
|
|
86
|
+
if (!this.allowedPaths.includes(resolved)) {
|
|
87
|
+
this.allowedPaths.push(resolved);
|
|
88
|
+
}
|
|
89
|
+
return { success: true, path: resolved };
|
|
90
|
+
}
|
|
91
|
+
async _tool_remove_path(request) {
|
|
92
|
+
const { path: dirPath } = request.params;
|
|
93
|
+
const resolved = path.resolve(dirPath);
|
|
94
|
+
const before = this.allowedPaths.length;
|
|
95
|
+
this.allowedPaths = this.allowedPaths.filter((p) => p !== resolved);
|
|
96
|
+
return { success: true, removed: this.allowedPaths.length < before };
|
|
97
|
+
}
|
|
98
|
+
async _tool_list_paths(_request) {
|
|
99
|
+
return { success: true, paths: [...this.allowedPaths] };
|
|
100
|
+
}
|
|
101
|
+
// ── File operations (scoped to allowed paths) ──────────────
|
|
102
|
+
async _tool_read_file(request) {
|
|
103
|
+
const { path: filePath } = request.params;
|
|
104
|
+
const resolved = path.resolve(filePath);
|
|
105
|
+
if (!this.isWithinAllowedPath(resolved)) {
|
|
106
|
+
return { success: false, error: `'${resolved}' is outside allowed paths` };
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const content = await readFile(resolved, 'utf8');
|
|
110
|
+
return { success: true, content, path: resolved };
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return { success: false, error: `Failed to read: ${err}` };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async _tool_list_files(request) {
|
|
117
|
+
const { path: dirPath, recursive } = request.params;
|
|
118
|
+
const resolved = path.resolve(dirPath);
|
|
119
|
+
if (!this.isWithinAllowedPath(resolved)) {
|
|
120
|
+
return { success: false, error: `'${resolved}' is outside allowed paths` };
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const entries = await readdir(resolved, {
|
|
124
|
+
withFileTypes: true,
|
|
125
|
+
recursive: !!recursive,
|
|
126
|
+
});
|
|
127
|
+
const files = entries.map((e) => ({
|
|
128
|
+
name: e.name,
|
|
129
|
+
isDirectory: e.isDirectory(),
|
|
130
|
+
path: path.join(e.parentPath || resolved, e.name),
|
|
131
|
+
}));
|
|
132
|
+
return { success: true, files };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
return { success: false, error: `Failed to list: ${err}` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async _tool_stat_path(request) {
|
|
139
|
+
const { path: targetPath } = request.params;
|
|
140
|
+
const resolved = path.resolve(targetPath);
|
|
141
|
+
if (!this.isWithinAllowedPath(resolved)) {
|
|
142
|
+
return { success: false, error: `'${resolved}' is outside allowed paths` };
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const stats = await stat(resolved);
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
path: resolved,
|
|
149
|
+
isFile: stats.isFile(),
|
|
150
|
+
isDirectory: stats.isDirectory(),
|
|
151
|
+
size: stats.size,
|
|
152
|
+
modified: stats.mtime.toISOString(),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return { success: false, error: `Failed to stat: ${err}` };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
160
|
+
isWithinAllowedPath(filePath) {
|
|
161
|
+
return this.allowedPaths.some((allowed) => filePath === allowed || filePath.startsWith(allowed + path.sep));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -14,6 +14,12 @@ export interface OlaneOSInstanceConfig {
|
|
|
14
14
|
transports?: string[];
|
|
15
15
|
oNetworkConfig?: OlaneOSConfig;
|
|
16
16
|
remoteLeaderAddress?: oNodeAddress;
|
|
17
|
+
/** Copass ID linked to this OS instance. */
|
|
18
|
+
copassId?: string;
|
|
19
|
+
/** World ID if this instance represents a world. */
|
|
20
|
+
worldId?: string;
|
|
21
|
+
/** Timestamp of first completed network index. */
|
|
22
|
+
indexCompletedAt?: string;
|
|
17
23
|
}
|
|
18
24
|
export interface CLIConfig {
|
|
19
25
|
instancesPath: string;
|
|
@@ -29,7 +35,7 @@ export declare class ConfigManager {
|
|
|
29
35
|
static getConfig(): Promise<CLIConfig>;
|
|
30
36
|
static saveConfig(config: Partial<CLIConfig>): Promise<void>;
|
|
31
37
|
static writeConfig(config: Partial<CLIConfig>): Promise<void>;
|
|
32
|
-
static getOSConfigFromPath(
|
|
38
|
+
static getOSConfigFromPath(p: string): Promise<OlaneOSInstanceConfig | null>;
|
|
33
39
|
static getOSConfig(name: string): Promise<OlaneOSInstanceConfig | null>;
|
|
34
40
|
static updateOSConfig(config: OlaneOSInstanceConfig): Promise<OlaneOSInstanceConfig | null>;
|
|
35
41
|
static saveOSConfig(config: OlaneOSInstanceConfig): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/utils/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,mBAAmB,CAAC,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/utils/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,mBAAmB,CAAC,EAAE,YAAY,CAAC;IACnC,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC5C;AAED,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAoB9C,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAuB;IAChD,OAAO,CAAC,MAAM,CAAC,aAAa,CAAyB;IACrD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAuB;WAEnC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASxC,MAAM,CAAC,gBAAgB,IAAI,SAAS;WAOvB,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC;WAM/B,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;WAOrD,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;WAItD,mBAAmB,CAC9B,CAAC,EAAE,MAAM,GACR,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;WAW3B,WAAW,CACtB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;WAY3B,cAAc,CACzB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;WAQ3B,YAAY,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;WAQ1D,eAAe,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;WAsBnD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM3D"}
|
package/dist/src/utils/config.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { readFile, writeFile, mkdir, readdir, rm, access } from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { DEFAULT_CONFIG_PATH, DEFAULT_INSTANCE_PATH, DEFAULT_CONFIG_FILE, } from '@olane/o-core';
|
|
4
4
|
export const CONFIG_FILE_NAME = 'config.json';
|
|
5
|
+
async function pathExists(p) {
|
|
6
|
+
try {
|
|
7
|
+
await access(p);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function readJson(p) {
|
|
15
|
+
const data = await readFile(p, 'utf8');
|
|
16
|
+
return JSON.parse(data);
|
|
17
|
+
}
|
|
18
|
+
async function writeJson(p, data, opts) {
|
|
19
|
+
await writeFile(p, JSON.stringify(data, null, opts?.spaces), 'utf8');
|
|
20
|
+
}
|
|
5
21
|
export class ConfigManager {
|
|
6
22
|
static async initialize() {
|
|
7
|
-
await
|
|
8
|
-
await
|
|
9
|
-
if (!(await
|
|
23
|
+
await mkdir(ConfigManager.configPath, { recursive: true });
|
|
24
|
+
await mkdir(ConfigManager.instancesPath, { recursive: true });
|
|
25
|
+
if (!(await pathExists(ConfigManager.configFile))) {
|
|
10
26
|
await this.writeConfig(this.getDefaultConfig());
|
|
11
27
|
}
|
|
12
28
|
}
|
|
@@ -18,7 +34,7 @@ export class ConfigManager {
|
|
|
18
34
|
}
|
|
19
35
|
static async getConfig() {
|
|
20
36
|
await this.initialize();
|
|
21
|
-
const config = await
|
|
37
|
+
const config = await readJson(ConfigManager.configFile);
|
|
22
38
|
return { ...this.getDefaultConfig(), ...config };
|
|
23
39
|
}
|
|
24
40
|
static async saveConfig(config) {
|
|
@@ -28,18 +44,25 @@ export class ConfigManager {
|
|
|
28
44
|
await this.writeConfig(newConfig);
|
|
29
45
|
}
|
|
30
46
|
static async writeConfig(config) {
|
|
31
|
-
await
|
|
47
|
+
await writeJson(ConfigManager.configFile, config, { spaces: 2 });
|
|
32
48
|
}
|
|
33
|
-
static async getOSConfigFromPath(
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
static async getOSConfigFromPath(p) {
|
|
50
|
+
try {
|
|
51
|
+
if (!(await pathExists(p)))
|
|
52
|
+
return null;
|
|
53
|
+
const raw = await readFile(p, 'utf8');
|
|
54
|
+
if (!raw || !raw.trim())
|
|
55
|
+
return null;
|
|
56
|
+
return JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
36
60
|
}
|
|
37
|
-
return null;
|
|
38
61
|
}
|
|
39
62
|
static async getOSConfig(name) {
|
|
40
63
|
const configPath = path.join(ConfigManager.instancesPath, name, CONFIG_FILE_NAME);
|
|
41
|
-
if (await
|
|
42
|
-
return await
|
|
64
|
+
if (await pathExists(configPath)) {
|
|
65
|
+
return await readJson(configPath);
|
|
43
66
|
}
|
|
44
67
|
return null;
|
|
45
68
|
}
|
|
@@ -51,10 +74,9 @@ export class ConfigManager {
|
|
|
51
74
|
return c;
|
|
52
75
|
}
|
|
53
76
|
static async saveOSConfig(config) {
|
|
54
|
-
// Use direct filesystem operations for OS config persistence
|
|
55
77
|
const osPath = path.join(ConfigManager.instancesPath, config.name);
|
|
56
|
-
await
|
|
57
|
-
await
|
|
78
|
+
await mkdir(osPath, { recursive: true });
|
|
79
|
+
await writeJson(path.join(osPath, CONFIG_FILE_NAME), config, {
|
|
58
80
|
spaces: 2,
|
|
59
81
|
});
|
|
60
82
|
}
|
|
@@ -62,7 +84,7 @@ export class ConfigManager {
|
|
|
62
84
|
try {
|
|
63
85
|
await this.initialize();
|
|
64
86
|
const osInstances = [];
|
|
65
|
-
const osInstanceNames = await
|
|
87
|
+
const osInstanceNames = await readdir(ConfigManager.instancesPath);
|
|
66
88
|
for (const osInstanceName of osInstanceNames) {
|
|
67
89
|
const config = await this.getOSConfig(osInstanceName);
|
|
68
90
|
if (config) {
|
|
@@ -72,7 +94,6 @@ export class ConfigManager {
|
|
|
72
94
|
return osInstances;
|
|
73
95
|
}
|
|
74
96
|
catch (error) {
|
|
75
|
-
// if the default path for config does not exist, return an empty array
|
|
76
97
|
if (error instanceof Error && error.message.includes('ENOENT')) {
|
|
77
98
|
return [];
|
|
78
99
|
}
|
|
@@ -81,8 +102,8 @@ export class ConfigManager {
|
|
|
81
102
|
}
|
|
82
103
|
static async deleteOSInstance(name) {
|
|
83
104
|
const osInstancePath = path.join(ConfigManager.instancesPath, name);
|
|
84
|
-
if (await
|
|
85
|
-
await
|
|
105
|
+
if (await pathExists(osInstancePath)) {
|
|
106
|
+
await rm(osInstancePath, { recursive: true });
|
|
86
107
|
}
|
|
87
108
|
}
|
|
88
109
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { oRequest } from '@olane/o-core';
|
|
2
|
+
import { oLaneTool } from '@olane/o-lane';
|
|
3
|
+
import { oNodeConfig } from '@olane/o-node';
|
|
4
|
+
import type { ToolResult } from '@olane/o-tool';
|
|
5
|
+
/**
|
|
6
|
+
* Vector store implementation backed by the Copass backend via the CLI.
|
|
7
|
+
*
|
|
8
|
+
* Sits at o://vector-store so that `index_network` and any tool that
|
|
9
|
+
* calls `use(oAddress('o://vector-store'), ...)` routes through here.
|
|
10
|
+
*
|
|
11
|
+
* - add_documents → `olane ingest text` for each document (parallelized)
|
|
12
|
+
* - search_similar → `olane copass context` (fast path search)
|
|
13
|
+
* - delete/update → no-ops (Copass is append-only)
|
|
14
|
+
*/
|
|
15
|
+
export declare class CopassVectorStoreTool extends oLaneTool {
|
|
16
|
+
constructor(config: oNodeConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Ingest documents into the Copass backend via `olane ingest text`.
|
|
19
|
+
* Runs up to INGEST_CONCURRENCY CLI calls in parallel.
|
|
20
|
+
*/
|
|
21
|
+
_tool_add_documents(request: oRequest): Promise<ToolResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Search via `olane copass context` (fast path).
|
|
24
|
+
*/
|
|
25
|
+
_tool_search_similar(request: oRequest): Promise<ToolResult>;
|
|
26
|
+
_tool_delete_documents(_request: oRequest): Promise<ToolResult>;
|
|
27
|
+
_tool_update_documents(_request: oRequest): Promise<ToolResult>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=copass-vector-store.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copass-vector-store.tool.d.ts","sourceRoot":"","sources":["../../../src/vector-store/copass-vector-store.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,MAAM,EAAE,WAAW;IA2C/B;;;OAGG;IACG,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAsCjE;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAsB5D,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAI/D,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CAGtE"}
|