@treenity/core 3.0.0 → 3.0.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/README.md +78 -0
- package/dist/chain.d.ts +3 -4
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js.map +1 -1
- package/dist/client/trpc.d.ts.map +1 -1
- package/dist/client/trpc.js +1 -0
- package/dist/client/trpc.js.map +1 -1
- package/dist/comp/index.d.ts +3 -4
- package/dist/comp/index.d.ts.map +1 -1
- package/dist/comp/index.js +5 -4
- package/dist/comp/index.js.map +1 -1
- package/dist/comp/needs.d.ts.map +1 -1
- package/dist/comp/needs.js +3 -3
- package/dist/comp/needs.js.map +1 -1
- package/dist/core/component.d.ts +10 -8
- package/dist/core/component.d.ts.map +1 -1
- package/dist/core/component.js +4 -8
- package/dist/core/component.js.map +1 -1
- package/dist/core/context.d.ts +2 -2
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +7 -3
- package/dist/core/path.js.map +1 -1
- package/dist/core/registry.d.ts +2 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +30 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +119 -0
- package/dist/log.js.map +1 -1
- package/dist/mod/examples/ticker/service.js +2 -3
- package/dist/mod/examples/ticker/service.js.map +1 -1
- package/dist/mod/index.d.ts +1 -1
- package/dist/mod/index.d.ts.map +1 -1
- package/dist/mod/index.js +1 -1
- package/dist/mod/index.js.map +1 -1
- package/dist/mod/loader.d.ts +1 -0
- package/dist/mod/loader.d.ts.map +1 -1
- package/dist/mod/loader.js +27 -1
- package/dist/mod/loader.js.map +1 -1
- package/dist/mods/clients.d.ts +2 -0
- package/dist/mods/clients.d.ts.map +1 -0
- package/dist/mods/clients.js +3 -0
- package/dist/mods/clients.js.map +1 -0
- package/dist/mods/llm/index.js +1 -1
- package/dist/mods/llm/index.js.map +1 -1
- package/dist/mods/mcp/server.d.ts +0 -1
- package/dist/mods/mcp/server.js +0 -1
- package/dist/mods/mcp/service.d.ts +0 -1
- package/dist/mods/mcp/service.js +0 -1
- package/dist/mods/mcp/types.d.ts +0 -1
- package/dist/mods/mcp/types.js +0 -1
- package/dist/mods/servers.d.ts +4 -0
- package/dist/mods/servers.d.ts.map +1 -0
- package/dist/mods/servers.js +5 -0
- package/dist/mods/servers.js.map +1 -0
- package/dist/mods/treenity/builtins.d.ts +2 -0
- package/dist/mods/treenity/builtins.d.ts.map +1 -0
- package/dist/mods/treenity/builtins.js +18 -0
- package/dist/mods/treenity/builtins.js.map +1 -0
- package/dist/mods/treenity/logs.d.ts +18 -0
- package/dist/mods/treenity/logs.d.ts.map +1 -0
- package/dist/mods/treenity/logs.js +17 -0
- package/dist/mods/treenity/logs.js.map +1 -0
- package/dist/mods/treenity/seed.js +29 -27
- package/dist/mods/treenity/seed.js.map +1 -1
- package/dist/mods/treenity/server.d.ts +2 -0
- package/dist/mods/treenity/server.d.ts.map +1 -1
- package/dist/mods/treenity/server.js +2 -0
- package/dist/mods/treenity/server.js.map +1 -1
- package/dist/mods/uix/client.js +4 -4
- package/dist/mods/uix/client.js.map +1 -1
- package/dist/mods/uix/compile.d.ts.map +1 -1
- package/dist/mods/uix/compile.js +4 -2
- package/dist/mods/uix/compile.js.map +1 -1
- package/dist/schema/_test-fixture.d.ts +11 -0
- package/dist/schema/_test-fixture.d.ts.map +1 -0
- package/dist/schema/_test-fixture.js +8 -0
- package/dist/schema/_test-fixture.js.map +1 -0
- package/dist/schema/types.d.ts +1 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/server/actions.js +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +4 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/client.d.ts +10 -3
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/client.js +1 -1
- package/dist/server/client.js.map +1 -1
- package/dist/server/doc-index.d.ts.map +1 -1
- package/dist/server/doc-index.js +13 -12
- package/dist/server/doc-index.js.map +1 -1
- package/dist/server/factory.d.ts +6 -0
- package/dist/server/factory.d.ts.map +1 -1
- package/dist/server/factory.js +44 -25
- package/dist/server/factory.js.map +1 -1
- package/dist/server/main.d.ts +0 -4
- package/dist/server/main.d.ts.map +1 -1
- package/dist/server/main.js +4 -30
- package/dist/server/main.js.map +1 -1
- package/dist/server/mcp.d.ts +0 -1
- package/dist/server/mcp.js +0 -1
- package/dist/server/migrate.js +3 -3
- package/dist/server/migrate.js.map +1 -1
- package/dist/server/mods-mount.d.ts +0 -1
- package/dist/server/mods-mount.d.ts.map +1 -1
- package/dist/server/mods-mount.js +0 -1
- package/dist/server/mods-mount.js.map +1 -1
- package/dist/server/mount-adapters.js +9 -4
- package/dist/server/mount-adapters.js.map +1 -1
- package/dist/server/prefab.d.ts +2 -2
- package/dist/server/prefab.d.ts.map +1 -1
- package/dist/server/prefab.js +4 -2
- package/dist/server/prefab.js.map +1 -1
- package/dist/server/refs.d.ts +3 -0
- package/dist/server/refs.d.ts.map +1 -0
- package/dist/server/refs.js +59 -0
- package/dist/server/refs.js.map +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +13 -3
- package/dist/server/server.js.map +1 -1
- package/dist/server/sub.js.map +1 -1
- package/dist/server/trpc.d.ts +7 -0
- package/dist/server/trpc.d.ts.map +1 -1
- package/dist/server/trpc.js +6 -2
- package/dist/server/trpc.js.map +1 -1
- package/dist/tree/fs.d.ts.map +1 -1
- package/dist/tree/fs.js +20 -18
- package/dist/tree/fs.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +2 -1
- package/dist/tree/index.js.map +1 -1
- package/dist/tree-chain.d.ts +2 -1
- package/dist/tree-chain.d.ts.map +1 -1
- package/dist/tree-chain.js +5 -3
- package/dist/tree-chain.js.map +1 -1
- package/dist/uri.d.ts.map +1 -1
- package/dist/uri.js +8 -3
- package/dist/uri.js.map +1 -1
- package/package.json +48 -6
- package/src/chain.ts +7 -5
- package/src/client/trpc.ts +1 -0
- package/src/comp/index.test.ts +45 -9
- package/src/comp/index.ts +19 -8
- package/src/comp/needs.ts +3 -3
- package/src/core/component.ts +16 -14
- package/src/core/context.ts +4 -4
- package/src/core/index.test.ts +22 -1
- package/src/core/path.ts +6 -3
- package/src/core/registry.ts +4 -7
- package/src/core.ts +1 -1
- package/src/index.ts +1 -0
- package/src/log.ts +172 -1
- package/src/mod/docs/07-realtime.md +19 -11
- package/src/mod/docs/08-services.md +10 -8
- package/src/mod/docs/10-acl.md +1 -1
- package/src/mod/docs/12-conventions.md +43 -0
- package/src/mod/docs/13-example.md +36 -26
- package/src/mod/docs/14-mod-format.md +81 -8
- package/src/mod/examples/ticker/service.ts +2 -3
- package/src/mod/index.ts +1 -1
- package/src/mod/loader.test.ts +53 -1
- package/src/mod/loader.ts +34 -1
- package/src/mods/clients.ts +2 -0
- package/src/mods/llm/index.ts +1 -1
- package/src/mods/servers.ts +4 -0
- package/src/mods/treenity/builtins.ts +21 -0
- package/src/mods/treenity/logs.ts +26 -0
- package/src/mods/treenity/seed.ts +30 -28
- package/src/mods/treenity/server.ts +2 -0
- package/src/mods/uix/client.ts +4 -4
- package/src/mods/uix/compile.ts +4 -2
- package/src/schema/_test-fixture.ts +12 -0
- package/src/schema/generated/ai.agent.json +133 -0
- package/src/schema/generated/ai.approval.json +105 -0
- package/src/schema/generated/ai.approvals.json +24 -0
- package/src/schema/generated/ai.assignment.json +28 -0
- package/src/schema/generated/ai.plan.json +84 -0
- package/src/schema/generated/ai.policy.json +105 -0
- package/src/schema/generated/ai.pool.json +37 -0
- package/src/schema/generated/ai.thread.json +64 -0
- package/src/schema/generated/board.kanban.json +7 -0
- package/src/schema/generated/canary.item.json +40 -0
- package/src/schema/generated/claude-search.json +20 -0
- package/src/schema/generated/craft.product.json +47 -0
- package/src/schema/generated/craft.shop.json +94 -0
- package/src/schema/generated/craft.subscription.json +27 -0
- package/src/schema/generated/examples.demo.sensor.reading.json +25 -0
- package/src/schema/generated/flow.node.action.json +61 -0
- package/src/schema/generated/flow.node.code.json +43 -0
- package/src/schema/generated/flow.node.condition.json +37 -0
- package/src/schema/generated/flow.node.end.json +35 -0
- package/src/schema/generated/flow.node.http.json +65 -0
- package/src/schema/generated/flow.node.llm.json +67 -0
- package/src/schema/generated/flow.node.loop.json +49 -0
- package/src/schema/generated/flow.node.start.json +39 -0
- package/src/schema/generated/flow.scenario.json +118 -0
- package/src/schema/generated/grove.attempt.json +199 -0
- package/src/schema/generated/grove.path.json +93 -0
- package/src/schema/generated/grove.review.json +27 -0
- package/src/schema/generated/grove.task.json +164 -0
- package/src/schema/generated/intel.scenario.json +58 -0
- package/src/schema/generated/intel.signal.json +113 -0
- package/src/schema/generated/intel.world.json +15 -0
- package/src/schema/generated/landing.block.json +201 -0
- package/src/schema/generated/landing.page.json +84 -0
- package/src/schema/generated/metatron.config.json +119 -0
- package/src/schema/generated/metatron.permission.json +36 -0
- package/src/schema/generated/metatron.skill.json +36 -0
- package/src/schema/generated/metatron.task.json +114 -0
- package/src/schema/generated/metatron.template.json +26 -0
- package/src/schema/generated/metatron.workspace.json +60 -0
- package/src/schema/generated/order.status.json +21 -0
- package/src/schema/generated/polyhope.backtest.json +161 -0
- package/src/schema/generated/polyhope.feed.json +33 -0
- package/src/schema/generated/polyhope.run.json +94 -0
- package/src/schema/generated/polyhope.strategy.json +152 -0
- package/src/schema/generated/polymax.activity.json +65 -0
- package/src/schema/generated/polymax.aggr-feed.json +28 -0
- package/src/schema/generated/polymax.aggr.json +20 -0
- package/src/schema/generated/polymax.alert.json +56 -0
- package/src/schema/generated/polymax.bot-config.json +14 -0
- package/src/schema/generated/polymax.bot-status.json +35 -0
- package/src/schema/generated/polymax.classification.json +55 -0
- package/src/schema/generated/polymax.deposits.json +45 -0
- package/src/schema/generated/polymax.holding.json +55 -0
- package/src/schema/generated/polymax.identity.json +71 -0
- package/src/schema/generated/polymax.lb-entry.json +75 -0
- package/src/schema/generated/polymax.leaderboard.json +37 -0
- package/src/schema/generated/polymax.market-ref.json +25 -0
- package/src/schema/generated/polymax.performance.json +65 -0
- package/src/schema/generated/polymax.profile.json +16 -0
- package/src/schema/generated/polymax.scan-result.json +50 -0
- package/src/schema/generated/polymax.status.json +40 -0
- package/src/schema/generated/polymax.tags.json +53 -0
- package/src/schema/generated/polymax.trader.json +16 -0
- package/src/schema/generated/polymax.wallet-market.json +50 -0
- package/src/schema/generated/polymax.wallet-pnl.json +35 -0
- package/src/schema/generated/pult.concept.json +53 -0
- package/src/schema/generated/pult.config.json +227 -0
- package/src/schema/generated/pult.connector.json +72 -0
- package/src/schema/generated/pult.market.json +113 -0
- package/src/schema/generated/pult.order.json +113 -0
- package/src/schema/generated/pult.rete.json +68 -0
- package/src/schema/generated/pult.sensor.json +74 -0
- package/src/schema/generated/pult.signal.json +54 -0
- package/src/schema/generated/pult.synapse.json +93 -0
- package/src/schema/generated/pult.trade.json +93 -0
- package/src/schema/generated/resim.config.json +34 -0
- package/src/schema/generated/resim.function.json +62 -0
- package/src/schema/generated/resim.goal.json +22 -0
- package/src/schema/generated/resim.resource.json +40 -0
- package/src/schema/generated/resim.state.json +48 -0
- package/src/schema/generated/resim.world.json +40 -0
- package/src/schema/generated/saveme.action.save.json +29 -0
- package/src/schema/generated/saveme.message.json +36 -0
- package/src/schema/generated/saveme.router.json +31 -0
- package/src/schema/generated/t.coolify.json +50 -0
- package/src/schema/generated/t.event.json +46 -0
- package/src/schema/generated/t.logs.json +155 -0
- package/src/schema/generated/t.note.json +31 -0
- package/src/schema/generated/t.person.json +36 -0
- package/src/schema/generated/t.tenant.json +57 -0
- package/src/schema/generated/t.tenant.status.json +42 -0
- package/src/schema/generated/tagger.config.json +115 -0
- package/src/schema/generated/tagger.result.json +57 -0
- package/src/schema/generated/tagger.tree.json +36 -0
- package/src/schema/generated/ui.table.json +46 -0
- package/src/schema/types.ts +1 -0
- package/src/server/actions.test.ts +1 -1
- package/src/server/actions.ts +1 -1
- package/src/server/api.test.ts +9 -0
- package/src/server/auth.ts +4 -3
- package/src/server/client.ts +1 -1
- package/src/server/coverage.test.ts +1 -1
- package/src/server/doc-index.ts +13 -12
- package/src/server/e2e.test.ts +4 -3
- package/src/server/factory.ts +46 -24
- package/src/server/main.ts +4 -36
- package/src/server/migrate.ts +4 -4
- package/src/server/mods-mount.ts +0 -2
- package/src/server/mount-adapters.ts +9 -4
- package/src/server/mount.test.ts +73 -5
- package/src/server/prefab.ts +3 -2
- package/src/server/refs.test.ts +82 -0
- package/src/server/refs.ts +64 -0
- package/src/server/server.ts +14 -3
- package/src/server/sub.ts +2 -2
- package/src/server/trpc.ts +9 -3
- package/src/tree/fs.ts +21 -15
- package/src/tree/index.test.ts +26 -0
- package/src/tree/index.ts +2 -1
- package/src/tree-chain.test.ts +37 -44
- package/src/tree-chain.ts +11 -5
- package/src/uri.test.ts +32 -5
- package/src/uri.ts +4 -2
- package/dist/mods/mcp/server.d.ts.map +0 -1
- package/dist/mods/mcp/server.js.map +0 -1
- package/dist/mods/mcp/service.d.ts.map +0 -1
- package/dist/mods/mcp/service.js.map +0 -1
- package/dist/mods/mcp/types.d.ts.map +0 -1
- package/dist/mods/mcp/types.js.map +0 -1
- package/dist/schema/test-opaque.d.ts +0 -3
- package/dist/schema/test-opaque.d.ts.map +0 -1
- package/dist/schema/test-opaque.js +0 -43
- package/dist/schema/test-opaque.js.map +0 -1
- package/dist/server/mcp.d.ts.map +0 -1
- package/dist/server/mcp.js.map +0 -1
- package/src/mods/mcp/CLAUDE.md +0 -6
- package/src/mods/mcp/server.ts +0 -2
- package/src/mods/mcp/service.ts +0 -19
- package/src/mods/mcp/types.ts +0 -7
- package/src/schema/test-opaque.ts +0 -42
- package/src/server/mcp.ts +0 -326
|
@@ -71,17 +71,13 @@ export default defineMod({
|
|
|
71
71
|
const existing = await store.get('/sensors/weather');
|
|
72
72
|
if (existing) return; // идемпотентно
|
|
73
73
|
|
|
74
|
-
await store.set({
|
|
75
|
-
$path: '/sensors/weather',
|
|
76
|
-
$type: 'weather.sensor',
|
|
74
|
+
await store.set(createNode('/sensors/weather', 'weather.sensor', {
|
|
77
75
|
config: { $type: 'weather.config', location: 'Moscow', interval: 60 },
|
|
78
|
-
}
|
|
76
|
+
}));
|
|
79
77
|
|
|
80
|
-
await store.set({
|
|
81
|
-
$path: '/sys/autostart/weather',
|
|
82
|
-
$type: 'ref',
|
|
78
|
+
await store.set(createNode('/sys/autostart/weather', 'ref', {
|
|
83
79
|
$ref: '/sensors/weather',
|
|
84
|
-
}
|
|
80
|
+
}));
|
|
85
81
|
},
|
|
86
82
|
|
|
87
83
|
onLoad: () => console.log('Weather mod loaded'),
|
|
@@ -302,3 +298,80 @@ buf.clear(); // сбросить всё
|
|
|
302
298
|
4. `expire()` — таймаут для зависших мутаций (сервер не ответил)
|
|
303
299
|
|
|
304
300
|
**Почему хранится method + data:** `structuredClone()` стирает прототипы. Без сохранённой ссылки на метод rebase не может переиграть мутации.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Views — register / Render / RenderContext
|
|
305
|
+
|
|
306
|
+
Вьюхи НИКОГДА не рендерят дочерние компоненты напрямую. Всё через реестр:
|
|
307
|
+
|
|
308
|
+
### Регистрация — типобезопасная
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { register } from '@treenity/core/core';
|
|
312
|
+
import type { View } from '@treenity/react/context';
|
|
313
|
+
import { Render, RenderContext } from '@treenity/react/context';
|
|
314
|
+
|
|
315
|
+
// View<T> — типизированный React-компонент: { value: T, ctx?, onChange? }
|
|
316
|
+
const SensorView: View<WeatherSensor> = ({ value, ctx }) => {
|
|
317
|
+
const path = ctx!.node.$path; // путь ноды — через ctx, НЕ из value
|
|
318
|
+
return <div>{value.location} — {value.temperature}°C</div>;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const SensorRow: View<WeatherSensor> = ({ value, ctx }) => { ... };
|
|
322
|
+
|
|
323
|
+
// register принимает Class<T> — T пробрасывается в handler автоматически
|
|
324
|
+
register(WeatherSensor, 'react', SensorView); // detail view
|
|
325
|
+
register(WeatherSensor, 'react:list', SensorRow); // compact list view
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**ЗАПРЕЩЕНО:**
|
|
329
|
+
```tsx
|
|
330
|
+
// WRONG — as any убивает типизацию, прячет ошибки:
|
|
331
|
+
register('weather.sensor', 'react', SensorView as any);
|
|
332
|
+
|
|
333
|
+
// WRONG — NodeData не смешивать с типами компонентов:
|
|
334
|
+
function SensorRow({ value }: { value: NodeData & WeatherSensor }) { ... }
|
|
335
|
+
|
|
336
|
+
// WRONG — путь из value (value — данные компонента, не нода):
|
|
337
|
+
const path = value.$path; // может не существовать!
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Рендер дочерних — ТОЛЬКО через `<Render>`
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
// WRONG — хардкод, обходит реестр:
|
|
344
|
+
{sensors.map(s => <SensorRow key={s.$path} sensor={s} />)}
|
|
345
|
+
|
|
346
|
+
// RIGHT — через реестр, композабельно:
|
|
347
|
+
<RenderContext name="react:list">
|
|
348
|
+
{sensors.map(s => <Render key={s.$path} value={s} />)}
|
|
349
|
+
</RenderContext>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Контексты
|
|
353
|
+
|
|
354
|
+
| Контекст | Назначение |
|
|
355
|
+
|----------|-----------|
|
|
356
|
+
| `react` | default/detail view |
|
|
357
|
+
| `react:list` | компактная карточка для списков |
|
|
358
|
+
| `react:edit` | форма редактирования |
|
|
359
|
+
|
|
360
|
+
Fallback автоматический: `react:list` → `react` → `default`.
|
|
361
|
+
|
|
362
|
+
### Сигнатура view-функции
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
// View<T> даёт: value: T, ctx: ViewCtx (node, path, execute), onChange
|
|
366
|
+
const SensorView: View<WeatherSensor> = ({ value, ctx }) => {
|
|
367
|
+
// value — данные компонента (WeatherSensor fields)
|
|
368
|
+
// ctx!.node — полная NodeData (с $path, $type, $acl и т.д.)
|
|
369
|
+
// ctx!.execute(action, data) — вызов экшена
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Почему это важно
|
|
374
|
+
|
|
375
|
+
- **Композиция:** любой тип рендерит чужих детей, не зная их вьюх
|
|
376
|
+
- **Переопределение:** зарегистрируй новый `react:list` для типа → все списки обновятся
|
|
377
|
+
- **AI visibility:** реестр интроспектируемый, AI находит доступные вьюхи
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type NodeData, register } from '#core';
|
|
1
|
+
import { getComponent, type NodeData, register } from '#core';
|
|
3
2
|
import { TickerConfig } from './types';
|
|
4
3
|
|
|
5
4
|
register('ticker', 'service', async (node: NodeData, ctx) => {
|
|
6
|
-
const config =
|
|
5
|
+
const config = getComponent(node, TickerConfig);
|
|
7
6
|
const interval = (config?.intervalSec ?? 10) * 1000;
|
|
8
7
|
|
|
9
8
|
const timer = setInterval(async () => {
|
package/src/mod/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
export { defineMod } from './types';
|
|
4
4
|
export type { TreenityMod, ModManifest, ModState, LoadedMod } from './types';
|
|
5
5
|
export { discoverMods } from './discover';
|
|
6
|
-
export { sortByDependencies, loadMods, loadLocalMods, getLoadedMods, getMod, isModLoaded, clearModRegistry } from './loader';
|
|
6
|
+
export { sortByDependencies, loadMods, loadLocalMods, loadAllMods, getLoadedMods, getMod, isModLoaded, clearModRegistry } from './loader';
|
|
7
7
|
export type { LoadTarget, LoadResult } from './loader';
|
|
8
8
|
export { OptimisticBuffer } from './optimistic';
|
|
9
9
|
export type { PendingMutation } from './optimistic';
|
package/src/mod/loader.test.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { beforeEach, describe, it } from 'node:test';
|
|
3
|
-
import {
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { clearModRegistry, getLoadedMods, getMod, isModLoaded, loadLocalMods, loadMods, sortByDependencies } from './loader';
|
|
4
7
|
import type { ModManifest } from './types';
|
|
5
8
|
|
|
6
9
|
function m(name: string, deps?: string[]): ModManifest {
|
|
@@ -166,3 +169,52 @@ describe('loadMods', () => {
|
|
|
166
169
|
assert.equal(isModLoaded('fail-mod'), false);
|
|
167
170
|
});
|
|
168
171
|
});
|
|
172
|
+
|
|
173
|
+
describe('loadLocalMods', () => {
|
|
174
|
+
let tmpDir: string;
|
|
175
|
+
|
|
176
|
+
beforeEach(() => {
|
|
177
|
+
clearModRegistry();
|
|
178
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'treenity-mods-'));
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('discovers mod with server.ts in a directory', async () => {
|
|
182
|
+
const modDir = join(tmpDir, 'my-mod');
|
|
183
|
+
mkdirSync(modDir);
|
|
184
|
+
writeFileSync(join(modDir, 'server.ts'), 'globalThis.__canary_server_loaded = true;');
|
|
185
|
+
|
|
186
|
+
const result = await loadLocalMods(tmpDir, 'server');
|
|
187
|
+
assert.deepEqual(result.loaded, ['my-mod']);
|
|
188
|
+
assert.equal(result.failed.length, 0);
|
|
189
|
+
|
|
190
|
+
rmSync(tmpDir, { recursive: true });
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('skips mod without matching entry file', async () => {
|
|
194
|
+
const modDir = join(tmpDir, 'server-only');
|
|
195
|
+
mkdirSync(modDir);
|
|
196
|
+
writeFileSync(join(modDir, 'server.ts'), '');
|
|
197
|
+
|
|
198
|
+
const result = await loadLocalMods(tmpDir, 'client');
|
|
199
|
+
assert.deepEqual(result.loaded, []);
|
|
200
|
+
|
|
201
|
+
rmSync(tmpDir, { recursive: true });
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('returns empty for nonexistent directory', async () => {
|
|
205
|
+
const result = await loadLocalMods('/tmp/no-such-dir-xyz-42', 'server');
|
|
206
|
+
assert.deepEqual(result.loaded, []);
|
|
207
|
+
assert.deepEqual(result.failed, []);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('skips hidden directories', async () => {
|
|
211
|
+
const modDir = join(tmpDir, '.hidden-mod');
|
|
212
|
+
mkdirSync(modDir);
|
|
213
|
+
writeFileSync(join(modDir, 'server.ts'), '');
|
|
214
|
+
|
|
215
|
+
const result = await loadLocalMods(tmpDir, 'server');
|
|
216
|
+
assert.deepEqual(result.loaded, []);
|
|
217
|
+
|
|
218
|
+
rmSync(tmpDir, { recursive: true });
|
|
219
|
+
});
|
|
220
|
+
});
|
package/src/mod/loader.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Tree } from '#tree';
|
|
4
4
|
import { readdir, stat } from 'node:fs/promises';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
6
|
import { setCurrentMod } from './tracking';
|
|
7
7
|
import type { LoadedMod, ModManifest, TreenityMod } from './types';
|
|
8
8
|
|
|
@@ -172,3 +172,36 @@ export async function loadLocalMods(modsDir: string, target: LoadTarget): Promis
|
|
|
172
172
|
|
|
173
173
|
return result;
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
// ── Load all mods: internal + engine + project (CWD) ──
|
|
177
|
+
|
|
178
|
+
export async function loadAllMods(target: LoadTarget, ...extraDirs: string[]): Promise<LoadResult> {
|
|
179
|
+
const internalDir = new URL('../mods', import.meta.url).pathname;
|
|
180
|
+
const engineDir = new URL('../../../mods', import.meta.url).pathname;
|
|
181
|
+
|
|
182
|
+
const dirs = [internalDir, engineDir];
|
|
183
|
+
|
|
184
|
+
// CWD/mods/ if different from engine mods
|
|
185
|
+
const projectDir = resolve('mods');
|
|
186
|
+
if (resolve(projectDir) !== resolve(engineDir)) dirs.push(projectDir);
|
|
187
|
+
|
|
188
|
+
dirs.push(...extraDirs);
|
|
189
|
+
|
|
190
|
+
const seen = new Set<string>();
|
|
191
|
+
const result: LoadResult = { loaded: [], failed: [] };
|
|
192
|
+
|
|
193
|
+
for (const dir of dirs) {
|
|
194
|
+
const abs = resolve(dir);
|
|
195
|
+
if (seen.has(abs)) continue;
|
|
196
|
+
seen.add(abs);
|
|
197
|
+
|
|
198
|
+
const r = await loadLocalMods(dir, target);
|
|
199
|
+
result.loaded.push(...r.loaded);
|
|
200
|
+
result.failed.push(...r.failed);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (result.failed.length) console.error('failed mods:', result.failed.map(f => `${f.name}: ${f.error.message}`).join(', '));
|
|
204
|
+
console.log(`mods: ${result.loaded.join(', ')}`);
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
package/src/mods/llm/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ export function exportSchemaForLLM(): SchemaExport {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export async function describeTree(ctx: ActionCtx, data: unknown): Promise<string> {
|
|
35
|
-
const
|
|
35
|
+
const depth = Number((data as Record<string, unknown>)?.depth) || 3;
|
|
36
36
|
const path = ctx.node.$path;
|
|
37
37
|
const lines: string[] = [];
|
|
38
38
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Core built-in types — registered so validation (Write-Barrier) accepts them.
|
|
2
|
+
// Convention: no dot = core built-in (see Type Naming Convention in CLAUDE.md)
|
|
3
|
+
|
|
4
|
+
import { normalizeType, register } from '#core';
|
|
5
|
+
|
|
6
|
+
const builtins = [
|
|
7
|
+
'dir', 'root', 'ref', 'user', 'type', 'mount-point', 'session',
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export function registerBuiltins() {
|
|
11
|
+
for (const type of builtins) {
|
|
12
|
+
register(type, 'schema', () => ({
|
|
13
|
+
$id: normalizeType(type),
|
|
14
|
+
type: 'object' as const,
|
|
15
|
+
title: type,
|
|
16
|
+
properties: {},
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
registerBuiltins();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// t.logs — unified log buffer, queryable via actions + MCP
|
|
2
|
+
|
|
3
|
+
import { registerType } from '#comp';
|
|
4
|
+
import { interceptConsole, logStats, queryLogs, type LogLevel } from '#log';
|
|
5
|
+
|
|
6
|
+
interceptConsole();
|
|
7
|
+
|
|
8
|
+
/** @description Server log buffer — query, grep, filter by level */
|
|
9
|
+
export class Logs {
|
|
10
|
+
/** @description Query log buffer with grep, level filter, head/tail */
|
|
11
|
+
async query(data: {
|
|
12
|
+
/** Regex pattern to filter messages */ grep?: string;
|
|
13
|
+
/** Log level(s) to include */ level?: LogLevel | LogLevel[];
|
|
14
|
+
/** Return first N entries */ head?: number;
|
|
15
|
+
/** Return last N entries */ tail?: number;
|
|
16
|
+
}) {
|
|
17
|
+
return queryLogs(data);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** @description Buffer stats: buffered count, total ever, max capacity */
|
|
21
|
+
async stats() {
|
|
22
|
+
return logStats();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
registerType('t.logs', Logs);
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { type NodeData, R, S } from '@treenity/core
|
|
1
|
+
import { type NodeData, R, S } from '@treenity/core';
|
|
2
2
|
import { registerPrefab } from '@treenity/core/mod';
|
|
3
3
|
|
|
4
|
+
// Universal infra — works with any storage backend (FS, memory, Mongo)
|
|
4
5
|
registerPrefab('core', 'seed', [
|
|
5
6
|
{ $path: 'sys', $type: 'treenity.system' },
|
|
6
|
-
{ $path: 'auth', $type: 'dir' },
|
|
7
|
-
{ $path: 'auth/users', $type: 'mount-point',
|
|
8
|
-
connection: { $type: 'connection', db: 'treenity', collection: 'users' },
|
|
9
|
-
mount: { $type: 't.mount.mongo' },
|
|
10
|
-
$acl: [{ g: 'authenticated', p: R | S }, { g: 'public', p: 0 }],
|
|
11
|
-
},
|
|
12
7
|
{ $path: 'sys/types', $type: 'mount-point',
|
|
13
8
|
mount: { $type: 't.mount.types' },
|
|
14
9
|
$acl: [{ g: 'public', p: R }],
|
|
@@ -17,40 +12,47 @@ registerPrefab('core', 'seed', [
|
|
|
17
12
|
mount: { $type: 't.mount.mods' },
|
|
18
13
|
$acl: [{ g: 'public', p: R }],
|
|
19
14
|
},
|
|
20
|
-
{ $path: '
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
connection: { $type: 'connection', db: 'treenity', collection: 'orders' },
|
|
15
|
+
{ $path: 'sys/mcp', $type: 'mcp.server', port: 0 },
|
|
16
|
+
{ $path: 'sys/autostart', $type: 'autostart' },
|
|
17
|
+
{ $path: '/sys/autostart/mcp', $type: 'ref', $ref: '/sys/mcp' },
|
|
18
|
+
{ $path: 'proc', $type: 'mount-point',
|
|
19
|
+
mount: { $type: 't.mount.memory' },
|
|
20
|
+
$acl: [{ g: 'public', p: R }],
|
|
27
21
|
},
|
|
28
|
-
{ $path: '
|
|
22
|
+
{ $path: 'llm', $type: 't.llm' },
|
|
29
23
|
{ $path: 'demo', $type: 'dir',
|
|
30
24
|
metadata: { $type: 'metadata', title: 'Demo Node', description: 'Try calling actions' },
|
|
31
25
|
status: { $type: 'status', value: 'draft' },
|
|
32
26
|
counter: { $type: 'counter', count: 0 },
|
|
33
27
|
},
|
|
34
|
-
{ $path: 'llm', $type: 't.llm' },
|
|
35
|
-
{ $path: 'sys/mcp', $type: 'mcp.server', port: 0 },
|
|
36
28
|
{ $path: 'demo/sensors', $type: 'examples.demo.sensor',
|
|
37
29
|
mount: { $type: 't.mount.memory' },
|
|
38
30
|
},
|
|
31
|
+
{ $path: '/sys/autostart/sensors', $type: 'ref', $ref: '/demo/sensors' },
|
|
39
32
|
{ $path: 'sys/claude-search', $type: 'claude-search' },
|
|
40
|
-
{ $path: 'proc', $type: 'mount-point',
|
|
41
|
-
mount: { $type: 't.mount.memory' },
|
|
42
|
-
$acl: [{ g: 'public', p: R }],
|
|
43
|
-
},
|
|
44
|
-
{ $path: 'sys/autostart', $type: 'autostart' },
|
|
45
|
-
{ $path: '/sys/autostart/mcp', $type: 'ref', $ref: '/sys/mcp' },
|
|
46
33
|
{ $path: '/sys/autostart/claude-search', $type: 'ref', $ref: '/sys/claude-search' },
|
|
47
|
-
{ $path: '/sys/autostart/sensors', $type: 'ref', $ref: '/demo/sensors' },
|
|
48
34
|
] as NodeData[], (nodes) => {
|
|
49
|
-
// MCP port from env
|
|
50
35
|
const mcpPort = Number(process.env.MCP_PORT) || 3212;
|
|
51
|
-
|
|
36
|
+
return nodes.map(n =>
|
|
52
37
|
n.$path === 'sys/mcp' ? { ...n, port: mcpPort } : n,
|
|
53
38
|
);
|
|
54
|
-
|
|
55
|
-
return result;
|
|
56
39
|
}, { tier: 'core' });
|
|
40
|
+
|
|
41
|
+
// Mongo-dependent infra — auth, orders, entities
|
|
42
|
+
registerPrefab('mongo', 'seed', [
|
|
43
|
+
{ $path: 'auth', $type: 'dir' },
|
|
44
|
+
{ $path: 'auth/users', $type: 'mount-point',
|
|
45
|
+
connection: { $type: 'connection', db: 'treenity', collection: 'users' },
|
|
46
|
+
mount: { $type: 't.mount.mongo' },
|
|
47
|
+
$acl: [{ g: 'authenticated', p: R | S }, { g: 'public', p: 0 }],
|
|
48
|
+
},
|
|
49
|
+
{ $path: 'auth/sessions', $type: 'mount-point',
|
|
50
|
+
connection: { $type: 'connection', db: 'treenity', collection: 'sessions' },
|
|
51
|
+
mount: { $type: 't.mount.mongo' },
|
|
52
|
+
},
|
|
53
|
+
{ $path: 'mnt', $type: 'dir' },
|
|
54
|
+
{ $path: 'mnt/orders', $type: 't.mount.mongo',
|
|
55
|
+
connection: { $type: 'connection', db: 'treenity', collection: 'orders' },
|
|
56
|
+
},
|
|
57
|
+
{ $path: 'entities', $type: 'dir' },
|
|
58
|
+
] as NodeData[]);
|
package/src/mods/uix/client.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { onResolveMiss, register, unregister } from '#core';
|
|
|
5
5
|
import { createInflight } from '#tree/inflight';
|
|
6
6
|
import * as cache from '@treenity/react/cache';
|
|
7
7
|
import { tree } from '@treenity/react/client';
|
|
8
|
-
import {
|
|
8
|
+
import { UixNoView } from '@treenity/react/context';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { compileComponent, invalidateCache } from './compile';
|
|
11
11
|
|
|
@@ -55,7 +55,7 @@ register('uix.view', 'react', UixView);
|
|
|
55
55
|
const dedup = createInflight<void>();
|
|
56
56
|
|
|
57
57
|
// Watch a type node for future view creation (e.g. AI agent saves view.source via MCP).
|
|
58
|
-
// When the node changes and gains view.source, swap
|
|
58
|
+
// When the node changes and gains view.source, swap UixNoView for the real view.
|
|
59
59
|
const watched = new Set<string>();
|
|
60
60
|
|
|
61
61
|
function watchTypeNode(type: string, typePath: string) {
|
|
@@ -86,7 +86,7 @@ function watchTypeNode(type: string, typePath: string) {
|
|
|
86
86
|
onResolveMiss('react', (type) => {
|
|
87
87
|
// Built-in types (no dot) never have UIX views — avoid contaminating tRPC batches
|
|
88
88
|
if (!type.includes('.')) {
|
|
89
|
-
register(type, 'react',
|
|
89
|
+
register(type, 'react', UixNoView);
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -98,7 +98,7 @@ onResolveMiss('react', (type) => {
|
|
|
98
98
|
const source = (typeNode as any)?.view?.source;
|
|
99
99
|
|
|
100
100
|
if (!source || typeof source !== 'string') {
|
|
101
|
-
register(type, 'react',
|
|
101
|
+
register(type, 'react', UixNoView);
|
|
102
102
|
// Watch for future view creation (e.g. AI agent saves view.source later)
|
|
103
103
|
watchTypeNode(type, typePath);
|
|
104
104
|
return;
|
package/src/mods/uix/compile.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Turns raw JSX/TSX code string into a React component
|
|
3
3
|
|
|
4
4
|
import { register } from '#core';
|
|
5
|
-
import { Render, RenderContext, RenderField } from '@treenity/react/context';
|
|
6
|
-
import { useChildren, usePath } from '@treenity/react/hooks';
|
|
5
|
+
import { Render, RenderContext, RenderField, viewCtx } from '@treenity/react/context';
|
|
6
|
+
import { execute, useChildren, usePath } from '@treenity/react/hooks';
|
|
7
7
|
import React from 'react';
|
|
8
8
|
import { compileJSX } from './jsx-parser';
|
|
9
9
|
|
|
@@ -22,6 +22,8 @@ const SCOPE: Record<string, unknown> = {
|
|
|
22
22
|
RenderField,
|
|
23
23
|
usePath,
|
|
24
24
|
useChildren,
|
|
25
|
+
execute,
|
|
26
|
+
viewCtx,
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const cache = new Map<string, React.FC<any>>();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Test fixture for extract-schemas.test.ts — provides Autostart class after monorepo split.
|
|
2
|
+
// The real Autostart moved to mods/autostart/ (outside core/src/), so tests use this stub.
|
|
3
|
+
|
|
4
|
+
export class Autostart {
|
|
5
|
+
/** Start a service at given path */
|
|
6
|
+
start(data: {
|
|
7
|
+
/** service to start */
|
|
8
|
+
path: string;
|
|
9
|
+
}) {}
|
|
10
|
+
|
|
11
|
+
stop(data: { path: string }) {}
|
|
12
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "ai.agent",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "AI Agent — autonomous worker node at /agents/{name}\nLLM config (model, systemPrompt, sessionId) lives in named metatron.config component.\nWork creates metatron.task nodes under /agents/{name}/tasks/.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"role": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"title": "Open-ended role string. Guardian policies keyed by role.",
|
|
10
|
+
"default": "qa"
|
|
11
|
+
},
|
|
12
|
+
"status": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": [
|
|
15
|
+
"idle",
|
|
16
|
+
"working",
|
|
17
|
+
"blocked",
|
|
18
|
+
"error",
|
|
19
|
+
"offline"
|
|
20
|
+
],
|
|
21
|
+
"default": "offline"
|
|
22
|
+
},
|
|
23
|
+
"currentTask": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"title": "Path to current board.task being worked on",
|
|
26
|
+
"default": ""
|
|
27
|
+
},
|
|
28
|
+
"taskRef": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"title": "Path to current metatron.task (live log)",
|
|
31
|
+
"default": ""
|
|
32
|
+
},
|
|
33
|
+
"lastRunAt": {
|
|
34
|
+
"type": "number",
|
|
35
|
+
"default": 0
|
|
36
|
+
},
|
|
37
|
+
"totalTokens": {
|
|
38
|
+
"type": "number",
|
|
39
|
+
"title": "Total tokens used across all tasks",
|
|
40
|
+
"default": 0
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": [
|
|
44
|
+
"role",
|
|
45
|
+
"status",
|
|
46
|
+
"currentTask",
|
|
47
|
+
"taskRef",
|
|
48
|
+
"lastRunAt",
|
|
49
|
+
"totalTokens"
|
|
50
|
+
],
|
|
51
|
+
"methods": {
|
|
52
|
+
"online": {
|
|
53
|
+
"description": "Bring agent online",
|
|
54
|
+
"arguments": []
|
|
55
|
+
},
|
|
56
|
+
"offline": {
|
|
57
|
+
"description": "Take agent offline",
|
|
58
|
+
"arguments": []
|
|
59
|
+
},
|
|
60
|
+
"assign": {
|
|
61
|
+
"description": "Assign a board task to this agent",
|
|
62
|
+
"arguments": [
|
|
63
|
+
{
|
|
64
|
+
"name": "data",
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"task": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"title": "Path to board.task"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": [
|
|
73
|
+
"task"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"complete": {
|
|
79
|
+
"description": "Agent finished current task",
|
|
80
|
+
"arguments": []
|
|
81
|
+
},
|
|
82
|
+
"block": {
|
|
83
|
+
"description": "Agent is blocked",
|
|
84
|
+
"arguments": [
|
|
85
|
+
{
|
|
86
|
+
"name": "data",
|
|
87
|
+
"anyOf": [
|
|
88
|
+
{},
|
|
89
|
+
{
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"reason": {
|
|
93
|
+
"anyOf": [
|
|
94
|
+
{},
|
|
95
|
+
{
|
|
96
|
+
"type": "string"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"title": "Reason"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"fail": {
|
|
108
|
+
"description": "Agent hit an error",
|
|
109
|
+
"arguments": [
|
|
110
|
+
{
|
|
111
|
+
"name": "data",
|
|
112
|
+
"anyOf": [
|
|
113
|
+
{},
|
|
114
|
+
{
|
|
115
|
+
"type": "object",
|
|
116
|
+
"properties": {
|
|
117
|
+
"error": {
|
|
118
|
+
"anyOf": [
|
|
119
|
+
{},
|
|
120
|
+
{
|
|
121
|
+
"type": "string"
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"title": "Error message"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|