@shardworks/nexus-core 0.1.20 → 0.1.21
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/clockworks.d.ts +0 -20
- package/dist/clockworks.d.ts.map +1 -1
- package/dist/clockworks.js +104 -19
- package/dist/clockworks.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +132 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +321 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrate.d.ts +63 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +141 -0
- package/dist/migrate.js.map +1 -0
- package/dist/session.d.ts +214 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +349 -0
- package/dist/session.js.map +1 -0
- package/dist/worktree.d.ts +69 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +116 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +1 -1
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest — assembles an anima's identity for a session.
|
|
3
|
+
*
|
|
4
|
+
* Reads the anima's composition from the Ledger (roles, curricula, temperament),
|
|
5
|
+
* resolves tools by role gating and precondition checks, reads all prompt
|
|
6
|
+
* ingredients from disk, and assembles the composed system prompt.
|
|
7
|
+
*
|
|
8
|
+
* The manifest is the anima's *identity*: who are you, what can you do.
|
|
9
|
+
* The *user prompt* (commission spec, brief, conversation topic) is NOT part
|
|
10
|
+
* of the manifest — it comes from the caller.
|
|
11
|
+
*
|
|
12
|
+
* Absorbed from the former `engine-manifest` package. MCP config generation
|
|
13
|
+
* is NOT here — that's a transport detail owned by session providers.
|
|
14
|
+
*/
|
|
15
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
16
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
17
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
18
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return path;
|
|
22
|
+
};
|
|
23
|
+
import fs from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import Database from 'better-sqlite3';
|
|
26
|
+
import { ledgerPath } from "./nexus-home.js";
|
|
27
|
+
import { readGuildConfig } from "./guild-config.js";
|
|
28
|
+
import { readPreconditions, checkPreconditions } from "./preconditions.js";
|
|
29
|
+
import { resolveToolFromExport } from "./tool.js";
|
|
30
|
+
/** Extract the npm package name from a package specifier that may include a subpath. */
|
|
31
|
+
function basePackageName(pkg) {
|
|
32
|
+
// Scoped: @scope/name/subpath → @scope/name
|
|
33
|
+
if (pkg.startsWith('@')) {
|
|
34
|
+
const parts = pkg.split('/');
|
|
35
|
+
return parts.slice(0, 2).join('/');
|
|
36
|
+
}
|
|
37
|
+
// Unscoped: name/subpath → name
|
|
38
|
+
return pkg.split('/')[0];
|
|
39
|
+
}
|
|
40
|
+
// ── Core Functions ─────────────────────────────────────────────────────
|
|
41
|
+
/**
|
|
42
|
+
* Read an anima's full record from the Ledger, including roles and composition.
|
|
43
|
+
*/
|
|
44
|
+
export function readAnima(home, animaName) {
|
|
45
|
+
const db = new Database(ledgerPath(home));
|
|
46
|
+
db.pragma('foreign_keys = ON');
|
|
47
|
+
try {
|
|
48
|
+
// Get anima
|
|
49
|
+
const anima = db.prepare(`SELECT id, name, status FROM animas WHERE name = ?`).get(animaName);
|
|
50
|
+
if (!anima) {
|
|
51
|
+
throw new Error(`Anima "${animaName}" not found in the Ledger.`);
|
|
52
|
+
}
|
|
53
|
+
// Get roles
|
|
54
|
+
const roleRows = db.prepare(`SELECT role FROM roster WHERE anima_id = ? ORDER BY role`).all(anima.id);
|
|
55
|
+
const roles = roleRows.map(r => r.role);
|
|
56
|
+
// Get composition — now includes name/version metadata
|
|
57
|
+
const composition = db.prepare(`SELECT curriculum_name, curriculum_version, curriculum_snapshot,
|
|
58
|
+
temperament_name, temperament_version, temperament_snapshot
|
|
59
|
+
FROM anima_compositions WHERE anima_id = ?`).get(anima.id);
|
|
60
|
+
return {
|
|
61
|
+
id: anima.id,
|
|
62
|
+
name: anima.name,
|
|
63
|
+
status: anima.status,
|
|
64
|
+
roles,
|
|
65
|
+
curriculumName: composition?.curriculum_name ?? '',
|
|
66
|
+
curriculumVersion: composition?.curriculum_version ?? '',
|
|
67
|
+
curriculumSnapshot: composition?.curriculum_snapshot ?? '',
|
|
68
|
+
temperamentName: composition?.temperament_name ?? '',
|
|
69
|
+
temperamentVersion: composition?.temperament_version ?? '',
|
|
70
|
+
temperamentSnapshot: composition?.temperament_snapshot ?? '',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
db.close();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the set of tools an anima has access to, based on role
|
|
79
|
+
* definitions and precondition checks.
|
|
80
|
+
*
|
|
81
|
+
* 1. Start with baseTools (available to all animas)
|
|
82
|
+
* 2. For each anima role, look up the role in guild.json.roles
|
|
83
|
+
* - If defined: union in that role's tools
|
|
84
|
+
* - If undefined: warn and skip (no tools, no instructions from that role)
|
|
85
|
+
* 3. Deduplicate tool names
|
|
86
|
+
* 4. Resolve each tool from guild.json.tools catalog
|
|
87
|
+
* 5. Run precondition checks — split into available and unavailable
|
|
88
|
+
*
|
|
89
|
+
* Returns available tools, unavailable tools, and any warnings.
|
|
90
|
+
*/
|
|
91
|
+
export async function resolveTools(home, config, animaRoles) {
|
|
92
|
+
const warnings = [];
|
|
93
|
+
// Collect tool names: start with base, union in role-specific
|
|
94
|
+
const toolNames = new Set(config.baseTools ?? []);
|
|
95
|
+
for (const role of animaRoles) {
|
|
96
|
+
const roleDef = config.roles[role];
|
|
97
|
+
if (!roleDef) {
|
|
98
|
+
warnings.push(`Role "${role}" is assigned to this anima but not defined in guild.json. ` +
|
|
99
|
+
`No tools or instructions from this role will be available.`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
for (const toolName of roleDef.tools) {
|
|
103
|
+
toolNames.add(toolName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Resolve each tool from the catalog
|
|
107
|
+
const available = [];
|
|
108
|
+
const unavailable = [];
|
|
109
|
+
for (const name of toolNames) {
|
|
110
|
+
const entry = config.tools[name];
|
|
111
|
+
if (!entry) {
|
|
112
|
+
warnings.push(`Tool "${name}" is referenced by a role or baseTools but not found in guild.json.tools. Skipping.`);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Resolve on-disk path
|
|
116
|
+
const toolPath = path.join(home, 'tools', name);
|
|
117
|
+
const descriptorPath = path.join(toolPath, 'nexus-tool.json');
|
|
118
|
+
// Check preconditions before including in the available set
|
|
119
|
+
const preconditions = readPreconditions(descriptorPath);
|
|
120
|
+
if (preconditions.length > 0) {
|
|
121
|
+
const results = checkPreconditions(preconditions);
|
|
122
|
+
const failures = results.filter(r => !r.passed).map(r => r.message);
|
|
123
|
+
if (failures.length > 0) {
|
|
124
|
+
unavailable.push({ name, reasons: failures });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Read instructions from multiple sources, in priority order:
|
|
129
|
+
// 1. Descriptor file on disk (nexus-tool.json → instructions field)
|
|
130
|
+
// 2. Tool definition (import module → instructions or instructionsFile)
|
|
131
|
+
let instructions = null;
|
|
132
|
+
// Source 1: descriptor file on disk
|
|
133
|
+
if (fs.existsSync(descriptorPath)) {
|
|
134
|
+
try {
|
|
135
|
+
const descriptor = JSON.parse(fs.readFileSync(descriptorPath, 'utf-8'));
|
|
136
|
+
if (descriptor.instructions) {
|
|
137
|
+
const instructionsPath = path.join(toolPath, descriptor.instructions);
|
|
138
|
+
if (fs.existsSync(instructionsPath)) {
|
|
139
|
+
instructions = fs.readFileSync(instructionsPath, 'utf-8');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// If descriptor is unreadable, skip instructions from descriptor
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Source 2: tool definition (for collection packages or tools with
|
|
148
|
+
// inline instructions / instructionsFile in the tool() definition)
|
|
149
|
+
if (!instructions && entry.package) {
|
|
150
|
+
try {
|
|
151
|
+
const mod = await import(__rewriteRelativeImportExtension(entry.package));
|
|
152
|
+
const toolDef = resolveToolFromExport(mod.default, name);
|
|
153
|
+
if (toolDef) {
|
|
154
|
+
if (toolDef.instructions) {
|
|
155
|
+
// Inline instructions text
|
|
156
|
+
instructions = toolDef.instructions;
|
|
157
|
+
}
|
|
158
|
+
else if (toolDef.instructionsFile) {
|
|
159
|
+
// File path relative to the package root in node_modules.
|
|
160
|
+
const instrPath = path.join(home, 'node_modules', basePackageName(entry.package), toolDef.instructionsFile);
|
|
161
|
+
if (fs.existsSync(instrPath)) {
|
|
162
|
+
instructions = fs.readFileSync(instrPath, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// If import fails, skip — instructions are optional
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
available.push({
|
|
172
|
+
name,
|
|
173
|
+
path: toolPath,
|
|
174
|
+
instructions,
|
|
175
|
+
package: entry.package ?? null,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return { available, unavailable, warnings };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Read codex documents from the guildhall — guild-wide policy for all animas.
|
|
182
|
+
*
|
|
183
|
+
* Reads all .md files in the codex/ directory (non-recursive top level).
|
|
184
|
+
*/
|
|
185
|
+
export function readCodex(home) {
|
|
186
|
+
const codexDir = path.join(home, 'codex');
|
|
187
|
+
if (!fs.existsSync(codexDir))
|
|
188
|
+
return '';
|
|
189
|
+
const sections = [];
|
|
190
|
+
// Read top-level .md files (included for all animas)
|
|
191
|
+
for (const entry of fs.readdirSync(codexDir, { withFileTypes: true })) {
|
|
192
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
193
|
+
const content = fs.readFileSync(path.join(codexDir, entry.name), 'utf-8').trim();
|
|
194
|
+
if (content)
|
|
195
|
+
sections.push(content);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return sections.join('\n\n---\n\n');
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Read role-specific instructions for an anima's roles.
|
|
202
|
+
*
|
|
203
|
+
* For each role the anima holds, reads the instructions file pointed to by
|
|
204
|
+
* the role definition in guild.json. Skips undefined roles (warning already
|
|
205
|
+
* emitted by resolveTools). Skips roles without instructions.
|
|
206
|
+
*/
|
|
207
|
+
export function readRoleInstructions(home, config, animaRoles) {
|
|
208
|
+
const sections = [];
|
|
209
|
+
for (const role of animaRoles) {
|
|
210
|
+
const roleDef = config.roles[role];
|
|
211
|
+
if (!roleDef || !roleDef.instructions)
|
|
212
|
+
continue;
|
|
213
|
+
const instructionsPath = path.join(home, roleDef.instructions);
|
|
214
|
+
if (!fs.existsSync(instructionsPath))
|
|
215
|
+
continue;
|
|
216
|
+
try {
|
|
217
|
+
const content = fs.readFileSync(instructionsPath, 'utf-8').trim();
|
|
218
|
+
if (content) {
|
|
219
|
+
sections.push(content);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// If unreadable, skip
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return sections.join('\n\n---\n\n');
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Assemble the composed system prompt for an anima session.
|
|
230
|
+
*
|
|
231
|
+
* Sections are included in order: codex → role instructions → curricula →
|
|
232
|
+
* temperament → tool instructions → unavailable tools notice.
|
|
233
|
+
* Empty sections are omitted.
|
|
234
|
+
*/
|
|
235
|
+
export function assembleSystemPrompt(codex, roleInstructions, anima, tools, unavailable = []) {
|
|
236
|
+
const sections = [];
|
|
237
|
+
// Codex — guild-wide policies and procedures
|
|
238
|
+
if (codex.trim()) {
|
|
239
|
+
sections.push(`# Codex\n\n${codex}`);
|
|
240
|
+
}
|
|
241
|
+
// Role instructions — role-specific operational guidance
|
|
242
|
+
if (roleInstructions.trim()) {
|
|
243
|
+
sections.push(`# Role Instructions\n\n${roleInstructions}`);
|
|
244
|
+
}
|
|
245
|
+
// Curricula — the anima's training content
|
|
246
|
+
if (anima.curriculumSnapshot.trim()) {
|
|
247
|
+
sections.push(`# Training\n\n${anima.curriculumSnapshot}`);
|
|
248
|
+
}
|
|
249
|
+
// Temperament — the anima's personality
|
|
250
|
+
if (anima.temperamentSnapshot.trim()) {
|
|
251
|
+
sections.push(`# Temperament\n\n${anima.temperamentSnapshot}`);
|
|
252
|
+
}
|
|
253
|
+
// Tool instructions — guidance for each tool the anima has access to
|
|
254
|
+
const toolInstructions = tools
|
|
255
|
+
.filter(t => t.instructions)
|
|
256
|
+
.map(t => t.instructions);
|
|
257
|
+
if (toolInstructions.length > 0) {
|
|
258
|
+
sections.push(`# Tool Instructions\n\n${toolInstructions.join('\n\n---\n\n')}`);
|
|
259
|
+
}
|
|
260
|
+
// Unavailable tools notice — tell the anima what's broken and why
|
|
261
|
+
if (unavailable.length > 0) {
|
|
262
|
+
const notices = unavailable.map(u => {
|
|
263
|
+
const reasons = u.reasons.map(r => ` - ${r}`).join('\n');
|
|
264
|
+
return `**${u.name}** — unavailable:\n${reasons}`;
|
|
265
|
+
});
|
|
266
|
+
sections.push(`# Unavailable Tools\n\n` +
|
|
267
|
+
`The following tools are registered for your roles but are currently ` +
|
|
268
|
+
`unavailable due to unmet environment requirements. Do not attempt to use them. ` +
|
|
269
|
+
`If a patron or operator asks you to perform work that requires these tools, ` +
|
|
270
|
+
`explain what is needed to make them available.\n\n` +
|
|
271
|
+
notices.join('\n\n'));
|
|
272
|
+
}
|
|
273
|
+
return sections.join('\n\n---\n\n');
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Manifest an anima for a session.
|
|
277
|
+
*
|
|
278
|
+
* This is the main entry point. Reads the anima's composition, resolves
|
|
279
|
+
* tools by role, assembles the system prompt, and returns the full
|
|
280
|
+
* manifest with composition provenance.
|
|
281
|
+
*/
|
|
282
|
+
export async function manifest(home, animaName) {
|
|
283
|
+
const config = readGuildConfig(home);
|
|
284
|
+
const anima = readAnima(home, animaName);
|
|
285
|
+
if (anima.status !== 'active') {
|
|
286
|
+
throw new Error(`Anima "${animaName}" is not active (status: ${anima.status}). Cannot manifest.`);
|
|
287
|
+
}
|
|
288
|
+
// Resolve tools based on role definitions + precondition checks
|
|
289
|
+
const { available, unavailable, warnings } = await resolveTools(home, config, anima.roles);
|
|
290
|
+
// Read codex (guild-wide, no role filtering)
|
|
291
|
+
const codex = readCodex(home);
|
|
292
|
+
// Read role-specific instructions
|
|
293
|
+
const roleInstructions = readRoleInstructions(home, config, anima.roles);
|
|
294
|
+
// Assemble system prompt (includes role instructions and unavailability notices)
|
|
295
|
+
const systemPrompt = assembleSystemPrompt(codex, roleInstructions, anima, available, unavailable);
|
|
296
|
+
// Build composition provenance
|
|
297
|
+
const curriculum = anima.curriculumName
|
|
298
|
+
? { name: anima.curriculumName, version: anima.curriculumVersion, content: anima.curriculumSnapshot }
|
|
299
|
+
: null;
|
|
300
|
+
const temperament = anima.temperamentName
|
|
301
|
+
? { name: anima.temperamentName, version: anima.temperamentVersion, content: anima.temperamentSnapshot }
|
|
302
|
+
: null;
|
|
303
|
+
const toolInstructions = available
|
|
304
|
+
.filter(t => t.instructions)
|
|
305
|
+
.map(t => ({ toolName: t.name, instructions: t.instructions }));
|
|
306
|
+
return {
|
|
307
|
+
anima,
|
|
308
|
+
systemPrompt,
|
|
309
|
+
composition: {
|
|
310
|
+
codex,
|
|
311
|
+
roleInstructions,
|
|
312
|
+
curriculum,
|
|
313
|
+
temperament,
|
|
314
|
+
toolInstructions,
|
|
315
|
+
},
|
|
316
|
+
tools: available,
|
|
317
|
+
unavailable,
|
|
318
|
+
warnings,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ledger migration — applies pending SQL migrations to the guild's Ledger.
|
|
3
|
+
*
|
|
4
|
+
* Runs at guild bootstrap, before dispatch, and on demand after framework
|
|
5
|
+
* upgrades. Absorbed from the former `engine-ledger-migrate` package.
|
|
6
|
+
*
|
|
7
|
+
* Migrations are numbered sequentially (001-initial-schema.sql, etc.) and
|
|
8
|
+
* applied in order. The module tracks which migrations have been applied in
|
|
9
|
+
* a `_migrations` table and only runs new ones.
|
|
10
|
+
*
|
|
11
|
+
* ## Migration file naming
|
|
12
|
+
*
|
|
13
|
+
* Files must match the pattern: NNN-description.sql
|
|
14
|
+
* where NNN is a zero-padded sequence number.
|
|
15
|
+
*
|
|
16
|
+
* Examples:
|
|
17
|
+
* 001-initial-schema.sql
|
|
18
|
+
* 002-add-priority-to-commissions.sql
|
|
19
|
+
* 003-add-sessions-table.sql
|
|
20
|
+
*/
|
|
21
|
+
/** A migration file discovered on disk. */
|
|
22
|
+
export interface MigrationFile {
|
|
23
|
+
/** Sequence number (e.g. 1, 2, 3). */
|
|
24
|
+
sequence: number;
|
|
25
|
+
/** Full filename (e.g. '001-initial-schema.sql'). */
|
|
26
|
+
filename: string;
|
|
27
|
+
/** Absolute path to the file. */
|
|
28
|
+
path: string;
|
|
29
|
+
}
|
|
30
|
+
/** Provenance metadata for migrations installed via bundles. */
|
|
31
|
+
export interface MigrationProvenance {
|
|
32
|
+
/** Bundle that delivered this migration (e.g. "@shardworks/guild-starter-kit@0.1.5"). */
|
|
33
|
+
bundle: string;
|
|
34
|
+
/** Original filename in the bundle before renumbering (e.g. "001-initial-schema.sql"). */
|
|
35
|
+
originalName: string;
|
|
36
|
+
}
|
|
37
|
+
/** Result of applying migrations. */
|
|
38
|
+
export interface MigrateResult {
|
|
39
|
+
/** Migrations that were applied in this run. */
|
|
40
|
+
applied: string[];
|
|
41
|
+
/** Migrations that were already applied (skipped). */
|
|
42
|
+
skipped: string[];
|
|
43
|
+
/** Total number of migrations on disk. */
|
|
44
|
+
total: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Discover migration files in the migrations directory, sorted by sequence.
|
|
48
|
+
*/
|
|
49
|
+
export declare function discoverMigrations(migrationsDir: string): MigrationFile[];
|
|
50
|
+
/**
|
|
51
|
+
* Apply pending migrations to the Ledger.
|
|
52
|
+
*
|
|
53
|
+
* Reads migration files from the guild's nexus/migrations/ directory, compares
|
|
54
|
+
* against the _migrations tracking table, and applies any that haven't been
|
|
55
|
+
* run yet. Each migration runs in its own transaction.
|
|
56
|
+
*
|
|
57
|
+
* @param home - Absolute path to the guild root.
|
|
58
|
+
* @param provenance - Optional map of guild filename → bundle provenance,
|
|
59
|
+
* supplied by the bundle installer for migrations it copied into the guild.
|
|
60
|
+
* @returns Summary of what was applied and skipped.
|
|
61
|
+
*/
|
|
62
|
+
export declare function applyMigrations(home: string, provenance?: Record<string, MigrationProvenance>): MigrateResult;
|
|
63
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf;AAkCD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,EAAE,CAoBzE;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC/C,aAAa,CA8Df"}
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ledger migration — applies pending SQL migrations to the guild's Ledger.
|
|
3
|
+
*
|
|
4
|
+
* Runs at guild bootstrap, before dispatch, and on demand after framework
|
|
5
|
+
* upgrades. Absorbed from the former `engine-ledger-migrate` package.
|
|
6
|
+
*
|
|
7
|
+
* Migrations are numbered sequentially (001-initial-schema.sql, etc.) and
|
|
8
|
+
* applied in order. The module tracks which migrations have been applied in
|
|
9
|
+
* a `_migrations` table and only runs new ones.
|
|
10
|
+
*
|
|
11
|
+
* ## Migration file naming
|
|
12
|
+
*
|
|
13
|
+
* Files must match the pattern: NNN-description.sql
|
|
14
|
+
* where NNN is a zero-padded sequence number.
|
|
15
|
+
*
|
|
16
|
+
* Examples:
|
|
17
|
+
* 001-initial-schema.sql
|
|
18
|
+
* 002-add-priority-to-commissions.sql
|
|
19
|
+
* 003-add-sessions-table.sql
|
|
20
|
+
*/
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import Database from 'better-sqlite3';
|
|
24
|
+
import { ledgerPath } from "./nexus-home.js";
|
|
25
|
+
/** Pattern for migration filenames: NNN-description.sql */
|
|
26
|
+
const MIGRATION_PATTERN = /^(\d{3})-(.+)\.sql$/;
|
|
27
|
+
/**
|
|
28
|
+
* Ensure the _migrations tracking table exists.
|
|
29
|
+
*
|
|
30
|
+
* This table is NOT part of the regular schema migrations — it's the
|
|
31
|
+
* module's own bookkeeping, created on first use.
|
|
32
|
+
*/
|
|
33
|
+
function ensureMigrationsTable(db) {
|
|
34
|
+
db.exec(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
36
|
+
sequence INTEGER PRIMARY KEY,
|
|
37
|
+
filename TEXT NOT NULL,
|
|
38
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
39
|
+
bundle TEXT,
|
|
40
|
+
original_name TEXT
|
|
41
|
+
);
|
|
42
|
+
`);
|
|
43
|
+
// Add provenance columns if upgrading from an older schema.
|
|
44
|
+
// SQLite errors on duplicate ADD COLUMN, so check first.
|
|
45
|
+
const cols = db.pragma('table_info(_migrations)');
|
|
46
|
+
const colNames = new Set(cols.map(c => c.name));
|
|
47
|
+
if (!colNames.has('bundle')) {
|
|
48
|
+
db.exec(`ALTER TABLE _migrations ADD COLUMN bundle TEXT;`);
|
|
49
|
+
}
|
|
50
|
+
if (!colNames.has('original_name')) {
|
|
51
|
+
db.exec(`ALTER TABLE _migrations ADD COLUMN original_name TEXT;`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Discover migration files in the migrations directory, sorted by sequence.
|
|
56
|
+
*/
|
|
57
|
+
export function discoverMigrations(migrationsDir) {
|
|
58
|
+
if (!fs.existsSync(migrationsDir))
|
|
59
|
+
return [];
|
|
60
|
+
const files = fs.readdirSync(migrationsDir);
|
|
61
|
+
const migrations = [];
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
const match = file.match(MIGRATION_PATTERN);
|
|
64
|
+
if (!match)
|
|
65
|
+
continue;
|
|
66
|
+
migrations.push({
|
|
67
|
+
sequence: parseInt(match[1], 10),
|
|
68
|
+
filename: file,
|
|
69
|
+
path: path.join(migrationsDir, file),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Sort by sequence number
|
|
73
|
+
migrations.sort((a, b) => a.sequence - b.sequence);
|
|
74
|
+
return migrations;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the set of already-applied migration sequence numbers.
|
|
78
|
+
*/
|
|
79
|
+
function getAppliedSequences(db) {
|
|
80
|
+
const rows = db.prepare(`SELECT sequence FROM _migrations ORDER BY sequence`).all();
|
|
81
|
+
return new Set(rows.map(r => r.sequence));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Apply pending migrations to the Ledger.
|
|
85
|
+
*
|
|
86
|
+
* Reads migration files from the guild's nexus/migrations/ directory, compares
|
|
87
|
+
* against the _migrations tracking table, and applies any that haven't been
|
|
88
|
+
* run yet. Each migration runs in its own transaction.
|
|
89
|
+
*
|
|
90
|
+
* @param home - Absolute path to the guild root.
|
|
91
|
+
* @param provenance - Optional map of guild filename → bundle provenance,
|
|
92
|
+
* supplied by the bundle installer for migrations it copied into the guild.
|
|
93
|
+
* @returns Summary of what was applied and skipped.
|
|
94
|
+
*/
|
|
95
|
+
export function applyMigrations(home, provenance) {
|
|
96
|
+
const migrationsDir = path.join(home, 'nexus', 'migrations');
|
|
97
|
+
const dbPath = ledgerPath(home);
|
|
98
|
+
// Discover available migrations
|
|
99
|
+
const migrations = discoverMigrations(migrationsDir);
|
|
100
|
+
if (migrations.length === 0) {
|
|
101
|
+
return { applied: [], skipped: [], total: 0 };
|
|
102
|
+
}
|
|
103
|
+
const db = new Database(dbPath);
|
|
104
|
+
db.pragma('foreign_keys = ON');
|
|
105
|
+
try {
|
|
106
|
+
ensureMigrationsTable(db);
|
|
107
|
+
const applied = getAppliedSequences(db);
|
|
108
|
+
const result = {
|
|
109
|
+
applied: [],
|
|
110
|
+
skipped: [],
|
|
111
|
+
total: migrations.length,
|
|
112
|
+
};
|
|
113
|
+
for (const migration of migrations) {
|
|
114
|
+
if (applied.has(migration.sequence)) {
|
|
115
|
+
result.skipped.push(migration.filename);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
// Read and apply the migration.
|
|
119
|
+
// PRAGMAs can't run inside transactions, so extract them and run separately.
|
|
120
|
+
const sql = fs.readFileSync(migration.path, 'utf-8');
|
|
121
|
+
const pragmaPattern = /^\s*PRAGMA\s+[^;]+;\s*$/gmi;
|
|
122
|
+
const pragmas = sql.match(pragmaPattern) || [];
|
|
123
|
+
const body = sql.replace(pragmaPattern, '').trim();
|
|
124
|
+
for (const pragma of pragmas) {
|
|
125
|
+
db.exec(pragma);
|
|
126
|
+
}
|
|
127
|
+
const prov = provenance?.[migration.filename];
|
|
128
|
+
db.transaction(() => {
|
|
129
|
+
if (body)
|
|
130
|
+
db.exec(body);
|
|
131
|
+
db.prepare(`INSERT INTO _migrations (sequence, filename, bundle, original_name) VALUES (?, ?, ?, ?)`).run(migration.sequence, migration.filename, prov?.bundle ?? null, prov?.originalName ?? null);
|
|
132
|
+
})();
|
|
133
|
+
result.applied.push(migration.filename);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
db.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
|