From 109c4125d29839bc1b0755cc128ab4be93a94f45 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 21 Jul 2026 14:13:14 +0200 Subject: [PATCH] feat(agent): prepare SQLite storage package for publishing --- package-lock.json | 4 +- packages/agent/README.md | 2 +- packages/storage/sqlite-node/CHANGELOG.md | 7 ++ packages/storage/sqlite-node/README.md | 2 +- packages/storage/sqlite-node/package.json | 5 +- scripts/local-release.mjs | 1 + scripts/package-workspaces.mjs | 24 +++++ scripts/publish.mjs | 1 + scripts/release.mjs | 13 ++- scripts/sync-versions.js | 116 +++++++++------------- 10 files changed, 94 insertions(+), 81 deletions(-) create mode 100644 packages/storage/sqlite-node/CHANGELOG.md create mode 100644 scripts/package-workspaces.mjs diff --git a/package-lock.json b/package-lock.json index 5b9e3024..7243aae1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -786,7 +786,7 @@ "resolved": "packages/agent", "link": true }, - "node_modules/@earendil-works/pi-agent-sqlite-node": { + "node_modules/@earendil-works/pi-storage-sqlite-node": { "resolved": "packages/storage/sqlite-node", "link": true }, @@ -6093,7 +6093,7 @@ } }, "packages/storage/sqlite-node": { - "name": "@earendil-works/pi-agent-sqlite-node", + "name": "@earendil-works/pi-storage-sqlite-node", "version": "0.80.10", "license": "MIT", "dependencies": { diff --git a/packages/agent/README.md b/packages/agent/README.md index 00dac621..543f4c8f 100644 --- a/packages/agent/README.md +++ b/packages/agent/README.md @@ -10,7 +10,7 @@ npm install @earendil-works/pi-agent-core ### SQLite session backends -The SQLite session backend and the `node:sqlite` adapter live in a separate package, `@earendil-works/pi-agent-sqlite-node`, so the core package does not pull in runtime builtins or native SQLite dependencies by default. The backend accepts a runtime-specific SQLite factory, allowing other storage backends to ship as their own packages in the future. +The SQLite session backend and the `node:sqlite` adapter live in a separate package, `@earendil-works/pi-storage-sqlite-node`, so the core package does not pull in runtime builtins or native SQLite dependencies by default. The backend accepts a runtime-specific SQLite factory, allowing other storage backends to ship as their own packages in the future. ## Quick Start diff --git a/packages/storage/sqlite-node/CHANGELOG.md b/packages/storage/sqlite-node/CHANGELOG.md new file mode 100644 index 00000000..d2f5abd6 --- /dev/null +++ b/packages/storage/sqlite-node/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## [Unreleased] + +### Added + +- Added a Node.js SQLite storage backend for agent harness sessions, including migrations and materialized session views ([#6594](https://github.com/earendil-works/pi/pull/6594) by [@cristinaponcela](https://github.com/cristinaponcela)). diff --git a/packages/storage/sqlite-node/README.md b/packages/storage/sqlite-node/README.md index 84915738..3b423ff6 100644 --- a/packages/storage/sqlite-node/README.md +++ b/packages/storage/sqlite-node/README.md @@ -1,4 +1,4 @@ -# @earendil-works/pi-agent-sqlite-node +# @earendil-works/pi-storage-sqlite-node Node sqlite storage backend for `@earendil-works/pi-agent-core` sessions. Provides the `node:sqlite` adapter (`SqliteDatabase` implementation) and the SQLite session diff --git a/packages/storage/sqlite-node/package.json b/packages/storage/sqlite-node/package.json index 66069bd1..5579786f 100644 --- a/packages/storage/sqlite-node/package.json +++ b/packages/storage/sqlite-node/package.json @@ -1,5 +1,5 @@ { - "name": "@earendil-works/pi-agent-sqlite-node", + "name": "@earendil-works/pi-storage-sqlite-node", "version": "0.80.10", "description": "Node sqlite storage backend for @earendil-works/pi-agent-core sessions", "type": "module", @@ -13,7 +13,8 @@ }, "files": [ "dist", - "README.md" + "README.md", + "CHANGELOG.md" ], "scripts": { "clean": "node -e \"import('node:fs/promises').then(fs=>fs.rm('dist',{recursive:true,force:true}))\"", diff --git a/scripts/local-release.mjs b/scripts/local-release.mjs index 181379d1..ac752c77 100644 --- a/scripts/local-release.mjs +++ b/scripts/local-release.mjs @@ -9,6 +9,7 @@ const packages = [ { directory: "packages/ai", name: "@earendil-works/pi-ai" }, { directory: "packages/tui", name: "@earendil-works/pi-tui" }, { directory: "packages/agent", name: "@earendil-works/pi-agent-core" }, + { directory: "packages/storage/sqlite-node", name: "@earendil-works/pi-storage-sqlite-node" }, { directory: "packages/coding-agent", name: "@earendil-works/pi-coding-agent" }, ]; diff --git a/scripts/package-workspaces.mjs b/scripts/package-workspaces.mjs new file mode 100644 index 00000000..745ce818 --- /dev/null +++ b/scripts/package-workspaces.mjs @@ -0,0 +1,24 @@ +import { existsSync, readdirSync } from "node:fs"; +import { join } from "node:path"; + +const SKIPPED_DIRECTORIES = new Set(["dist", "node_modules"]); + +export function findPackageDirectories(root = "packages") { + const packageDirectories = []; + + function visit(directory) { + if (existsSync(join(directory, "package.json"))) { + packageDirectories.push(directory); + } + + for (const entry of readdirSync(directory, { withFileTypes: true })) { + if (!entry.isDirectory() || SKIPPED_DIRECTORIES.has(entry.name)) { + continue; + } + visit(join(directory, entry.name)); + } + } + + visit(root); + return packageDirectories.sort(); +} diff --git a/scripts/publish.mjs b/scripts/publish.mjs index 920954f8..e0b1410c 100644 --- a/scripts/publish.mjs +++ b/scripts/publish.mjs @@ -7,6 +7,7 @@ import { join } from "node:path"; const packages = [ { directory: "packages/ai", name: "@earendil-works/pi-ai" }, { directory: "packages/agent", name: "@earendil-works/pi-agent-core" }, + { directory: "packages/storage/sqlite-node", name: "@earendil-works/pi-storage-sqlite-node" }, { directory: "packages/tui", name: "@earendil-works/pi-tui" }, { directory: "packages/coding-agent", name: "@earendil-works/pi-coding-agent" }, ]; diff --git a/scripts/release.mjs b/scripts/release.mjs index 95cac6e6..8aae005d 100755 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -18,9 +18,10 @@ * 9. Push main and the tag to trigger CI publishing */ -import { execSync } from "child_process"; -import { readFileSync, writeFileSync, readdirSync, existsSync } from "fs"; -import { join } from "path"; +import { execSync } from "node:child_process"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { findPackageDirectories } from "./package-workspaces.mjs"; const RELEASE_TARGET = process.argv[2]; const BUMP_TYPES = new Set(["major", "minor", "patch"]); @@ -97,10 +98,8 @@ function bumpOrSetVersion(target) { } function getChangelogs() { - const packagesDir = "packages"; - const packages = readdirSync(packagesDir); - return packages - .map((pkg) => join(packagesDir, pkg, "CHANGELOG.md")) + return findPackageDirectories() + .map((directory) => join(directory, "CHANGELOG.md")) .filter((path) => existsSync(path)); } diff --git a/scripts/sync-versions.js b/scripts/sync-versions.js index 136ed088..88ebdf87 100644 --- a/scripts/sync-versions.js +++ b/scripts/sync-versions.js @@ -1,96 +1,76 @@ #!/usr/bin/env node /** - * Syncs all workspace package dependency versions to match their current versions. - * This ensures lockstep versioning across the monorepo. + * Syncs all non-private workspace package dependency versions to match their current versions. + * This ensures release packages, including unpublished packages, use lockstep versioning. */ -import { readFileSync, writeFileSync, readdirSync } from 'fs'; -import { join } from 'path'; +import { readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { findPackageDirectories } from "./package-workspaces.mjs"; -const packagesDir = join(process.cwd(), 'packages'); -const packageDirs = readdirSync(packagesDir, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name); +const packages = findPackageDirectories() + .map((directory) => { + const path = join(directory, "package.json"); + return { data: JSON.parse(readFileSync(path, "utf8")), path }; + }) + .filter((pkg) => pkg.data.private !== true); -// Read all package.json files and build version map -const packages = {}; -const versionMap = {}; +const versionMap = new Map(packages.map((pkg) => [pkg.data.name, pkg.data.version])); -for (const dir of packageDirs) { - const pkgPath = join(packagesDir, dir, 'package.json'); - try { - const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); - packages[dir] = { path: pkgPath, data: pkg }; - versionMap[pkg.name] = pkg.version; - } catch (e) { - console.error(`Failed to read ${pkgPath}:`, e.message); - } -} - -console.log('Current versions:'); -for (const [name, version] of Object.entries(versionMap).sort()) { +console.log("Current versions:"); +for (const [name, version] of [...versionMap].sort(([a], [b]) => a.localeCompare(b))) { console.log(` ${name}: ${version}`); } -// Verify all versions are the same (lockstep) -const versions = new Set(Object.values(versionMap)); +const versions = new Set(versionMap.values()); if (versions.size > 1) { - console.error('\n❌ ERROR: Not all packages have the same version!'); - console.error('Expected lockstep versioning. Run one of:'); - console.error(' npm run version:patch'); - console.error(' npm run version:minor'); - console.error(' npm run version:major'); + console.error("\nERROR: Not all non-private packages have the same version."); + console.error("Expected lockstep versioning. Run one of:"); + console.error(" npm run version:patch"); + console.error(" npm run version:minor"); + console.error(" npm run version:major"); process.exit(1); } -console.log('\n✅ All packages at same version (lockstep)'); +console.log("\nAll non-private packages are at the same version (lockstep)."); -// Update all inter-package dependencies let totalUpdates = 0; -for (const [dir, pkg] of Object.entries(packages)) { +for (const pkg of packages) { let updated = false; - - // Check dependencies - if (pkg.data.dependencies) { - for (const [depName, currentVersion] of Object.entries(pkg.data.dependencies)) { - if (versionMap[depName]) { - const newVersion = `^${versionMap[depName]}`; - if (currentVersion !== newVersion) { - console.log(`\n${pkg.data.name}:`); - console.log(` ${depName}: ${currentVersion} → ${newVersion}`); - pkg.data.dependencies[depName] = newVersion; - updated = true; - totalUpdates++; - } + + for (const dependencyType of ["dependencies", "devDependencies"]) { + const dependencies = pkg.data[dependencyType]; + if (!dependencies) { + continue; + } + + for (const [dependencyName, currentVersion] of Object.entries(dependencies)) { + const dependencyVersion = versionMap.get(dependencyName); + if (!dependencyVersion) { + continue; } + + const newVersion = `^${dependencyVersion}`; + if (currentVersion === newVersion) { + continue; + } + + console.log(`\n${pkg.data.name}:`); + console.log(` ${dependencyName}: ${currentVersion} → ${newVersion}${dependencyType === "devDependencies" ? " (devDependencies)" : ""}`); + dependencies[dependencyName] = newVersion; + updated = true; + totalUpdates++; } } - - // Check devDependencies - if (pkg.data.devDependencies) { - for (const [depName, currentVersion] of Object.entries(pkg.data.devDependencies)) { - if (versionMap[depName]) { - const newVersion = `^${versionMap[depName]}`; - if (currentVersion !== newVersion) { - console.log(`\n${pkg.data.name}:`); - console.log(` ${depName}: ${currentVersion} → ${newVersion} (devDependencies)`); - pkg.data.devDependencies[depName] = newVersion; - updated = true; - totalUpdates++; - } - } - } - } - - // Write if updated + if (updated) { - writeFileSync(pkg.path, JSON.stringify(pkg.data, null, '\t') + '\n'); + writeFileSync(pkg.path, `${JSON.stringify(pkg.data, null, "\t")}\n`); } } if (totalUpdates === 0) { - console.log('\nAll inter-package dependencies already in sync.'); + console.log("\nAll inter-package dependencies are already in sync."); } else { - console.log(`\n✅ Updated ${totalUpdates} dependency version(s)`); + console.log(`\nUpdated ${totalUpdates} dependency version(s).`); }