trellis 3.1.6 → 3.1.7

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 (56) hide show
  1. package/README.md +1 -0
  2. package/bin/trellis.mjs +68 -2
  3. package/dist/better-sqlite-backend-ahx5p0br.js +147 -0
  4. package/dist/cli/index.js +116 -45
  5. package/dist/client/index.js +5 -4
  6. package/dist/cms/client.d.ts +2 -1
  7. package/dist/cms/client.d.ts.map +1 -1
  8. package/dist/cms/index.js +4 -0
  9. package/dist/cms/types.d.ts +1 -1
  10. package/dist/cms/types.d.ts.map +1 -1
  11. package/dist/core/index.d.ts +4 -0
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js +14 -4
  14. package/dist/core/persist/factory.d.ts +28 -0
  15. package/dist/core/persist/factory.d.ts.map +1 -0
  16. package/dist/core/persist/factory.js +8 -0
  17. package/dist/core/persist/sqlite-backend.d.ts.map +1 -1
  18. package/dist/core/persist/sqljs-backend.d.ts +60 -0
  19. package/dist/core/persist/sqljs-backend.d.ts.map +1 -0
  20. package/dist/core/persist/sqljs-backend.js +8 -0
  21. package/dist/db/index.js +11 -10
  22. package/dist/embeddings/index.js +1 -1
  23. package/dist/embeddings/store.d.ts.map +1 -1
  24. package/dist/{index-0zk3fx2s.js → index-4wxa8xz4.js} +4 -237
  25. package/dist/{index-6n5dcebj.js → index-53f3b8p8.js} +84 -30
  26. package/dist/{index-y3d71wzd.js → index-7pjz3tsy.js} +36 -6
  27. package/dist/{index-0q7wbasy.js → index-a2a394zz.js} +7 -3
  28. package/dist/index-h32txmxe.js +42 -0
  29. package/dist/index-h7zxhhhh.js +252 -0
  30. package/dist/index-h9e2efx4.js +251 -0
  31. package/dist/{index-2917tjd8.js → index-hr9qvv77.js} +15 -3
  32. package/dist/{index-hmdbnd4n.js → index-hy73j9z8.js} +1 -1
  33. package/dist/{index-q31hfjja.js → index-jgda3xyv.js} +1 -1
  34. package/dist/{index-7e27kvvj.js → index-wncptktd.js} +1 -1
  35. package/dist/index.js +8 -6
  36. package/dist/react/index.js +5 -4
  37. package/dist/{sdk-snn5gad3.js → sdk-bepky0xs.js} +5 -4
  38. package/dist/server/index.d.ts +2 -2
  39. package/dist/server/index.d.ts.map +1 -1
  40. package/dist/server/index.js +27 -24
  41. package/dist/server/node-adapter.d.ts +38 -0
  42. package/dist/server/node-adapter.d.ts.map +1 -0
  43. package/dist/server/node-adapter.js +108 -0
  44. package/dist/server/server-shared.d.ts +21 -0
  45. package/dist/server/server-shared.d.ts.map +1 -0
  46. package/dist/server/server.d.ts +19 -2
  47. package/dist/server/server.d.ts.map +1 -1
  48. package/dist/server/tenancy.d.ts +28 -2
  49. package/dist/server/tenancy.d.ts.map +1 -1
  50. package/dist/{server-mrctdwzr.js → server-szdjx0nt.js} +5 -3
  51. package/dist/sqlite-backend-0vsmc6qj.js +8 -0
  52. package/dist/{tenancy-7d1g4ayp.js → tenancy-pjm32b4v.js} +4 -3
  53. package/dist/vcs/blob-store.d.ts +2 -1
  54. package/dist/vcs/blob-store.d.ts.map +1 -1
  55. package/dist/vcs/index.js +2 -2
  56. package/package.json +16 -3
package/README.md CHANGED
@@ -81,6 +81,7 @@ off();
81
81
  ```
82
82
 
83
83
  - **Collections**: `cms.collections()` returns explicit `TypeSchema` collections and inferred collections from existing entity types.
84
+ - **Schema**: `cms.collection('blogpost').schema()` returns field definitions including types, labels, requirements, formulas, and targets.
84
85
  - **Normalized keys**: `cms.collection('blogpost')` matches stored entity types like `BlogPost`.
85
86
  - **References**: `expand` resolves reference ids from facts and graph links into nested entries.
86
87
  - **Scaffolds**: `scaffoldConsumer({ collection, framework, directory })` generates starter consumer code for vanilla, React, Solid, or Vue.
package/bin/trellis.mjs CHANGED
@@ -1,2 +1,68 @@
1
- #!/usr/bin/env bun
2
- import '../dist/cli/index.js';
1
+ #!/usr/bin/env node
2
+ // Trellis CLI launcher.
3
+ //
4
+ // The bundled CLI in dist/ is built with `--target bun` and uses Bun-specific
5
+ // APIs at runtime. This launcher works under either runtime:
6
+ // - If invoked by bun, it imports the bundle directly.
7
+ // - If invoked by node (the default for `npx trellis`), it re-execs the same
8
+ // script under bun. If bun is missing, it prints actionable install hints.
9
+
10
+ import { spawnSync } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { dirname, join, delimiter } from 'node:path';
13
+ import { existsSync } from 'node:fs';
14
+
15
+ const isWindows = process.platform === 'win32';
16
+
17
+ if (process.versions?.bun) {
18
+ await import('../dist/cli/index.js');
19
+ } else {
20
+ const bun = findOnPath('bun') ?? findUserBun();
21
+ if (!bun) {
22
+ process.stderr.write(
23
+ [
24
+ '',
25
+ 'Trellis requires Bun to run.',
26
+ '',
27
+ 'Install Bun:',
28
+ ' macOS/Linux: curl -fsSL https://bun.sh/install | bash',
29
+ ' Windows: powershell -c "irm bun.sh/install.ps1 | iex"',
30
+ '',
31
+ 'More info: https://bun.sh',
32
+ '',
33
+ ].join('\n'),
34
+ );
35
+ process.exit(1);
36
+ }
37
+
38
+ const here = fileURLToPath(import.meta.url);
39
+ const result = spawnSync(bun, [here, ...process.argv.slice(2)], {
40
+ stdio: 'inherit',
41
+ });
42
+ process.exit(result.status ?? 1);
43
+ }
44
+
45
+ function findOnPath(cmd) {
46
+ const PATH = process.env.PATH || '';
47
+ const exts = isWindows ? ['.exe', '.cmd', '.bat', ''] : [''];
48
+ for (const dir of PATH.split(isWindows ? ';' : delimiter)) {
49
+ if (!dir) continue;
50
+ for (const ext of exts) {
51
+ const p = join(dir, cmd + ext);
52
+ if (existsSync(p)) return p;
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+
58
+ function findUserBun() {
59
+ const home = process.env.HOME || process.env.USERPROFILE;
60
+ if (!home) return null;
61
+ const candidates = isWindows
62
+ ? [join(home, '.bun', 'bin', 'bun.exe')]
63
+ : [join(home, '.bun', 'bin', 'bun')];
64
+ for (const p of candidates) {
65
+ if (existsSync(p)) return p;
66
+ }
67
+ return null;
68
+ }
@@ -0,0 +1,147 @@
1
+ // @bun
2
+ import {
3
+ __require
4
+ } from "./index-a76rekgs.js";
5
+
6
+ // src/core/persist/better-sqlite-backend.ts
7
+ var __filename = "/Users/trentbrew/TURTLE/Projects/Packages/trellis-package/src/core/persist/better-sqlite-backend.ts";
8
+ var SCHEMA_SQL = `
9
+ CREATE TABLE IF NOT EXISTS ops (
10
+ hash TEXT PRIMARY KEY,
11
+ kind TEXT NOT NULL,
12
+ timestamp TEXT NOT NULL,
13
+ agent_id TEXT NOT NULL,
14
+ previous_hash TEXT,
15
+ payload TEXT NOT NULL
16
+ );
17
+
18
+ CREATE TABLE IF NOT EXISTS snapshots (
19
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
20
+ last_op_hash TEXT NOT NULL,
21
+ data TEXT NOT NULL,
22
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
23
+ );
24
+
25
+ CREATE TABLE IF NOT EXISTS blobs (
26
+ hash TEXT PRIMARY KEY,
27
+ content BLOB NOT NULL
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_ops_kind ON ops(kind);
31
+ CREATE INDEX IF NOT EXISTS idx_ops_timestamp ON ops(timestamp);
32
+ CREATE INDEX IF NOT EXISTS idx_ops_agent ON ops(agent_id);
33
+ CREATE INDEX IF NOT EXISTS idx_ops_previous ON ops(previous_hash);
34
+ CREATE INDEX IF NOT EXISTS idx_snapshots_op ON snapshots(last_op_hash);
35
+ `;
36
+
37
+ class BetterSqliteKernelBackend {
38
+ db;
39
+ _stmts;
40
+ _initialized = false;
41
+ constructor(dbPath) {
42
+ try {
43
+ const createRequire = __require("module").createRequire;
44
+ const req = createRequire(__filename);
45
+ const Database = req("better-sqlite3");
46
+ this.db = new Database(dbPath);
47
+ } catch (e) {
48
+ throw new Error(`Failed to initialize SQLite backend: ${e instanceof Error ? e.message : "Unknown error"}. ` + "Ensure better-sqlite3 is installed: npm install better-sqlite3");
49
+ }
50
+ this._stmts = this._prepareStatements();
51
+ }
52
+ _prepareStatements() {
53
+ const db = this.db;
54
+ return {
55
+ insert: db.prepare("INSERT OR REPLACE INTO ops (hash, kind, timestamp, agent_id, previous_hash, payload) VALUES (?, ?, ?, ?, ?, ?)"),
56
+ readAll: db.prepare("SELECT * FROM ops ORDER BY timestamp ASC"),
57
+ readUntil: db.prepare("SELECT * FROM ops WHERE timestamp <= (SELECT timestamp FROM ops WHERE hash = ?) ORDER BY timestamp ASC"),
58
+ readAfter: db.prepare("SELECT * FROM ops WHERE timestamp > (SELECT timestamp FROM ops WHERE hash = ?) ORDER BY timestamp ASC"),
59
+ getByHash: db.prepare("SELECT * FROM ops WHERE hash = ?"),
60
+ getLast: db.prepare("SELECT * FROM ops ORDER BY timestamp DESC LIMIT 1"),
61
+ count: db.prepare("SELECT COUNT(*) as count FROM ops"),
62
+ saveSnapshot: db.prepare("INSERT INTO snapshots (last_op_hash, data, created_at) VALUES (?, ?, ?)"),
63
+ loadLatestSnapshot: db.prepare("SELECT * FROM snapshots ORDER BY id DESC LIMIT 1")
64
+ };
65
+ }
66
+ init() {
67
+ if (this._initialized)
68
+ return;
69
+ this.db.exec(SCHEMA_SQL);
70
+ this._initialized = true;
71
+ }
72
+ append(op) {
73
+ const payload = JSON.stringify({
74
+ facts: op.facts,
75
+ links: op.links,
76
+ deleteFacts: op.deleteFacts,
77
+ deleteLinks: op.deleteLinks
78
+ });
79
+ this._stmts.insert.run(op.hash, op.kind, op.timestamp, op.agentId, op.previousHash ?? null, payload);
80
+ }
81
+ readAll() {
82
+ const rows = this._stmts.readAll.all();
83
+ return rows.map(this._rowToOp);
84
+ }
85
+ readUntil(opHash) {
86
+ const row = this._stmts.getByHash.get(opHash);
87
+ if (!row)
88
+ return [];
89
+ const rows = this._stmts.readUntil.all(opHash);
90
+ return rows.map(this._rowToOp);
91
+ }
92
+ readUntilTimestamp(isoTimestamp) {
93
+ const rows = this.db.prepare("SELECT * FROM ops WHERE timestamp <= ? ORDER BY timestamp ASC").all(isoTimestamp);
94
+ return rows.map(this._rowToOp);
95
+ }
96
+ readAfter(opHash) {
97
+ const row = this._stmts.getByHash.get(opHash);
98
+ if (!row)
99
+ return this.readAll();
100
+ const rows = this._stmts.readAfter.all(opHash);
101
+ return rows.map(this._rowToOp);
102
+ }
103
+ getByHash(hash) {
104
+ const row = this._stmts.getByHash.get(hash);
105
+ return row ? this._rowToOp(row) : undefined;
106
+ }
107
+ getLastOp() {
108
+ const row = this._stmts.getLast.get();
109
+ return row ? this._rowToOp(row) : undefined;
110
+ }
111
+ getOpCount() {
112
+ const row = this._stmts.count.get();
113
+ return row?.count ?? 0;
114
+ }
115
+ saveSnapshot(hash, data) {
116
+ this._stmts.saveSnapshot.run(hash, data, new Date().toISOString());
117
+ }
118
+ loadLatestSnapshot() {
119
+ const row = this._stmts.loadLatestSnapshot.get();
120
+ if (!row)
121
+ return;
122
+ return {
123
+ lastOpHash: row.last_op_hash,
124
+ data: row.data
125
+ };
126
+ }
127
+ close() {
128
+ this.db?.close();
129
+ }
130
+ _rowToOp(row) {
131
+ const payload = JSON.parse(row.payload);
132
+ return {
133
+ hash: row.hash,
134
+ kind: row.kind,
135
+ timestamp: row.timestamp,
136
+ agentId: row.agent_id,
137
+ previousHash: row.previous_hash ?? undefined,
138
+ facts: payload.facts,
139
+ links: payload.links,
140
+ deleteFacts: payload.deleteFacts,
141
+ deleteLinks: payload.deleteLinks
142
+ };
143
+ }
144
+ }
145
+ export {
146
+ BetterSqliteKernelBackend
147
+ };
package/dist/cli/index.js CHANGED
@@ -12,13 +12,13 @@ import {
12
12
  updateProfile,
13
13
  writeAgentScaffold,
14
14
  writeIdeScaffold
15
- } from "../index-q31hfjja.js";
15
+ } from "../index-jgda3xyv.js";
16
16
  import {
17
17
  OntologyRegistry,
18
18
  builtinOntologies,
19
19
  validateStore
20
20
  } from "../index-w7ng765c.js";
21
- import"../index-0q7wbasy.js";
21
+ import"../index-a2a394zz.js";
22
22
  import {
23
23
  VectorStore,
24
24
  buildRAGContext,
@@ -28,7 +28,7 @@ import {
28
28
  init_embeddings,
29
29
  init_model,
30
30
  init_store
31
- } from "../index-2917tjd8.js";
31
+ } from "../index-hr9qvv77.js";
32
32
  import {
33
33
  buildRefIndex,
34
34
  createResolverContext,
@@ -45,13 +45,12 @@ import {
45
45
  parseSimple
46
46
  } from "../index-n9f2qyh5.js";
47
47
  import {
48
- SqliteKernelBackend,
49
48
  TrellisKernel
50
- } from "../index-0zk3fx2s.js";
51
- import"../index-yhwjgfvj.js";
49
+ } from "../index-4wxa8xz4.js";
52
50
  import {
53
51
  QueryEngine
54
52
  } from "../index-yp88he8n.js";
53
+ import"../index-yhwjgfvj.js";
55
54
  import {
56
55
  __commonJS,
57
56
  __esm,
@@ -7336,9 +7335,10 @@ Server stopped.`));
7336
7335
  process.exit(1);
7337
7336
  }
7338
7337
  });
7339
- function bootKernel(rootPath) {
7338
+ async function bootKernel(rootPath) {
7340
7339
  const dbPath = join6(rootPath, ".trellis", "kernel.db");
7341
- const backend = new SqliteKernelBackend(dbPath);
7340
+ const { createKernelBackend } = await import("../core/persist/factory.js");
7341
+ const backend = await createKernelBackend(dbPath);
7342
7342
  const kernel = new TrellisKernel({
7343
7343
  backend,
7344
7344
  agentId: `agent:${process.env.USER ?? "unknown"}`
@@ -7350,7 +7350,7 @@ var entityCmd = program2.command("entity").description("Manage graph entities (g
7350
7350
  entityCmd.command("create").description("Create a new entity in the graph").requiredOption("-i, --id <id>", 'Entity ID (e.g. "project:my-app")').requiredOption("-t, --type <type>", 'Entity type (e.g. "Project", "User")').option("-a, --attr <attrs...>", "Attributes as key=value pairs").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7351
7351
  const rootPath = resolve(opts.path);
7352
7352
  requireRepo(rootPath);
7353
- const kernel = bootKernel(rootPath);
7353
+ const kernel = await bootKernel(rootPath);
7354
7354
  try {
7355
7355
  const attrs = {};
7356
7356
  if (opts.attr) {
@@ -7378,10 +7378,10 @@ entityCmd.command("create").description("Create a new entity in the graph").requ
7378
7378
  kernel.close();
7379
7379
  }
7380
7380
  });
7381
- entityCmd.command("get").description("Get an entity by ID").argument("<id>", "Entity ID").option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action((id, opts) => {
7381
+ entityCmd.command("get").description("Get an entity by ID").argument("<id>", "Entity ID").option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action(async (id, opts) => {
7382
7382
  const rootPath = resolve(opts.path);
7383
7383
  requireRepo(rootPath);
7384
- const kernel = bootKernel(rootPath);
7384
+ const kernel = await bootKernel(rootPath);
7385
7385
  try {
7386
7386
  const entity = kernel.getEntity(id);
7387
7387
  if (!entity) {
@@ -7423,7 +7423,7 @@ entityCmd.command("get").description("Get an entity by ID").argument("<id>", "En
7423
7423
  entityCmd.command("update").description("Update attributes on an existing entity").argument("<id>", "Entity ID").requiredOption("-a, --attr <attrs...>", "Attributes as key=value pairs").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
7424
7424
  const rootPath = resolve(opts.path);
7425
7425
  requireRepo(rootPath);
7426
- const kernel = bootKernel(rootPath);
7426
+ const kernel = await bootKernel(rootPath);
7427
7427
  try {
7428
7428
  const updates = {};
7429
7429
  for (const pair of opts.attr) {
@@ -7452,7 +7452,7 @@ entityCmd.command("update").description("Update attributes on an existing entity
7452
7452
  entityCmd.command("delete").description("Delete an entity and all its facts/links").argument("<id>", "Entity ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
7453
7453
  const rootPath = resolve(opts.path);
7454
7454
  requireRepo(rootPath);
7455
- const kernel = bootKernel(rootPath);
7455
+ const kernel = await bootKernel(rootPath);
7456
7456
  try {
7457
7457
  const entity = kernel.getEntity(id);
7458
7458
  if (!entity) {
@@ -7465,10 +7465,10 @@ entityCmd.command("delete").description("Delete an entity and all its facts/link
7465
7465
  kernel.close();
7466
7466
  }
7467
7467
  });
7468
- entityCmd.command("list").description("List entities, optionally filtered by type").option("-t, --type <type>", "Filter by entity type").option("-f, --filter <filters...>", "Attribute filters as key=value").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
7468
+ entityCmd.command("list").description("List entities, optionally filtered by type").option("-t, --type <type>", "Filter by entity type").option("-f, --filter <filters...>", "Attribute filters as key=value").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7469
7469
  const rootPath = resolve(opts.path);
7470
7470
  requireRepo(rootPath);
7471
- const kernel = bootKernel(rootPath);
7471
+ const kernel = await bootKernel(rootPath);
7472
7472
  try {
7473
7473
  let filters;
7474
7474
  if (opts.filter) {
@@ -7521,7 +7521,7 @@ var factCmd = program2.command("fact").description("Add or remove individual fac
7521
7521
  factCmd.command("add").description("Add a fact to an entity").argument("<entity>", "Entity ID").argument("<attribute>", "Attribute name").argument("<value>", "Value").option("-p, --path <path>", "Repository path", ".").action(async (entity, attribute, value, opts) => {
7522
7522
  const rootPath = resolve(opts.path);
7523
7523
  requireRepo(rootPath);
7524
- const kernel = bootKernel(rootPath);
7524
+ const kernel = await bootKernel(rootPath);
7525
7525
  try {
7526
7526
  let val = value;
7527
7527
  if (val === "true")
@@ -7539,7 +7539,7 @@ factCmd.command("add").description("Add a fact to an entity").argument("<entity>
7539
7539
  factCmd.command("remove").description("Remove a fact from an entity").argument("<entity>", "Entity ID").argument("<attribute>", "Attribute name").argument("<value>", "Value to remove").option("-p, --path <path>", "Repository path", ".").action(async (entity, attribute, value, opts) => {
7540
7540
  const rootPath = resolve(opts.path);
7541
7541
  requireRepo(rootPath);
7542
- const kernel = bootKernel(rootPath);
7542
+ const kernel = await bootKernel(rootPath);
7543
7543
  try {
7544
7544
  let val = value;
7545
7545
  if (val === "true")
@@ -7554,10 +7554,10 @@ factCmd.command("remove").description("Remove a fact from an entity").argument("
7554
7554
  kernel.close();
7555
7555
  }
7556
7556
  });
7557
- factCmd.command("query").description("Query facts by entity or attribute").option("-e, --entity <id>", "Filter by entity ID").option("-a, --attribute <attr>", "Filter by attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
7557
+ factCmd.command("query").description("Query facts by entity or attribute").option("-e, --entity <id>", "Filter by entity ID").option("-a, --attribute <attr>", "Filter by attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7558
7558
  const rootPath = resolve(opts.path);
7559
7559
  requireRepo(rootPath);
7560
- const kernel = bootKernel(rootPath);
7560
+ const kernel = await bootKernel(rootPath);
7561
7561
  try {
7562
7562
  const store = kernel.getStore();
7563
7563
  let facts;
@@ -7592,7 +7592,7 @@ var linkCmd = program2.command("link").description("Add or remove links between
7592
7592
  linkCmd.command("add").description("Add a link between two entities").argument("<source>", "Source entity ID").argument("<attribute>", "Relationship attribute").argument("<target>", "Target entity ID").option("-p, --path <path>", "Repository path", ".").action(async (source, attribute, target, opts) => {
7593
7593
  const rootPath = resolve(opts.path);
7594
7594
  requireRepo(rootPath);
7595
- const kernel = bootKernel(rootPath);
7595
+ const kernel = await bootKernel(rootPath);
7596
7596
  try {
7597
7597
  await kernel.addLink(source, attribute, target);
7598
7598
  console.log(source_default.green(`\u2713 Link: ${source_default.bold(source)} \u2014[${attribute}]\u2192 ${source_default.bold(target)}`));
@@ -7603,7 +7603,7 @@ linkCmd.command("add").description("Add a link between two entities").argument("
7603
7603
  linkCmd.command("remove").description("Remove a link between two entities").argument("<source>", "Source entity ID").argument("<attribute>", "Relationship attribute").argument("<target>", "Target entity ID").option("-p, --path <path>", "Repository path", ".").action(async (source, attribute, target, opts) => {
7604
7604
  const rootPath = resolve(opts.path);
7605
7605
  requireRepo(rootPath);
7606
- const kernel = bootKernel(rootPath);
7606
+ const kernel = await bootKernel(rootPath);
7607
7607
  try {
7608
7608
  await kernel.removeLink(source, attribute, target);
7609
7609
  console.log(source_default.green(`\u2713 Removed: ${source_default.bold(source)} \u2014[${attribute}]\u2192 ${source_default.bold(target)}`));
@@ -7611,10 +7611,10 @@ linkCmd.command("remove").description("Remove a link between two entities").argu
7611
7611
  kernel.close();
7612
7612
  }
7613
7613
  });
7614
- linkCmd.command("query").description("Query links for an entity").option("-e, --entity <id>", "Entity ID").option("-a, --attribute <attr>", "Relationship attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
7614
+ linkCmd.command("query").description("Query links for an entity").option("-e, --entity <id>", "Entity ID").option("-a, --attribute <attr>", "Relationship attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7615
7615
  const rootPath = resolve(opts.path);
7616
7616
  requireRepo(rootPath);
7617
- const kernel = bootKernel(rootPath);
7617
+ const kernel = await bootKernel(rootPath);
7618
7618
  try {
7619
7619
  const store = kernel.getStore();
7620
7620
  let links;
@@ -7647,10 +7647,10 @@ linkCmd.command("query").description("Query links for an entity").option("-e, --
7647
7647
  kernel.close();
7648
7648
  }
7649
7649
  });
7650
- program2.command("query").description("Execute an EQL-S query against the graph").argument("<query>", 'EQL-S query string (or "find ?e where attr = value" shorthand)').option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action((queryStr, opts) => {
7650
+ program2.command("query").description("Execute an EQL-S query against the graph").argument("<query>", 'EQL-S query string (or "find ?e where attr = value" shorthand)').option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action(async (queryStr, opts) => {
7651
7651
  const rootPath = resolve(opts.path);
7652
7652
  requireRepo(rootPath);
7653
- const kernel = bootKernel(rootPath);
7653
+ const kernel = await bootKernel(rootPath);
7654
7654
  try {
7655
7655
  const store = kernel.getStore();
7656
7656
  const engine = new QueryEngine(store);
@@ -7690,7 +7690,7 @@ ${result.count} result(s) in ${result.executionTime.toFixed(1)}ms`));
7690
7690
  program2.command("repl").description("Interactive EQL-S query shell").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7691
7691
  const rootPath = resolve(opts.path);
7692
7692
  requireRepo(rootPath);
7693
- const kernel = bootKernel(rootPath);
7693
+ const kernel = await bootKernel(rootPath);
7694
7694
  const store = kernel.getStore();
7695
7695
  const engine = new QueryEngine(store);
7696
7696
  console.log(source_default.cyan.bold("Trellis EQL-S REPL"));
@@ -7866,10 +7866,10 @@ Relations:`));
7866
7866
  console.log(source_default.dim("Available ontologies: " + registry.list().map((s) => s.id).join(", ")));
7867
7867
  console.log(source_default.dim("Available types: " + registry.listEntityTypes().join(", ")));
7868
7868
  });
7869
- ontologyCmd.command("validate").description("Validate all entities in the graph against registered ontologies").option("-p, --path <path>", "Repository path", ".").option("--strict", "Treat unknown types as errors").action((opts) => {
7869
+ ontologyCmd.command("validate").description("Validate all entities in the graph against registered ontologies").option("-p, --path <path>", "Repository path", ".").option("--strict", "Treat unknown types as errors").action(async (opts) => {
7870
7870
  const rootPath = resolve(opts.path);
7871
7871
  requireRepo(rootPath);
7872
- const kernel = bootKernel(rootPath);
7872
+ const kernel = await bootKernel(rootPath);
7873
7873
  try {
7874
7874
  const registry = new OntologyRegistry;
7875
7875
  for (const o of builtinOntologies)
@@ -8021,8 +8021,8 @@ db.command("init").description("Initialize a new Trellis DB in the current direc
8021
8021
  });
8022
8022
  db.command("serve").description("Start the Trellis DB HTTP + WebSocket server").option("-p, --port <port>", "Override port from config").option("--config-dir <dir>", "Directory containing .trellis-db.json", ".").action(async (opts) => {
8023
8023
  const { readConfig } = await import("../config-8hczw0rs.js");
8024
- const { TenantPool } = await import("../tenancy-7d1g4ayp.js");
8025
- const { startServer } = await import("../server-mrctdwzr.js");
8024
+ const { TenantPool } = await import("../tenancy-pjm32b4v.js");
8025
+ const { startServer } = await import("../server-szdjx0nt.js");
8026
8026
  const config = readConfig(opts.configDir);
8027
8027
  if (!config) {
8028
8028
  console.error(source_default.red("No .trellis-db.json found. Run `trellis db init` first."));
@@ -8034,7 +8034,8 @@ db.command("serve").description("Start the Trellis DB HTTP + WebSocket server").
8034
8034
  }
8035
8035
  const port = opts.port ? parseInt(opts.port) : config.port ?? 3000;
8036
8036
  const pool = new TenantPool(config.dbPath);
8037
- const server = startServer({ port, config, pool });
8037
+ const { startServerCrossRuntime } = await import("../server-szdjx0nt.js");
8038
+ const server = await startServerCrossRuntime({ port, config, pool });
8038
8039
  console.log(source_default.green(`\u2713 Trellis DB running`));
8039
8040
  console.log(source_default.dim(` URL: http://localhost:${port}`));
8040
8041
  console.log(source_default.dim(` Docs: GET http://localhost:${port}/health`));
@@ -8043,7 +8044,7 @@ db.command("serve").description("Start the Trellis DB HTTP + WebSocket server").
8043
8044
  }
8044
8045
  });
8045
8046
  db.command("create <type> [json]").description("Create an entity (json can be a JSON string or @file.json)").option("--config-dir <dir>", "Config directory", ".").option("--tenant <id>", "Tenant ID").action(async (type, jsonArg, opts) => {
8046
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8047
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8047
8048
  const db2 = TrellisDb.fromConfig(opts.configDir);
8048
8049
  let attributes = {};
8049
8050
  if (jsonArg) {
@@ -8056,7 +8057,7 @@ db.command("create <type> [json]").description("Create an entity (json can be a
8056
8057
  db2.close();
8057
8058
  });
8058
8059
  db.command("read <id>").description("Read an entity by ID").option("--config-dir <dir>", "Config directory", ".").action(async (id, opts) => {
8059
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8060
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8060
8061
  const db2 = TrellisDb.fromConfig(opts.configDir);
8061
8062
  const entity = await db2.read(id);
8062
8063
  if (!entity) {
@@ -8067,7 +8068,7 @@ db.command("read <id>").description("Read an entity by ID").option("--config-dir
8067
8068
  db2.close();
8068
8069
  });
8069
8070
  db.command("update <id> <json>").description("Update entity attributes (JSON string or @file.json)").option("--config-dir <dir>", "Config directory", ".").action(async (id, jsonArg, opts) => {
8070
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8071
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8071
8072
  const db2 = TrellisDb.fromConfig(opts.configDir);
8072
8073
  const { readFileSync: readFileSync4 } = await import("fs");
8073
8074
  const raw = jsonArg.startsWith("@") ? readFileSync4(jsonArg.slice(1), "utf8") : jsonArg;
@@ -8076,14 +8077,14 @@ db.command("update <id> <json>").description("Update entity attributes (JSON str
8076
8077
  db2.close();
8077
8078
  });
8078
8079
  db.command("delete <id>").description("Delete an entity by ID").option("--config-dir <dir>", "Config directory", ".").action(async (id, opts) => {
8079
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8080
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8080
8081
  const db2 = TrellisDb.fromConfig(opts.configDir);
8081
8082
  await db2.delete(id);
8082
8083
  console.log(source_default.green(`\u2713 Deleted: ${source_default.bold(id)}`));
8083
8084
  db2.close();
8084
8085
  });
8085
8086
  db.command("list").description("List entities").option("--type <type>", "Filter by entity type").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").option("--config-dir <dir>", "Config directory", ".").action(async (opts) => {
8086
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8087
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8087
8088
  const db2 = TrellisDb.fromConfig(opts.configDir);
8088
8089
  const result = await db2.list(opts.type, {
8089
8090
  limit: parseInt(opts.limit),
@@ -8093,7 +8094,7 @@ db.command("list").description("List entities").option("--type <type>", "Filter
8093
8094
  db2.close();
8094
8095
  });
8095
8096
  db.command("query <eql>").description("Run an EQL-S query").option("--config-dir <dir>", "Config directory", ".").action(async (eql, opts) => {
8096
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8097
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8097
8098
  const db2 = TrellisDb.fromConfig(opts.configDir);
8098
8099
  const result = await db2.query(eql);
8099
8100
  console.log(JSON.stringify(result.bindings, null, 2));
@@ -8103,7 +8104,7 @@ db.command("query <eql>").description("Run an EQL-S query").option("--config-dir
8103
8104
  db.command("upload <file>").description("Upload a file to the blob store").option("--config-dir <dir>", "Config directory", ".").option("--type <mime>", "MIME type (auto-detected if omitted)").action(async (filePath, opts) => {
8104
8105
  const { readFileSync: readFileSync4 } = await import("fs");
8105
8106
  const { extname } = await import("path");
8106
- const { TrellisDb } = await import("../sdk-snn5gad3.js");
8107
+ const { TrellisDb } = await import("../sdk-bepky0xs.js");
8107
8108
  const mimeMap = {
8108
8109
  ".png": "image/png",
8109
8110
  ".jpg": "image/jpeg",
@@ -8124,7 +8125,7 @@ db.command("upload <file>").description("Upload a file to the blob store").optio
8124
8125
  });
8125
8126
  db.command("import <file>").description("Import data from CSV, JSON, NDJSON, or Parquet").requiredOption("--type <type>", "Entity type to assign to each record").option("--id-field <field>", "Field to use as entity ID").option("--limit <n>", "Max rows to import").option("--config-dir <dir>", "Config directory", ".").option("--tenant <id>", "Tenant ID").action(async (filePath, opts) => {
8126
8127
  const { readConfig } = await import("../config-8hczw0rs.js");
8127
- const { TenantPool } = await import("../tenancy-7d1g4ayp.js");
8128
+ const { TenantPool } = await import("../tenancy-pjm32b4v.js");
8128
8129
  const { importFile } = await import("../import-s2b8e0ft.js");
8129
8130
  const config = readConfig(opts.configDir);
8130
8131
  if (!config?.dbPath) {
@@ -8132,7 +8133,7 @@ db.command("import <file>").description("Import data from CSV, JSON, NDJSON, or
8132
8133
  process.exit(1);
8133
8134
  }
8134
8135
  const pool = new TenantPool(config.dbPath);
8135
- const kernel = pool.get(opts.tenant ?? null);
8136
+ const kernel = await pool.preload(opts.tenant ?? null);
8136
8137
  const result = await importFile(kernel, filePath, {
8137
8138
  type: opts.type,
8138
8139
  idField: opts.idField,
@@ -8482,12 +8483,16 @@ vmProgram.command("code [name]").description("Create Sprite, deploy Trellis, ope
8482
8483
  const { loadVmConfig, trackSprite, getActiveSprite } = await import("../vm-config-6xhsj6b3.js");
8483
8484
  const { deploy } = await import("../deploy-999q207z.js");
8484
8485
  const openUrl = (url) => {
8485
- if (process.platform === "darwin") {
8486
- Bun.spawn(["open", url], { stdio: "ignore" });
8487
- } else if (process.platform === "linux") {
8488
- Bun.spawn(["xdg-open", url], { stdio: "ignore" });
8489
- } else {
8486
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
8487
+ if (!cmd) {
8490
8488
  console.log(url);
8489
+ return;
8490
+ }
8491
+ if (typeof globalThis.Bun !== "undefined") {
8492
+ globalThis.Bun.spawn([cmd, url], { stdio: "ignore" });
8493
+ } else {
8494
+ const { spawn } = __require("child_process");
8495
+ spawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
8491
8496
  }
8492
8497
  };
8493
8498
  try {
@@ -8717,6 +8722,72 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8717
8722
  process.exit(code ?? 0);
8718
8723
  });
8719
8724
  });
8725
+ program2.command("studio").alias("web").description("Launch Trellis Studio in your browser, bridged to this repo").option("-p, --path <path>", "Repository path", ".").option("--port <port>", "Studio HTTP port (defaults to turtlecode default)").option("--new", "Open the new-project prompt instead of the current directory").option("--no-open", "Do not auto-open the browser").option("--no-init", "Skip initialization even if not a Trellis workspace").option("--quiet-backend", "Suppress backend stdout/stderr").allowUnknownOption(true).action(async (opts, command) => {
8726
+ const { resolve: resolve2 } = await import("path");
8727
+ const { existsSync: existsSync6, readFileSync: readFileSync4 } = await import("fs");
8728
+ const { spawn } = await import("child_process");
8729
+ const { createRequire } = await import("module");
8730
+ const path = await import("path");
8731
+ const rootPath = resolve2(opts.path);
8732
+ if (!opts.noInit) {
8733
+ if (!TrellisVcsEngine.isRepo(rootPath)) {
8734
+ console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
8735
+ await runInit(rootPath, { interactive: false });
8736
+ console.log(source_default.green("\u2713 Initialized Trellis repository"));
8737
+ }
8738
+ }
8739
+ const requireFn = createRequire(import.meta.url);
8740
+ let turtlecodeBin = null;
8741
+ try {
8742
+ const pkgPath = requireFn.resolve("turtlecode/package.json");
8743
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
8744
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.turtlecode;
8745
+ if (binRel) {
8746
+ const candidate = path.join(path.dirname(pkgPath), binRel);
8747
+ if (existsSync6(candidate))
8748
+ turtlecodeBin = candidate;
8749
+ }
8750
+ } catch {}
8751
+ const passthrough = [];
8752
+ if (opts.port)
8753
+ passthrough.push("--port", String(opts.port));
8754
+ if (opts.new)
8755
+ passthrough.push("--new");
8756
+ if (!opts.open)
8757
+ passthrough.push("--no-open");
8758
+ if (opts.quietBackend)
8759
+ passthrough.push("--quiet-backend");
8760
+ const extra = command.args ?? [];
8761
+ passthrough.push(...extra);
8762
+ const spawnArgs = turtlecodeBin ? [turtlecodeBin, ...passthrough] : ["turtlecode", ...passthrough];
8763
+ const spawnCmd = turtlecodeBin ? process.execPath : "npx";
8764
+ console.log(source_default.green("\u26A1 Launching Trellis Studio..."));
8765
+ console.log(source_default.dim(` Project: ${rootPath}`));
8766
+ console.log("");
8767
+ const child = spawn(spawnCmd, spawnArgs, {
8768
+ stdio: "inherit",
8769
+ cwd: rootPath,
8770
+ env: process.env
8771
+ });
8772
+ child.on("error", (err) => {
8773
+ if (err.code === "ENOENT" && !turtlecodeBin) {
8774
+ console.error(source_default.red("\nCould not find `turtlecode`. Reinstall trellis, or install turtlecode globally: `npm i -g turtlecode`\n"));
8775
+ } else {
8776
+ console.error(source_default.red(`
8777
+ Failed to launch Studio: ${err.message}`));
8778
+ }
8779
+ process.exit(1);
8780
+ });
8781
+ const shutdown = () => {
8782
+ if (!child.killed)
8783
+ child.kill();
8784
+ };
8785
+ process.on("SIGINT", shutdown);
8786
+ process.on("SIGTERM", shutdown);
8787
+ child.on("exit", (code) => {
8788
+ process.exit(code ?? 0);
8789
+ });
8790
+ });
8720
8791
  async function waitForMcpReady(url, timeoutMs) {
8721
8792
  const { get } = await import("http");
8722
8793
  const start = Date.now();
@@ -3,7 +3,7 @@ import"../index-k5b0xskw.js";
3
3
  import {
4
4
  FetchError,
5
5
  TrellisDb
6
- } from "../index-7e27kvvj.js";
6
+ } from "../index-wncptktd.js";
7
7
  import {
8
8
  CONFIG_FILE,
9
9
  configPath,
@@ -13,10 +13,11 @@ import {
13
13
  updateConfig,
14
14
  writeConfig
15
15
  } from "../index-xzym9w0m.js";
16
- import"../index-y3d71wzd.js";
17
- import"../index-0zk3fx2s.js";
18
- import"../index-yhwjgfvj.js";
16
+ import"../index-7pjz3tsy.js";
17
+ import"../index-4wxa8xz4.js";
19
18
  import"../index-yp88he8n.js";
19
+ import"../index-yhwjgfvj.js";
20
+ import"../index-h7zxhhhh.js";
20
21
  import"../index-a76rekgs.js";
21
22
  export {
22
23
  writeConfig,
@@ -23,7 +23,7 @@
23
23
  *
24
24
  * @module trellis/cms
25
25
  */
26
- import type { CmsClientOptions, Collection, Entry, EntrySubscribeOptions, EntrySubscriber, GetOptions, ListOptions, ListSubscribeOptions, ListSubscriber, Unsubscribe } from './types.js';
26
+ import type { CmsClientOptions, Collection, Entry, EntrySubscribeOptions, EntrySubscriber, GetOptions, ListOptions, ListSubscribeOptions, ListSubscriber, Unsubscribe, FieldDefinition } from './types.js';
27
27
  import { type RawEntity, type RawFact } from './internal.js';
28
28
  export declare class CmsClient {
29
29
  private readonly url;
@@ -71,6 +71,7 @@ export declare class CollectionRef<T extends Record<string, unknown> = Record<st
71
71
  * and one HTTP request per cycle.
72
72
  */
73
73
  subscribe(callback: ListSubscriber<T>, opts?: ListSubscribeOptions): Unsubscribe;
74
+ schema(): Promise<FieldDefinition[]>;
74
75
  }
75
76
  export declare class EntryRef<T extends Record<string, unknown> = Record<string, unknown>> {
76
77
  private readonly client;