driftseal 2.0.0 → 3.0.0

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.
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ const MINIMUM_NODE = Object.freeze({ major: 22, minor: 13, display: '22.13.0' });
4
+
5
+ class SqliteUnavailableError extends Error {
6
+ constructor(message, cause) {
7
+ super(message, cause ? { cause } : undefined);
8
+ this.name = 'SqliteUnavailableError';
9
+ }
10
+ }
11
+
12
+ function assertSupportedNode(version = process.versions.node) {
13
+ const [major, minor] = String(version)
14
+ .split('.')
15
+ .map((part) => Number.parseInt(part, 10));
16
+ if (major > MINIMUM_NODE.major || (major === MINIMUM_NODE.major && minor >= MINIMUM_NODE.minor)) {
17
+ return;
18
+ }
19
+ throw new Error(`DriftSeal requires Node.js ${MINIMUM_NODE.display} or newer; found ${version}`);
20
+ }
21
+
22
+ function warningType(args) {
23
+ const first = args[0];
24
+ if (typeof first === 'string') return first;
25
+ if (first && typeof first === 'object') return first.type || first.name || null;
26
+ return null;
27
+ }
28
+
29
+ function warningMessage(warning) {
30
+ if (warning instanceof Error) return warning.message;
31
+ return String(warning);
32
+ }
33
+
34
+ function loadNodeSqlite() {
35
+ assertSupportedNode();
36
+ if (process.env._DRIFTSEAL_TEST_DISABLE_SQLITE === '1') {
37
+ throw new SqliteUnavailableError('node:sqlite is unavailable in this runtime');
38
+ }
39
+ const originalEmitWarning = process.emitWarning;
40
+ process.emitWarning = function filteredEmitWarning(warning, ...args) {
41
+ if (
42
+ warningType(args) === 'ExperimentalWarning' &&
43
+ /\bSQLite\b/i.test(warningMessage(warning))
44
+ ) {
45
+ return;
46
+ }
47
+ return Reflect.apply(originalEmitWarning, this, [warning, ...args]);
48
+ };
49
+ try {
50
+ return require('node:sqlite');
51
+ } catch (error) {
52
+ if (error instanceof SqliteUnavailableError) throw error;
53
+ throw new SqliteUnavailableError('node:sqlite is unavailable in this runtime', error);
54
+ } finally {
55
+ process.emitWarning = originalEmitWarning;
56
+ }
57
+ }
58
+
59
+ let databaseSync;
60
+
61
+ function getDatabaseSync() {
62
+ if (!databaseSync) ({ DatabaseSync: databaseSync } = loadNodeSqlite());
63
+ return databaseSync;
64
+ }
65
+
66
+ module.exports = {
67
+ MINIMUM_NODE,
68
+ SqliteUnavailableError,
69
+ assertSupportedNode,
70
+ getDatabaseSync,
71
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "driftseal",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Seal outcomes, verification, and decisions into an auditable workflow for agentic coding",
5
5
  "keywords": [
6
6
  "driftseal",
@@ -34,16 +34,21 @@
34
34
  "files": [
35
35
  "index.js",
36
36
  "bin",
37
+ "lib",
38
+ "benchmark",
37
39
  "skills",
40
+ "test/package-smoke.js",
38
41
  "README.md",
39
42
  "README.zh-CN.md",
40
43
  "LICENSE"
41
44
  ],
42
45
  "scripts": {
46
+ "benchmark:recent-log": "node benchmark/recent-log.js",
47
+ "test:package": "node test/package-smoke.js",
43
48
  "test": "node --test test/*.test.js"
44
49
  },
45
50
  "engines": {
46
- "node": ">=18"
51
+ "node": ">=22.13.0"
47
52
  },
48
53
  "dependencies": {
49
54
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -32,7 +32,10 @@ driftseal log --last 3
32
32
 
33
33
  Then resume, extend, replace, verify, or close the outcome exactly as
34
34
  `AGENTS.md` requires. Use `extend` only when the additional work still delivers
35
- the same coherent outcome. For command syntax, run:
35
+ the same coherent outcome. `status` and `log --last 3` follow the current lane;
36
+ an open outcome stays visible even if it belongs to another lane. If the
37
+ requested work belongs to a different existing lane, switch first. For
38
+ command syntax, run:
36
39
 
37
40
  ```sh
38
41
  driftseal help
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert/strict');
4
+ const { execFileSync, spawnSync } = require('node:child_process');
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+
9
+ const root = path.resolve(__dirname, '..');
10
+ const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'driftseal-package-smoke-'));
11
+ const packDirectory = path.join(temporary, 'pack');
12
+ const consumer = path.join(temporary, 'consumer');
13
+
14
+ function npmCli() {
15
+ const npmJs = process.env.npm_execpath;
16
+ assert.equal(typeof npmJs, 'string', 'package smoke expects to run under npm (npm_execpath)');
17
+ assert.notEqual(npmJs, '', 'package smoke expects to run under npm (npm_execpath)');
18
+ assert.match(path.basename(npmJs), /\.js$/i, 'npm_execpath must be the JavaScript CLI');
19
+ assert.equal(fs.existsSync(npmJs), true, `npm_execpath exists: ${npmJs}`);
20
+ return npmJs;
21
+ }
22
+
23
+ function run(command, args, options = {}) {
24
+ return execFileSync(command, args, {
25
+ cwd: root,
26
+ encoding: 'utf8',
27
+ stdio: ['ignore', 'pipe', 'pipe'],
28
+ ...options,
29
+ });
30
+ }
31
+
32
+ function runNpm(args, options = {}) {
33
+ return run(process.execPath, [npmCli(), ...args], options);
34
+ }
35
+
36
+ function leftoverRebuildFiles(outcomeDir) {
37
+ return fs
38
+ .readdirSync(outcomeDir)
39
+ .filter(
40
+ (name) =>
41
+ name.startsWith('..outcome-index.sqlite.') && name.endsWith('.tmp')
42
+ );
43
+ }
44
+
45
+ function assertValidRebuiltIndex(indexFile, outcomeDir, runtime) {
46
+ assert.equal(fs.existsSync(indexFile), true, 'rebuilt SQLite index exists');
47
+ const raw = fs.readFileSync(indexFile);
48
+ assert.notEqual(raw.toString('utf8'), 'corrupt package smoke index');
49
+ assert.equal(raw.subarray(0, 16).toString(), 'SQLite format 3\0');
50
+
51
+ const leftovers = leftoverRebuildFiles(outcomeDir);
52
+ assert.deepEqual(leftovers, [], `temporary rebuild files remain: ${leftovers.join(', ')}`);
53
+
54
+ const DatabaseSync = runtime.getDatabaseSync();
55
+ const db = new DatabaseSync(indexFile, { readOnly: true });
56
+ try {
57
+ assert.equal(
58
+ Number(db.prepare('PRAGMA user_version').get().user_version),
59
+ runtime.INDEX_SCHEMA_VERSION
60
+ );
61
+ assert.equal(db.prepare('PRAGMA quick_check').get().quick_check, 'ok');
62
+ const outcomes = db
63
+ .prepare('SELECT id, ordinal, lane, status FROM outcomes ORDER BY ordinal')
64
+ .all();
65
+ assert.equal(outcomes.length, 1);
66
+ assert.equal(typeof outcomes[0].id, 'string');
67
+ assert.notEqual(outcomes[0].id, '');
68
+ assert.equal(Number(outcomes[0].ordinal), 0);
69
+ assert.equal(outcomes[0].lane, 'main');
70
+ assert.equal(outcomes[0].status, 'abandoned');
71
+ } finally {
72
+ db.close();
73
+ }
74
+ }
75
+
76
+ try {
77
+ fs.mkdirSync(packDirectory);
78
+ fs.mkdirSync(consumer);
79
+ fs.writeFileSync(
80
+ path.join(consumer, 'package.json'),
81
+ `${JSON.stringify({ name: 'driftseal-package-smoke', private: true }, null, 2)}\n`
82
+ );
83
+ const packed = JSON.parse(
84
+ runNpm(['pack', '--json', '--pack-destination', packDirectory])
85
+ );
86
+ assert.equal(packed.length, 1);
87
+ const tarball = path.join(packDirectory, packed[0].filename);
88
+ runNpm(['install', '--ignore-scripts', '--no-audit', '--no-fund', tarball], {
89
+ cwd: consumer,
90
+ });
91
+
92
+ const installed = path.join(consumer, 'node_modules', 'driftseal');
93
+ for (const file of [
94
+ 'bin/driftseal.js',
95
+ 'bin/driftseal-mcp.js',
96
+ 'lib/outcome-fold.js',
97
+ 'lib/outcome-index-sqlite.js',
98
+ 'lib/sqlite-runtime.js',
99
+ 'benchmark/recent-log.js',
100
+ 'test/package-smoke.js',
101
+ ]) {
102
+ assert.equal(fs.existsSync(path.join(installed, file)), true, `${file} is packaged`);
103
+ }
104
+
105
+ const sandbox = path.join(temporary, 'sandbox');
106
+ const home = path.join(sandbox, '.seal');
107
+ const outcomeDir = path.join(home, 'outcomes');
108
+ const indexFile = path.join(outcomeDir, '.outcome-index.sqlite');
109
+ fs.mkdirSync(sandbox);
110
+ const env = {
111
+ ...process.env,
112
+ DRIFTSEAL_HOME: home,
113
+ DRIFTSEAL_DECISION_HOME: path.join(home, 'madr'),
114
+ };
115
+ const cli = path.join(installed, 'bin', 'driftseal.js');
116
+ run(process.execPath, [cli, 'begin', 'packaged sqlite smoke'], {
117
+ cwd: sandbox,
118
+ env,
119
+ });
120
+ run(
121
+ process.execPath,
122
+ [cli, 'end', '--status', 'abandoned', '--note', 'packaged smoke'],
123
+ { cwd: sandbox, env }
124
+ );
125
+ const logged = spawnSync(process.execPath, [cli, 'log', '--last', '1'], {
126
+ cwd: sandbox,
127
+ env,
128
+ encoding: 'utf8',
129
+ stdio: ['ignore', 'pipe', 'pipe'],
130
+ });
131
+ assert.equal(logged.status, 0, logged.stderr);
132
+ assert.match(logged.stdout, /packaged sqlite smoke/);
133
+ assert.doesNotMatch(logged.stderr, /SQLite is an experimental feature/);
134
+ assert.equal(fs.existsSync(indexFile), true);
135
+ fs.writeFileSync(indexFile, 'corrupt package smoke index');
136
+ const rebuilt = run(process.execPath, [cli, 'log', '--last', '1'], {
137
+ cwd: sandbox,
138
+ env,
139
+ });
140
+ assert.match(rebuilt, /packaged sqlite smoke/);
141
+ assertValidRebuiltIndex(indexFile, outcomeDir, {
142
+ INDEX_SCHEMA_VERSION: require(path.join(installed, 'lib', 'outcome-index-sqlite.js'))
143
+ .INDEX_SCHEMA_VERSION,
144
+ getDatabaseSync: require(path.join(installed, 'lib', 'sqlite-runtime.js'))
145
+ .getDatabaseSync,
146
+ });
147
+ process.stdout.write(`package smoke passed: ${packed[0].filename}\n`);
148
+ } finally {
149
+ fs.rmSync(temporary, { recursive: true, force: true });
150
+ }