@olane/os 0.8.16 → 0.8.18
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 +24 -0
- package/dist/src/o-olane-os/o-os.d.ts.map +1 -1
- package/dist/src/o-olane-os/o-os.js +121 -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 +214 -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 +17 -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,11 @@ 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';
|
|
19
|
+
import { FilesystemTool } from '../tools/filesystem.tool.js';
|
|
13
20
|
export class OlaneOS extends oObject {
|
|
14
21
|
constructor(config) {
|
|
15
22
|
super();
|
|
@@ -17,6 +24,10 @@ export class OlaneOS extends oObject {
|
|
|
17
24
|
this.nodes = []; // clones of node for scale
|
|
18
25
|
this.rootLeader = null; // the root leader node
|
|
19
26
|
this.roundRobinIndex = 0;
|
|
27
|
+
this.worldManager = null;
|
|
28
|
+
this.addressBook = null;
|
|
29
|
+
this.memory = null;
|
|
30
|
+
this.filesystemTool = null;
|
|
20
31
|
this.config = config;
|
|
21
32
|
}
|
|
22
33
|
entryNode() {
|
|
@@ -50,14 +61,17 @@ export class OlaneOS extends oObject {
|
|
|
50
61
|
// Then we'll migrate to using o://os-config for persistence
|
|
51
62
|
if (this.config.configFilePath) {
|
|
52
63
|
try {
|
|
64
|
+
// Ensure parent directory exists before touch
|
|
65
|
+
await mkdir(path.dirname(this.config.configFilePath), { recursive: true });
|
|
53
66
|
await touch(this.config.configFilePath);
|
|
54
67
|
this.logger.debug('Config file path: ' + this.config.configFilePath);
|
|
55
68
|
// let's load the config
|
|
56
69
|
const config = await readFile(this.config.configFilePath, 'utf8');
|
|
57
70
|
this.logger.debug('Config file contents: ' + config);
|
|
58
|
-
if (config
|
|
59
|
-
//
|
|
60
|
-
|
|
71
|
+
if (!config || config.trim().length === 0) {
|
|
72
|
+
// Empty file (fresh OS) — use the in-memory config as-is
|
|
73
|
+
this.logger.debug('Empty config file, using constructor config');
|
|
74
|
+
return;
|
|
61
75
|
}
|
|
62
76
|
const json = JSON.parse(config);
|
|
63
77
|
this.logger.debug('Config file parsed: ' + json);
|
|
@@ -246,6 +260,104 @@ export class OlaneOS extends oObject {
|
|
|
246
260
|
// Don't throw - allow OS to continue even if replay fails
|
|
247
261
|
}
|
|
248
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Initialize world manager, address book, and memory harness
|
|
265
|
+
* after the leader and nodes are started.
|
|
266
|
+
*/
|
|
267
|
+
async initOSServices() {
|
|
268
|
+
const instanceName = (await this.getOSInstanceName()) ||
|
|
269
|
+
this.config.network?.name ||
|
|
270
|
+
'default';
|
|
271
|
+
// Address book
|
|
272
|
+
this.addressBook = new AddressBook(instanceName);
|
|
273
|
+
await this.addressBook.load();
|
|
274
|
+
// OS service nodes (children of root leader)
|
|
275
|
+
if (this.rootLeader) {
|
|
276
|
+
// Memory harness — shells out to `olane ingest/copass` CLI
|
|
277
|
+
this.memory = new MemoryHarness();
|
|
278
|
+
this.logger.debug('Memory harness initialized');
|
|
279
|
+
// Vector store — routes to Copass backend so index_network
|
|
280
|
+
// and any o://vector-store calls go through the memory harness
|
|
281
|
+
const vectorStore = new CopassVectorStoreTool({
|
|
282
|
+
address: new oAddress('o://vector-store'),
|
|
283
|
+
leader: this.rootLeader.address,
|
|
284
|
+
parent: this.rootLeader.address,
|
|
285
|
+
_allowNestedAddress: true,
|
|
286
|
+
});
|
|
287
|
+
this.rootLeader.addChildNode(vectorStore);
|
|
288
|
+
await vectorStore.start();
|
|
289
|
+
this.logger.debug('Copass vector store started');
|
|
290
|
+
// Filesystem tool — single instance for the entire OS
|
|
291
|
+
this.filesystemTool = new FilesystemTool({
|
|
292
|
+
address: new oAddress('o://fs'),
|
|
293
|
+
leader: this.rootLeader.address,
|
|
294
|
+
parent: this.rootLeader.address,
|
|
295
|
+
_allowNestedAddress: true,
|
|
296
|
+
});
|
|
297
|
+
this.rootLeader.addChildNode(this.filesystemTool);
|
|
298
|
+
await this.filesystemTool.start();
|
|
299
|
+
this.logger.debug('Filesystem tool started');
|
|
300
|
+
// World manager
|
|
301
|
+
this.worldManager = new WorldManagerTool({
|
|
302
|
+
leader: this.rootLeader.address,
|
|
303
|
+
parent: this.rootLeader.address,
|
|
304
|
+
systemName: instanceName,
|
|
305
|
+
_allowNestedAddress: true,
|
|
306
|
+
});
|
|
307
|
+
this.rootLeader.addChildNode(this.worldManager);
|
|
308
|
+
await this.worldManager.start();
|
|
309
|
+
this.logger.debug('World manager started');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Only index if `indexCompletedAt` is not set in the instance config.
|
|
314
|
+
* Records the timestamp after successful indexing.
|
|
315
|
+
*/
|
|
316
|
+
async indexOnceInBackground() {
|
|
317
|
+
const osName = await this.getOSInstanceName();
|
|
318
|
+
if (osName) {
|
|
319
|
+
const existing = await ConfigManager.getOSConfig(osName);
|
|
320
|
+
if (existing?.indexCompletedAt) {
|
|
321
|
+
this.logger.debug('Network already indexed, skipping');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
await this.indexNetworkParallel();
|
|
326
|
+
// Record completion
|
|
327
|
+
if (osName) {
|
|
328
|
+
const config = await ConfigManager.getOSConfig(osName);
|
|
329
|
+
if (config) {
|
|
330
|
+
await ConfigManager.saveOSConfig({
|
|
331
|
+
...config,
|
|
332
|
+
indexCompletedAt: new Date().toISOString(),
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Index all nodes in the network in parallel.
|
|
339
|
+
* Each node indexes itself + its children concurrently,
|
|
340
|
+
* rather than the default sequential tree walk.
|
|
341
|
+
*/
|
|
342
|
+
async indexNetworkParallel() {
|
|
343
|
+
const allNodes = [
|
|
344
|
+
...this.leaders,
|
|
345
|
+
...this.nodes,
|
|
346
|
+
];
|
|
347
|
+
this.logger.debug(`Indexing ${allNodes.length} node(s) in parallel...`);
|
|
348
|
+
await Promise.allSettled(allNodes.map(async (node) => {
|
|
349
|
+
try {
|
|
350
|
+
await node.use(node.address, {
|
|
351
|
+
method: 'index_network',
|
|
352
|
+
params: {},
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
this.logger.warn(`Failed to index ${node.address.toString()}: ${err}`);
|
|
357
|
+
}
|
|
358
|
+
}));
|
|
359
|
+
this.logger.debug('Parallel network indexing complete');
|
|
360
|
+
}
|
|
249
361
|
async use(oAddress, params) {
|
|
250
362
|
const entryNode = this.entryNode();
|
|
251
363
|
if (!entryNode) {
|
|
@@ -265,6 +377,9 @@ export class OlaneOS extends oObject {
|
|
|
265
377
|
this.logger.debug('Leaders started...');
|
|
266
378
|
await this.startNodes(NodeType.NODE);
|
|
267
379
|
this.logger.debug('Nodes started...');
|
|
380
|
+
// Initialize world manager, address book, and memory harness
|
|
381
|
+
await this.initOSServices();
|
|
382
|
+
this.logger.debug('OS services initialized...');
|
|
268
383
|
// Load config from o://os-config storage backend (after tools are initialized)
|
|
269
384
|
// This merges any saved lanes from persistent storage
|
|
270
385
|
await this.loadConfigFromStorage();
|
|
@@ -273,14 +388,11 @@ export class OlaneOS extends oObject {
|
|
|
273
388
|
await this.runSavedPlans();
|
|
274
389
|
this.logger.debug('Saved plans run...');
|
|
275
390
|
this.logger.debug('OS instance started...');
|
|
276
|
-
|
|
391
|
+
this.status = OlaneOSSystemStatus.RUNNING;
|
|
392
|
+
// Index the network once (first boot only), in the background.
|
|
277
393
|
if (!this.config.noIndexNetwork) {
|
|
278
|
-
|
|
279
|
-
method: 'index_network',
|
|
280
|
-
params: {},
|
|
281
|
-
});
|
|
394
|
+
this.indexOnceInBackground().catch((err) => this.logger.error('Background network indexing failed:', err));
|
|
282
395
|
}
|
|
283
|
-
this.status = OlaneOSSystemStatus.RUNNING;
|
|
284
396
|
return {
|
|
285
397
|
peerId: this.rootLeader?.peerId.toString() || '',
|
|
286
398
|
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
|
+
* A single instance exists per OS at o://fs, shared across all worlds.
|
|
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
|
+
* A single instance exists per OS at o://fs, shared across all worlds.
|
|
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"}
|