caliper-harness 0.0.0-stage → 0.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Specific Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,33 @@
1
- # Temporary Holding Version
1
+ # Caliper
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Local models, practical coding tools and project memory.
4
+
5
+ ```sh
6
+ npm install caliper-harness
7
+ caliper
8
+ ```
9
+
10
+ Installation downloads the complete application and registers `caliper` for your
11
+ user account. If your current terminal cannot find it, open a new terminal;
12
+ installation cannot change the PATH of a terminal that is already running. You
13
+ can then enter any project directory and run `caliper`.
14
+
15
+ Plain npm installation creates `node_modules` and npm package files in the
16
+ installation directory. To avoid those files in a project, install globally with
17
+ `npm install -g caliper-harness`; npm supplies the global command in that case.
18
+
19
+ Supported platforms are macOS Apple Silicon, Windows x64 and Linux x64/ARM64.
20
+ Node 20 or newer is needed for the npm launcher. On Windows, the launcher checks
21
+ the Microsoft Visual C++ runtime before starting. If it is missing or outdated,
22
+ the shared prerequisite helper downloads Microsoft's installer directly and may
23
+ request administrator approval or a restart. A compatible installation needs no
24
+ download. Caliper does not bundle Microsoft's installer or runtime DLLs.
25
+
26
+ Model weights and tools belonging to your project remain yours to install.
27
+
28
+ Caliper offers available updates at startup. Use `/update` to download an update
29
+ for your next launch, or repeat your npm installation with `caliper-harness@latest`.
30
+
31
+ Your settings, graph and sessions live in your operating system's user-data
32
+ folder, separate from application versions. Updates preserve previous versions
33
+ and never delete your project work or model files.
package/bin.cjs ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { spawn } = require('node:child_process');
6
+ const app = require('./install.cjs');
7
+ (async () => {
8
+ const base = app.home();
9
+ let selected;
10
+ try { selected = JSON.parse(fs.readFileSync(path.join(base, 'current.json'), 'utf8')); }
11
+ catch (error) { if (error.code !== 'ENOENT') throw error; await app.install(); selected = JSON.parse(fs.readFileSync(path.join(base, 'current.json'), 'utf8')); }
12
+ if (!/^\d+\.\d+\.\d+$/.test(selected.version) || selected.platform !== app.identity || selected.root !== path.join(base, 'versions', selected.version, app.identity, 'Caliper')) throw new Error('Invalid installed version selection');
13
+ app.validate(selected.root, true);
14
+ await app.ensurePrerequisites(selected.root);
15
+ const env = { ...process.env, PATH: path.join(selected.root, 'bin') + path.delimiter + (process.env.PATH || '') };
16
+ if (process.platform === 'linux') env.LD_LIBRARY_PATH = path.join(selected.root, 'lib') + (process.env.LD_LIBRARY_PATH ? path.delimiter + process.env.LD_LIBRARY_PATH : '');
17
+ const child = spawn(app.executable(selected.root), process.argv.slice(2), { env, stdio: 'inherit', windowsHide: false });
18
+ for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => { if (signal === 'SIGTERM') child.kill(signal); });
19
+ child.on('error', error => { console.error(`Cannot start Caliper: ${error.message}`); process.exitCode = 1; });
20
+ child.on('exit', (code, signal) => { process.exitCode = code ?? (signal === 'SIGINT' ? 130 : 1); });
21
+ })().catch(error => { console.error(`Caliper: ${error.message}`); process.exitCode = 1; });
package/install.cjs ADDED
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const os = require('node:os');
5
+ const crypto = require('node:crypto');
6
+ const { pipeline, finished } = require('node:stream/promises');
7
+ const { Transform, Readable } = require('node:stream');
8
+ const tar = require('tar');
9
+ const { spawn } = require('node:child_process');
10
+ const version = require('./package.json').version;
11
+ const identity = `${process.platform === 'win32' ? 'windows' : process.platform}-${process.arch === 'x64' ? 'amd64' : process.arch}`;
12
+ const supported = ['darwin-arm64', 'windows-amd64', 'linux-amd64', 'linux-arm64'];
13
+ function home() {
14
+ if (process.platform === 'darwin') return path.join(os.homedir(), 'Library', 'Application Support', 'caliper');
15
+ if (process.platform === 'win32') return path.join(process.env.LOCALAPPDATA || process.env.APPDATA, 'caliper');
16
+ return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share'), 'caliper');
17
+ }
18
+ function executable(root) { return path.join(root, process.platform === 'win32' ? 'caliper.exe' : 'caliper-harness'); }
19
+ function validate(root, allowLegacy = false) {
20
+ if (fs.existsSync(path.join(root, 'BUILD-INCOMPLETE'))) throw new Error('Incomplete application bundle');
21
+ const required = ['prompts.yaml', 'MANIFEST.sha256', 'runtime/decision/requirements.lock'];
22
+ if (identity.startsWith('windows-')) { required.push('kuzu_shared.dll', 'bin/rg.exe', 'runtime/decision/python.exe'); if (!allowLegacy || !legacyWindowsRuntime(root)) required.push('ensure-windows-runtime.ps1'); }
23
+ else { required.push('bin/rg', 'runtime/decision/bin/python3', identity.startsWith('darwin-') ? 'lib/libkuzu.dylib' : 'lib/libkuzu.so'); if (identity.startsWith('darwin-')) required.push('runtime/worker/bin/python3'); }
24
+ for (const name of [executable(root), ...required.map(file => path.join(root, file))]) if (!fs.statSync(name).isFile()) throw new Error(`Missing product file: ${name}`);
25
+ if (process.platform !== 'win32') { for (const file of [executable(root), ...required.filter(name => name === 'bin/rg' || name.endsWith('/bin/python3')).map(name => path.join(root,name))]) fs.accessSync(file, fs.constants.X_OK); }
26
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'runtime', 'manifest.json'), 'utf8'));
27
+ if (manifest.format !== 1 || manifest.platform !== identity) throw new Error('Application runtime platform mismatch');
28
+ }
29
+ async function verify(root) {
30
+ validate(root);
31
+ const lines = fs.readFileSync(path.join(root, 'MANIFEST.sha256'), 'utf8').trim().split(/\r?\n/);
32
+ for (const line of lines) {
33
+ const match = /^([a-f0-9]{64}) \.\/(.+)$/i.exec(line);
34
+ if (!match || /[\\:\0]/.test(match[2])) throw new Error('Invalid application inventory');
35
+ const file = fs.realpathSync(path.resolve(root, match[2]));
36
+ const rel = path.relative(fs.realpathSync(root), file);
37
+ if (rel === '..' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) throw new Error('Application inventory escapes install');
38
+ const hash = crypto.createHash('sha256');
39
+ for await (const chunk of fs.createReadStream(file)) hash.update(chunk);
40
+ if (hash.digest('hex').toLowerCase() !== match[1].toLowerCase()) throw new Error(`Application inventory mismatch: ${match[2]}`);
41
+ }
42
+ }
43
+ async function get(url) { const r = await fetch(url); if (!r.ok) { const error = new Error(`${url}: HTTP ${r.status}`); error.status = r.status; throw error; } return r; }
44
+ function legacyWindowsRuntime(root) {
45
+ try {
46
+ const inventory = '\n' + fs.readFileSync(path.join(root, 'MANIFEST.sha256'), 'utf8').replace(/\r\n/g, '\n').replace(/\n$/, '') + '\n';
47
+ return ['msvcp140.dll', 'vcruntime140.dll', 'vcruntime140_1.dll', 'runtime/decision/msvcp140.dll'].every(name => {
48
+ const file = fs.realpathSync(path.join(root, name)), rel = path.relative(fs.realpathSync(root), file);
49
+ if (rel === '..' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) return false;
50
+ const hash = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
51
+ return inventory.includes(`\n${hash} ./${name}\n`);
52
+ });
53
+ } catch { return false; }
54
+ }
55
+ async function ensurePrerequisites(root) {
56
+ if (process.platform !== 'win32') return;
57
+ const helper = path.join(root, 'ensure-windows-runtime.ps1');
58
+ if (!fs.existsSync(helper) && legacyWindowsRuntime(root)) return;
59
+ if (!process.env.SystemRoot) throw new Error('SystemRoot unavailable; cannot check Windows runtime');
60
+ await new Promise((resolve, reject) => {
61
+ const child = spawn(path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'), ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', helper], {stdio: 'inherit', windowsHide: false});
62
+ child.on('error', reject);
63
+ child.on('exit', code => code === 0 ? resolve() : reject(new Error(`Windows runtime prerequisite failed (${code}); previous selection preserved. Finish any requested restart before retrying.`)));
64
+ });
65
+ }
66
+ async function activateNative(root) {
67
+ await ensurePrerequisites(root);
68
+ const env = { ...process.env, PATH: path.join(root,'bin') + path.delimiter + (process.env.PATH || '') };
69
+ if (process.platform === 'linux') env.LD_LIBRARY_PATH = path.join(root,'lib') + (process.env.LD_LIBRARY_PATH ? path.delimiter + process.env.LD_LIBRARY_PATH : '');
70
+ await new Promise((resolve,reject) => {
71
+ const child=spawn(executable(root),['install-activate'],{env,stdio:'inherit'});
72
+ child.on('error',reject);child.on('exit',code=>code===0?resolve():reject(new Error(`Native installation activation failed (${code}); previous selection preserved`)));
73
+ });
74
+ }
75
+ async function registerCommand(root) {
76
+ // Global npm installs already register the package's bin entry. Local npm
77
+ // installs need the same per-user registration as downloaded app folders.
78
+ if (process.env.npm_config_global === 'true') return;
79
+ let command = '/bin/sh', args = [path.join(root, 'install.sh')];
80
+ if (process.platform === 'win32') {
81
+ if (!process.env.SystemRoot) throw new Error('SystemRoot unavailable; cannot register Caliper command');
82
+ command = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
83
+ // Register the installed folder in place; do not copy its runtime again.
84
+ args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', path.join(root, 'install-windows.ps1'), '-TargetDirectory', root];
85
+ }
86
+ await new Promise((resolve, reject) => {
87
+ const child = spawn(command, args, {stdio: 'inherit', windowsHide: false});
88
+ child.on('error', reject);
89
+ child.on('exit', code => code === 0 ? resolve() : reject(new Error(`Caliper command registration failed (${code}); application retained at ${root}`)));
90
+ });
91
+ }
92
+ async function install(base = home(), download = get, activate = activateNative) {
93
+ if (!supported.includes(identity)) throw new Error(`No Caliper product bundle for ${identity}. Supported: ${supported.join(', ')}`);
94
+ const parent = path.join(base, 'versions', version, identity);
95
+ const root = path.join(parent, 'Caliper');
96
+ if (fs.existsSync(parent)) { await verify(root); await activate(root, version, base); return root; }
97
+ fs.mkdirSync(path.join(base, 'versions'), { recursive: true });
98
+ const stage = fs.mkdtempSync(path.join(base, 'versions', '.npm-install-'));
99
+ try {
100
+ const name = `caliper-${version}-${identity}.tar.gz`;
101
+ const url = `https://github.com/santiagoaraoz2001-sketch/caliper-harness/releases/download/v${version}/${name}`;
102
+ const checksum = (await (await download(`${url}.sha256`)).text()).trim().split(/\s+/)[0];
103
+ if (!/^[a-f0-9]{64}$/i.test(checksum)) throw new Error('Invalid release checksum');
104
+ const archive = path.join(stage, 'bundle.tar.gz');
105
+ const hash = crypto.createHash('sha256');
106
+ const tee = new Transform({ transform(chunk, _, cb) { hash.update(chunk); cb(null, chunk); } });
107
+ const output = fs.createWriteStream(archive, { flags: 'wx' });
108
+ try {
109
+ let response;
110
+ try { response = await download(url); }
111
+ catch (error) { if (error.status !== 404) throw error; }
112
+ if (response) await pipeline(Readable.fromWeb(response.body), tee, output);
113
+ else {
114
+ const parts = await (await download(`${url}.parts.json`)).json();
115
+ if (parts.format !== 1 || parts.archive !== name || parts.sha256.toLowerCase() !== checksum.toLowerCase() || !Array.isArray(parts.parts) || !parts.parts.length) throw new Error('Invalid split-archive manifest');
116
+ const seen = new Set();
117
+ for (const part of parts.parts) {
118
+ if (typeof part.name !== 'string' || path.basename(part.name) !== part.name || /[\\:]/.test(part.name) || !part.name.startsWith(name + '.part-') || seen.has(part.name) || !Number.isSafeInteger(part.bytes) || part.bytes <= 0 || !/^[a-f0-9]{64}$/i.test(part.sha256)) throw new Error('Invalid archive part');
119
+ seen.add(part.name); const partHash = crypto.createHash('sha256'); let bytes = 0;
120
+ const source = await download(new URL(part.name, url).href);
121
+ const stream = new Transform({ transform(chunk, _, cb) { bytes += chunk.length; partHash.update(chunk); hash.update(chunk); cb(null, chunk); } });
122
+ await pipeline(Readable.fromWeb(source.body), stream, output, { end: false });
123
+ if (bytes !== part.bytes || partHash.digest('hex').toLowerCase() !== part.sha256.toLowerCase()) throw new Error(`Archive part failed verification: ${part.name}`);
124
+ }
125
+ output.end(); await finished(output);
126
+ }
127
+ } catch (error) { await new Promise(resolve => { if (output.closed) return resolve(); output.once("close", resolve); output.destroy(); }); throw error; }
128
+ if (hash.digest('hex').toLowerCase() !== checksum.toLowerCase()) throw new Error('Release checksum mismatch; previous installation unchanged');
129
+ const unpack = path.join(stage, 'unpacked'); fs.mkdirSync(unpack);
130
+ await tar.x({ file: archive, cwd: unpack, strict: true, preservePaths: false });
131
+ await verify(path.join(unpack, 'Caliper'));
132
+ fs.mkdirSync(path.dirname(parent), { recursive: true });
133
+ fs.renameSync(unpack, parent);
134
+ await activate(root, version, base);
135
+ return root;
136
+ } finally { fs.rmSync(stage, { recursive: true, force: true }); }
137
+ }
138
+ if (require.main === module) install().then(async root => { await registerCommand(root); console.log(`Caliper ${version} installed: ${root}\nRun caliper from your project directory. Reopen your terminal if the command is not found.`); }).catch(error => { console.error(`Caliper installation failed: ${error.message}`); process.exitCode = 1; });
139
+ module.exports = { home, executable, validate, install, identity, version, ensurePrerequisites, activateNative, registerCommand };
package/package.json CHANGED
@@ -1,6 +1,26 @@
1
1
  {
2
2
  "name": "caliper-harness",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.3",
4
+ "description": "A local coding harness with tools, planning and durable project memory",
5
+ "license": "MIT",
6
+ "repository": "github:santiagoaraoz2001-sketch/caliper-harness",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "bin": {
11
+ "caliper": "bin.cjs"
12
+ },
13
+ "files": [
14
+ "bin.cjs",
15
+ "install.cjs",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "postinstall": "node install.cjs",
21
+ "test": "node --test test.cjs"
22
+ },
23
+ "dependencies": {
24
+ "tar": "7.5.22"
25
+ }
26
+ }