@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.
Files changed (51) hide show
  1. package/dist/src/address/address-factory.d.ts +23 -0
  2. package/dist/src/address/address-factory.d.ts.map +1 -0
  3. package/dist/src/address/address-factory.js +42 -0
  4. package/dist/src/address-book/address-book.d.ts +32 -0
  5. package/dist/src/address-book/address-book.d.ts.map +1 -0
  6. package/dist/src/address-book/address-book.js +65 -0
  7. package/dist/src/auth/os-auth-guard.d.ts +20 -0
  8. package/dist/src/auth/os-auth-guard.d.ts.map +1 -0
  9. package/dist/src/auth/os-auth-guard.js +39 -0
  10. package/dist/src/identity/copass-id.d.ts +23 -0
  11. package/dist/src/identity/copass-id.d.ts.map +1 -0
  12. package/dist/src/identity/copass-id.js +37 -0
  13. package/dist/src/index.d.ts +12 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +12 -0
  16. package/dist/src/lifecycle/os-lifecycle.d.ts +36 -0
  17. package/dist/src/lifecycle/os-lifecycle.d.ts.map +1 -0
  18. package/dist/src/lifecycle/os-lifecycle.js +99 -0
  19. package/dist/src/mcp/os-mcp-server.d.ts +29 -0
  20. package/dist/src/mcp/os-mcp-server.d.ts.map +1 -0
  21. package/dist/src/mcp/os-mcp-server.js +244 -0
  22. package/dist/src/memory/memory-harness.d.ts +38 -0
  23. package/dist/src/memory/memory-harness.d.ts.map +1 -0
  24. package/dist/src/memory/memory-harness.js +100 -0
  25. package/dist/src/nodes/relay-node.d.ts +31 -0
  26. package/dist/src/nodes/relay-node.d.ts.map +1 -0
  27. package/dist/src/nodes/relay-node.js +85 -0
  28. package/dist/src/o-olane-os/interfaces/o-os.config.d.ts +15 -1
  29. package/dist/src/o-olane-os/interfaces/o-os.config.d.ts.map +1 -1
  30. package/dist/src/o-olane-os/o-os.d.ts +22 -0
  31. package/dist/src/o-olane-os/o-os.d.ts.map +1 -1
  32. package/dist/src/o-olane-os/o-os.js +109 -9
  33. package/dist/src/tools/filesystem.tool.d.ts +24 -0
  34. package/dist/src/tools/filesystem.tool.d.ts.map +1 -0
  35. package/dist/src/tools/filesystem.tool.js +163 -0
  36. package/dist/src/utils/config.d.ts +7 -1
  37. package/dist/src/utils/config.d.ts.map +1 -1
  38. package/dist/src/utils/config.js +40 -19
  39. package/dist/src/vector-store/copass-vector-store.tool.d.ts +29 -0
  40. package/dist/src/vector-store/copass-vector-store.tool.d.ts.map +1 -0
  41. package/dist/src/vector-store/copass-vector-store.tool.js +152 -0
  42. package/dist/src/worlds/world-instance.tool.d.ts +30 -0
  43. package/dist/src/worlds/world-instance.tool.d.ts.map +1 -0
  44. package/dist/src/worlds/world-instance.tool.js +179 -0
  45. package/dist/src/worlds/world-manager.tool.d.ts +61 -0
  46. package/dist/src/worlds/world-manager.tool.d.ts.map +1 -0
  47. package/dist/src/worlds/world-manager.tool.js +270 -0
  48. package/dist/src/worlds/world.types.d.ts +23 -0
  49. package/dist/src/worlds/world.types.d.ts.map +1 -0
  50. package/dist/src/worlds/world.types.js +1 -0
  51. package/package.json +15 -14
@@ -0,0 +1,270 @@
1
+ import { oLaneTool } from '@olane/o-lane';
2
+ import { oAddress, DEFAULT_INSTANCE_PATH } from '@olane/o-core';
3
+ import { WorldInstanceTool } from './world-instance.tool.js';
4
+ import * as fs from 'fs-extra';
5
+ import * as path from 'path';
6
+ import { watch } from 'node:fs';
7
+ /**
8
+ * WorldManagerTool — dedicated oLaneTool node at o://world-manager.
9
+ * Child of the root leader. Manages the lifecycle of all world instance nodes.
10
+ *
11
+ * On startup: reads persisted world configs and starts a WorldInstanceTool child for each.
12
+ */
13
+ export class WorldManagerTool extends oLaneTool {
14
+ constructor(config) {
15
+ super({
16
+ ...config,
17
+ address: new oAddress('o://world-manager'),
18
+ description: 'Manages world instances (branded networks)',
19
+ methods: {
20
+ create_world: {
21
+ name: 'create_world',
22
+ description: 'Create a new world (branded network)',
23
+ parameters: [
24
+ { name: 'name', type: 'string', required: true, description: 'World name' },
25
+ { name: 'description', type: 'string', required: false, description: 'World description' },
26
+ { name: 'icon', type: 'string', required: false, description: 'World icon' },
27
+ { name: 'supportedTypes', type: 'array', required: false, description: 'Supported address types' },
28
+ { name: 'createdBy', type: 'string', required: false, description: 'Copass ID of creator' },
29
+ ],
30
+ dependencies: [],
31
+ },
32
+ remove_world: {
33
+ name: 'remove_world',
34
+ description: 'Remove a world',
35
+ parameters: [
36
+ { name: 'id', type: 'string', required: true, description: 'World ID' },
37
+ ],
38
+ dependencies: [],
39
+ },
40
+ list_worlds: {
41
+ name: 'list_worlds',
42
+ description: 'List all worlds',
43
+ parameters: [],
44
+ dependencies: [],
45
+ },
46
+ join_world: {
47
+ name: 'join_world',
48
+ description: 'Join a world by Copass ID',
49
+ parameters: [
50
+ { name: 'id', type: 'string', required: true, description: 'World ID' },
51
+ { name: 'copassId', type: 'string', required: true, description: 'Copass ID' },
52
+ ],
53
+ dependencies: [],
54
+ },
55
+ },
56
+ });
57
+ this.worlds = new Map();
58
+ this.fsWatcher = null;
59
+ this.syncDebounce = null;
60
+ this.storagePath =
61
+ config.storagePath ??
62
+ path.join(DEFAULT_INSTANCE_PATH, '..', 'storage', config.systemName || 'default', 'worlds');
63
+ }
64
+ async start() {
65
+ await super.start();
66
+ await fs.ensureDir(this.storagePath);
67
+ await this.restoreWorlds();
68
+ this.startWatching();
69
+ }
70
+ async stop() {
71
+ this.stopWatching();
72
+ await super.stop();
73
+ }
74
+ /**
75
+ * Watch the worlds directory for file changes.
76
+ * When the CLI writes a new address to a world JSON,
77
+ * the running instance picks it up and syncs to memory.
78
+ */
79
+ startWatching() {
80
+ try {
81
+ this.fsWatcher = watch(this.storagePath, (event, filename) => {
82
+ if (!filename?.endsWith('.json'))
83
+ return;
84
+ // Debounce — multiple events fire for a single write
85
+ if (this.syncDebounce)
86
+ clearTimeout(this.syncDebounce);
87
+ this.syncDebounce = setTimeout(() => {
88
+ this.syncFromDisk(filename).catch((err) => this.logger.warn(`Failed to sync world file ${filename}:`, err));
89
+ }, 500);
90
+ });
91
+ this.fsWatcher.unref(); // Don't prevent process exit
92
+ this.logger.debug('Watching worlds directory for changes');
93
+ }
94
+ catch (err) {
95
+ this.logger.warn('Could not watch worlds directory:', err);
96
+ }
97
+ }
98
+ stopWatching() {
99
+ if (this.syncDebounce)
100
+ clearTimeout(this.syncDebounce);
101
+ if (this.fsWatcher) {
102
+ this.fsWatcher.close();
103
+ this.fsWatcher = null;
104
+ }
105
+ }
106
+ /**
107
+ * Sync a world from its JSON file on disk into the running instance.
108
+ * Handles both new worlds and updated addresses on existing worlds.
109
+ */
110
+ async syncFromDisk(filename) {
111
+ const filePath = path.join(this.storagePath, filename);
112
+ if (!(await fs.pathExists(filePath)))
113
+ return;
114
+ try {
115
+ const data = await fs.readJson(filePath);
116
+ const worldConfig = data.config;
117
+ if (!worldConfig?.id)
118
+ return;
119
+ const existing = this.worlds.get(worldConfig.id);
120
+ if (existing) {
121
+ // Sync addresses: add any new ones from disk into the running instance
122
+ const diskAddresses = data.addresses || [];
123
+ const currentAddresses = existing.getAddresses();
124
+ for (const addr of diskAddresses) {
125
+ const alreadyLoaded = currentAddresses.some((a) => a.address === addr.address);
126
+ if (!alreadyLoaded) {
127
+ await existing._tool_register_address({
128
+ params: { address: addr.address, type: addr.type, metadata: addr.metadata },
129
+ });
130
+ this.logger.debug(`Synced new address '${addr.address}' to world '${worldConfig.name}'`);
131
+ }
132
+ }
133
+ }
134
+ else {
135
+ // New world — start it
136
+ const instance = await this.startWorldInstance(worldConfig);
137
+ this.worlds.set(worldConfig.id, instance);
138
+ this.logger.debug(`Synced new world from disk: ${worldConfig.name}`);
139
+ }
140
+ }
141
+ catch (err) {
142
+ this.logger.warn(`Failed to parse world file ${filename}:`, err);
143
+ }
144
+ }
145
+ // ── Tool methods ───────────────────────────────────────────
146
+ async _tool_create_world(request) {
147
+ const { name, description, icon, supportedTypes, createdBy } = request.params;
148
+ const id = `world-${name.toLowerCase().replace(/[^a-z0-9-]/g, '-')}`;
149
+ if (this.worlds.has(id)) {
150
+ return { success: false, error: `World '${name}' already exists` };
151
+ }
152
+ const worldConfig = {
153
+ id,
154
+ name,
155
+ description,
156
+ icon,
157
+ supportedTypes: supportedTypes || ['filepath'],
158
+ members: createdBy ? [createdBy] : [],
159
+ createdAt: new Date().toISOString(),
160
+ createdBy,
161
+ };
162
+ const instance = await this.startWorldInstance(worldConfig);
163
+ this.worlds.set(id, instance);
164
+ await this.persistWorldConfig(worldConfig);
165
+ return { success: true, world: worldConfig };
166
+ }
167
+ async _tool_remove_world(request) {
168
+ const { id } = request.params;
169
+ const instance = this.worlds.get(id);
170
+ if (!instance) {
171
+ return { success: false, error: `World '${id}' not found` };
172
+ }
173
+ await instance.stop();
174
+ this.worlds.delete(id);
175
+ const worldFile = path.join(this.storagePath, `${id}.json`);
176
+ if (await fs.pathExists(worldFile)) {
177
+ await fs.remove(worldFile);
178
+ }
179
+ return { success: true };
180
+ }
181
+ async _tool_list_worlds(_request) {
182
+ const worlds = Array.from(this.worlds.values()).map((w) => w.world);
183
+ return { success: true, worlds };
184
+ }
185
+ async _tool_join_world(request) {
186
+ const { id, copassId } = request.params;
187
+ const instance = this.worlds.get(id);
188
+ if (!instance) {
189
+ return { success: false, error: `World '${id}' not found` };
190
+ }
191
+ if (!instance.world.members.includes(copassId)) {
192
+ instance.world.members.push(copassId);
193
+ await this.persistWorldConfig(instance.world);
194
+ }
195
+ return { success: true };
196
+ }
197
+ // ── Public accessors ───────────────────────────────────────
198
+ listWorlds() {
199
+ return Array.from(this.worlds.values()).map((w) => w.world);
200
+ }
201
+ getWorldInstance(id) {
202
+ return this.worlds.get(id);
203
+ }
204
+ /**
205
+ * Create a world (public convenience, delegates to _tool_create_world).
206
+ */
207
+ async createWorld(params) {
208
+ return this._tool_create_world({ params });
209
+ }
210
+ /**
211
+ * Join a world (public convenience, delegates to _tool_join_world).
212
+ */
213
+ async joinWorld(id, copassId) {
214
+ return this._tool_join_world({ params: { id, copassId } });
215
+ }
216
+ // ── Internal ───────────────────────────────────────────────
217
+ async startWorldInstance(worldConfig) {
218
+ const slug = worldConfig.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
219
+ const worldAddress = new oAddress(`o://world-manager/${slug}`);
220
+ const persistFile = path.join(this.storagePath, `${worldConfig.id}.json`);
221
+ const instance = new WorldInstanceTool({
222
+ address: worldAddress,
223
+ leader: this.address,
224
+ parent: this.address,
225
+ _allowNestedAddress: true,
226
+ }, worldConfig, persistFile);
227
+ this.addChildNode(instance);
228
+ await instance.start();
229
+ return instance;
230
+ }
231
+ async restoreWorlds() {
232
+ try {
233
+ const files = await fs.readdir(this.storagePath);
234
+ for (const file of files) {
235
+ if (!file.endsWith('.json'))
236
+ continue;
237
+ try {
238
+ const data = await fs.readJson(path.join(this.storagePath, file));
239
+ const worldConfig = data.config || data;
240
+ if (worldConfig.id && worldConfig.name) {
241
+ const instance = await this.startWorldInstance(worldConfig);
242
+ this.worlds.set(worldConfig.id, instance);
243
+ this.logger.debug(`Restored world: ${worldConfig.name}`);
244
+ }
245
+ }
246
+ catch (err) {
247
+ this.logger.error(`Failed to restore world from ${file}:`, err);
248
+ }
249
+ }
250
+ }
251
+ catch {
252
+ // No worlds to restore
253
+ }
254
+ }
255
+ async persistWorldConfig(config) {
256
+ await fs.ensureDir(this.storagePath);
257
+ const existing = path.join(this.storagePath, `${config.id}.json`);
258
+ let addresses = [];
259
+ try {
260
+ if (await fs.pathExists(existing)) {
261
+ const data = await fs.readJson(existing);
262
+ addresses = data.addresses || [];
263
+ }
264
+ }
265
+ catch {
266
+ // ignore
267
+ }
268
+ await fs.writeJson(existing, { config, addresses }, { spaces: 2 });
269
+ }
270
+ }
@@ -0,0 +1,23 @@
1
+ export type WorldSupportedType = 'filepath';
2
+ export interface WorldConfig {
3
+ id: string;
4
+ name: string;
5
+ description?: string;
6
+ icon?: string;
7
+ networkId?: string;
8
+ supportedTypes: WorldSupportedType[];
9
+ members: string[];
10
+ createdAt: string;
11
+ createdBy?: string;
12
+ }
13
+ export interface WorldAddressEntry {
14
+ address: string;
15
+ type: WorldSupportedType;
16
+ registeredAt: string;
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface WorldData {
20
+ config: WorldConfig;
21
+ addresses: WorldAddressEntry[];
22
+ }
23
+ //# sourceMappingURL=world.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.types.d.ts","sourceRoot":"","sources":["../../../src/worlds/world.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,kBAAkB,EAAE,CAAC;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,kBAAkB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/os",
3
- "version": "0.8.16",
3
+ "version": "0.8.17",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -58,23 +58,24 @@
58
58
  "typescript": "5.9.3"
59
59
  },
60
60
  "dependencies": {
61
- "@olane/o-client-limited": "0.8.16",
62
- "@olane/o-config": "0.8.16",
63
- "@olane/o-core": "0.8.16",
64
- "@olane/o-intelligence": "0.8.16",
65
- "@olane/o-lane": "0.8.16",
66
- "@olane/o-leader": "0.8.16",
67
- "@olane/o-login": "0.8.16",
68
- "@olane/o-protocol": "0.8.16",
69
- "@olane/o-storage": "0.8.16",
70
- "@olane/o-tool": "0.8.16",
71
- "@olane/o-tool-registry": "0.8.16",
72
- "@olane/o-tools-common": "0.8.16",
61
+ "@libp2p/circuit-relay-v2": "^4.1.3",
62
+ "@olane/o-client-limited": "0.8.17",
63
+ "@olane/o-config": "0.8.17",
64
+ "@olane/o-core": "0.8.17",
65
+ "@olane/o-intelligence": "0.8.17",
66
+ "@olane/o-lane": "0.8.17",
67
+ "@olane/o-leader": "0.8.17",
68
+ "@olane/o-login": "0.8.17",
69
+ "@olane/o-protocol": "0.8.17",
70
+ "@olane/o-storage": "0.8.17",
71
+ "@olane/o-tool": "0.8.17",
72
+ "@olane/o-tool-registry": "0.8.17",
73
+ "@olane/o-tools-common": "0.8.17",
73
74
  "chalk": "^5.6.2",
74
75
  "debug": "^4.4.3",
75
76
  "dotenv": "^17.3.1",
76
77
  "fs-extra": "^11.3.3",
77
78
  "touch": "^3.1.1"
78
79
  },
79
- "gitHead": "8e724c4efc57e8d771394815b036322085deff33"
80
+ "gitHead": "848be12a374d32f799026feecf15ada6bcf753ff"
80
81
  }