@kinlab/kin 0.2.13

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/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @kinlab/kin
2
+
3
+ The canonical npm install surface for [Kin](https://github.com/firelock-ai/kin), the
4
+ semantic system of record for software work.
5
+
6
+ ```sh
7
+ npm install -g @kinlab/kin
8
+ kin --version
9
+ ```
10
+
11
+ or zero-install:
12
+
13
+ ```sh
14
+ npx -y @kinlab/kin --version
15
+ ```
16
+
17
+ ## What it does
18
+
19
+ `@kinlab/kin` ships two thin launchers, not a JavaScript reimplementation:
20
+
21
+ - **`kin`** — provisions the managed native `kin` + `kin-daemon` release for your
22
+ platform on first run (downloaded from the matching GitHub release, verified against
23
+ its published SHA-256), installs it under `~/.kin/bin`, then passes every invocation
24
+ straight through to the real binary and mirrors its exit.
25
+ - **`kin-mcp`** — compatibility entrypoint that starts Kin's MCP server
26
+ (`kin mcp start`). MCP is one included mode of Kin, not a separate product.
27
+
28
+ The launcher and the shell installer (`scripts/install.sh`) share the same install
29
+ contract (`$KIN_HOME`, default `~/.kin`): either lane satisfies the other, and neither
30
+ silently downgrades an install the other made.
31
+
32
+ ## Environment
33
+
34
+ | Variable | Effect |
35
+ | --- | --- |
36
+ | `KIN_HOME` | Root of the managed install (default `~/.kin`). |
37
+ | `KIN_MANAGED_BIN` | Explicit path to a `kin` binary; disables provisioning entirely. |
38
+ | `KIN_NO_PROVISION=1` | Never touch the network; fail loud if no binary is present. |
39
+ | `KIN_LAUNCHER_ADOPT=1` | Allow re-provisioning over a version-skewed non-npm install. |
40
+
41
+ ## MCP setup
42
+
43
+ Any MCP client can use the included server:
44
+
45
+ ```json
46
+ { "command": "npx", "args": ["-y", "@kinlab/kin", "mcp", "start"] }
47
+ ```
48
+
49
+ `@kinlab/kin-mcp` remains published for existing configurations; new setups should use
50
+ this package.
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // Copyright 2026 Firelock, LLC
4
+
5
+ // `kin-mcp` — compatibility launcher for Kin's MCP server.
6
+ //
7
+ // MCP is one included mode of Kin, not a separate product. This bin exists so
8
+ // existing `@kinlab/kin-mcp` users keep a working `kin-mcp` entrypoint; it
9
+ // provisions the managed release like the `kin` launcher and starts
10
+ // `kin mcp start`, forwarding any extra argv. Progress and failures go to
11
+ // stderr only — stdout belongs to the MCP stdio protocol.
12
+
13
+ import os from 'node:os';
14
+ import { spawnSync } from 'node:child_process';
15
+
16
+ import { packageVersion, targetKinVersion, notProvisionedMessage } from '../lib/resolve.mjs';
17
+ import { ensureProvisioned } from '../lib/provision.mjs';
18
+
19
+ const argv = process.argv.slice(2);
20
+
21
+ if (argv.includes('--version') || argv.includes('-V')) {
22
+ process.stdout.write(
23
+ `@kinlab/kin ${packageVersion()} (kin-mcp compatibility launcher; provisions kin ${targetKinVersion()})\n`,
24
+ );
25
+ process.exit(0);
26
+ }
27
+
28
+ let binary = null;
29
+ try {
30
+ binary = await ensureProvisioned();
31
+ } catch (error) {
32
+ process.stderr.write(`kin-mcp: provisioning failed: ${error.message}\n`);
33
+ }
34
+
35
+ if (!binary) {
36
+ process.stderr.write(`${notProvisionedMessage('kin')}\n`);
37
+ process.exit(1);
38
+ }
39
+
40
+ const result = spawnSync(binary, ['mcp', 'start', ...argv], { stdio: 'inherit' });
41
+ if (result.error) {
42
+ process.stderr.write(`kin-mcp: failed to launch managed binary: ${result.error.message}\n`);
43
+ process.exit(1);
44
+ }
45
+ if (result.signal) {
46
+ process.exit(128 + (os.constants.signals[result.signal] ?? 0));
47
+ }
48
+ process.exit(result.status ?? 0);
package/bin/kin.mjs ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // Copyright 2026 Firelock, LLC
4
+
5
+ // `kin` — canonical launcher/passthrough for the Kin CLI.
6
+ //
7
+ // Provisions the managed native `kin` (+ `kin-daemon`) release on first run,
8
+ // then forwards argv straight to it and mirrors its exit. `--version` therefore
9
+ // proves provisioning end-to-end: it answers with the real binary's version.
10
+ // When provisioning is unavailable (offline, disabled), it fails loud with an
11
+ // actionable message — it never pretends to run.
12
+
13
+ import os from 'node:os';
14
+ import { spawnSync } from 'node:child_process';
15
+
16
+ import { packageVersion, targetKinVersion, notProvisionedMessage } from '../lib/resolve.mjs';
17
+ import { ensureProvisioned } from '../lib/provision.mjs';
18
+
19
+ const argv = process.argv.slice(2);
20
+ const wantsVersion = argv.includes('--version') || argv.includes('-V');
21
+
22
+ function launcherVersionLine(note) {
23
+ return `@kinlab/kin ${packageVersion()} (launcher; provisions kin ${targetKinVersion()}${note})\n`;
24
+ }
25
+
26
+ let binary = null;
27
+ let provisionError = null;
28
+ try {
29
+ binary = await ensureProvisioned();
30
+ } catch (error) {
31
+ provisionError = error;
32
+ }
33
+
34
+ if (!binary) {
35
+ if (provisionError) {
36
+ process.stderr.write(`kin: provisioning failed: ${provisionError.message}\n`);
37
+ }
38
+ if (wantsVersion) {
39
+ // Honest launcher identity; success only when provisioning was explicitly
40
+ // disabled rather than broken.
41
+ process.stdout.write(launcherVersionLine(provisionError ? ', provisioning failed' : ', not installed'));
42
+ process.exit(provisionError ? 1 : 0);
43
+ }
44
+ process.stderr.write(`${notProvisionedMessage('kin')}\n`);
45
+ process.exit(1);
46
+ }
47
+
48
+ const result = spawnSync(binary, argv, { stdio: 'inherit' });
49
+ if (result.error) {
50
+ process.stderr.write(`kin: failed to launch managed binary: ${result.error.message}\n`);
51
+ process.exit(1);
52
+ }
53
+ if (result.signal) {
54
+ // Mirror signal deaths honestly instead of reporting success.
55
+ process.exit(128 + (os.constants.signals[result.signal] ?? 0));
56
+ }
57
+ process.exit(result.status ?? 0);
@@ -0,0 +1,257 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Firelock, LLC
3
+
4
+ // Binary provisioning for the @kinlab/kin launcher.
5
+ //
6
+ // Downloads the platform archive of the pinned Kin release from GitHub
7
+ // Releases, verifies it against the per-artifact SHA-256 published next to it,
8
+ // and installs the binaries into <kinHome>/bin — the same directory and the
9
+ // same refusal semantics as scripts/install.sh, so the two install lanes are
10
+ // interchangeable. kin-daemon is mandatory (status/search/MCP require it): a
11
+ // daemon-less archive aborts before anything is moved rather than leaving a
12
+ // half-installed environment. Network, spawning, and the environment are all
13
+ // injectable so every decision here is unit testable offline.
14
+
15
+ import crypto from 'node:crypto';
16
+ import fs from 'node:fs';
17
+ import os from 'node:os';
18
+ import path from 'node:path';
19
+ import { spawnSync } from 'node:child_process';
20
+
21
+ import {
22
+ binaryName,
23
+ kinHome,
24
+ managedBinaryPath,
25
+ readLauncherStamp,
26
+ resolveManagedBinary,
27
+ targetKinVersion,
28
+ writeLauncherStamp,
29
+ } from './resolve.mjs';
30
+
31
+ /**
32
+ * Release-artifact file name for a host. Follows the release workflow's
33
+ * naming (kin-{macos|linux|windows}-{x86_64|aarch64}); Windows ships as .zip.
34
+ * There is no native windows-aarch64 artifact — fail honestly rather than
35
+ * fabricate a URL (Windows-on-ARM can run the x64 build under emulation).
36
+ */
37
+ export function artifactName(platform = process.platform, arch = process.arch) {
38
+ const artifacts = {
39
+ 'darwin:arm64': 'kin-macos-aarch64.tar.gz',
40
+ 'darwin:x64': 'kin-macos-x86_64.tar.gz',
41
+ 'linux:arm64': 'kin-linux-aarch64.tar.gz',
42
+ 'linux:x64': 'kin-linux-x86_64.tar.gz',
43
+ 'win32:x64': 'kin-windows-x86_64.zip',
44
+ };
45
+ const key = `${platform}:${arch}`;
46
+ const file = artifacts[key];
47
+ if (!file) {
48
+ const supported = Object.keys(artifacts).join(', ');
49
+ throw new Error(
50
+ key === 'win32:arm64'
51
+ ? 'no native windows-aarch64 Kin release artifact exists; use an x64 Node ' +
52
+ 'under emulation (the x86_64 build runs on Windows-on-ARM), or build from source.'
53
+ : `no Kin release artifact for "${key}"; supported: ${supported}`,
54
+ );
55
+ }
56
+ return file;
57
+ }
58
+
59
+ /** Download URL of a file attached to the v<version> GitHub release. */
60
+ export function releaseDownloadUrl(version, file) {
61
+ return `https://github.com/firelock-ai/kin/releases/download/v${version}/${file}`;
62
+ }
63
+
64
+ /**
65
+ * Extract the hex digest from a `shasum -a 256` style checksum file
66
+ * ("<hex> <name>"). Refuses anything that does not look like SHA-256.
67
+ */
68
+ export function parseSha256File(text) {
69
+ const token = String(text).trim().split(/\s+/)[0] ?? '';
70
+ if (!/^[0-9a-f]{64}$/i.test(token)) {
71
+ throw new Error(`malformed .sha256 file (expected a 64-hex digest, got "${token.slice(0, 80)}")`);
72
+ }
73
+ return token.toLowerCase();
74
+ }
75
+
76
+ /** SHA-256 hex digest of a buffer. */
77
+ export function sha256Hex(buf) {
78
+ return crypto.createHash('sha256').update(buf).digest('hex');
79
+ }
80
+
81
+ async function fetchBuffer(url, fetchImpl) {
82
+ const res = await fetchImpl(url);
83
+ if (!res.ok) {
84
+ throw new Error(`download failed (${res.status}) for ${url}`);
85
+ }
86
+ return Buffer.from(await res.arrayBuffer());
87
+ }
88
+
89
+ /**
90
+ * Locate the extraction root: release archives contain a kin-* subdirectory;
91
+ * tolerate a flat archive the way install.sh does.
92
+ */
93
+ function extractionRoot(tmp) {
94
+ const sub = fs
95
+ .readdirSync(tmp, { withFileTypes: true })
96
+ .find((e) => e.isDirectory() && e.name.startsWith('kin-'));
97
+ return sub ? path.join(tmp, sub.name) : tmp;
98
+ }
99
+
100
+ /** Replace-then-move a binary into place (never overwrite in place: replacing
101
+ * a running executable's inode causes launch deadlocks on macOS). */
102
+ function installFile(src, dest, mode) {
103
+ fs.rmSync(dest, { force: true });
104
+ fs.copyFileSync(src, dest);
105
+ fs.chmodSync(dest, mode);
106
+ }
107
+
108
+ /**
109
+ * Download, verify, and install the pinned Kin release. Returns the installed
110
+ * managed `kin` path. Mirrors scripts/install.sh: kin + kin-daemon are
111
+ * mandatory, kin-vfs and the projection shim library are optional extras.
112
+ */
113
+ export async function provision(version, opts = {}) {
114
+ const {
115
+ env = process.env,
116
+ platform = process.platform,
117
+ arch = process.arch,
118
+ fetchImpl = fetch,
119
+ log = (line) => process.stderr.write(`${line}\n`),
120
+ } = opts;
121
+
122
+ const file = artifactName(platform, arch);
123
+ const url = releaseDownloadUrl(version, file);
124
+ log(`kin: provisioning managed kin ${version} (${file})...`);
125
+
126
+ const [archive, shaText] = await Promise.all([
127
+ fetchBuffer(url, fetchImpl),
128
+ fetchBuffer(`${url}.sha256`, fetchImpl),
129
+ ]);
130
+ const expected = parseSha256File(shaText.toString('utf8'));
131
+ const actual = sha256Hex(archive);
132
+ if (actual !== expected) {
133
+ throw new Error(
134
+ `SHA-256 mismatch for ${file}: expected ${expected}, got ${actual}. ` +
135
+ 'The download may be corrupted or tampered with; refusing to install.',
136
+ );
137
+ }
138
+
139
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'kinlab-kin-'));
140
+ try {
141
+ const archivePath = path.join(tmp, file);
142
+ fs.writeFileSync(archivePath, archive);
143
+ // bsdtar reads both .tar.gz and .zip with -xf, on macOS, Linux, and
144
+ // Windows 10+ (tar.exe ships in System32).
145
+ const tar = spawnSync('tar', ['-xf', archivePath, '-C', tmp], { encoding: 'utf8' });
146
+ if (tar.status !== 0) {
147
+ throw new Error(`archive extraction failed: ${tar.stderr || tar.error?.message || 'tar exited non-zero'}`);
148
+ }
149
+
150
+ const root = extractionRoot(tmp);
151
+ const kinSrc = path.join(root, binaryName('kin', platform));
152
+ const daemonSrc = path.join(root, binaryName('kin-daemon', platform));
153
+ if (!fs.existsSync(kinSrc) || !fs.existsSync(daemonSrc)) {
154
+ throw new Error(
155
+ 'kin-daemon (or kin) missing from the downloaded archive. kin status/search and ' +
156
+ 'the MCP server require the daemon; refusing a daemon-less install.',
157
+ );
158
+ }
159
+
160
+ const binDir = path.join(kinHome(env), 'bin');
161
+ const libDir = path.join(kinHome(env), 'lib');
162
+ fs.mkdirSync(binDir, { recursive: true });
163
+
164
+ installFile(kinSrc, path.join(binDir, binaryName('kin', platform)), 0o755);
165
+ installFile(daemonSrc, path.join(binDir, binaryName('kin-daemon', platform)), 0o755);
166
+
167
+ const vfsSrc = path.join(root, binaryName('kin-vfs', platform));
168
+ if (fs.existsSync(vfsSrc)) {
169
+ installFile(vfsSrc, path.join(binDir, binaryName('kin-vfs', platform)), 0o755);
170
+ }
171
+ for (const lib of ['libkin_vfs_shim.so', 'libkin_vfs_shim.dylib']) {
172
+ const libSrc = path.join(root, lib);
173
+ if (fs.existsSync(libSrc)) {
174
+ fs.mkdirSync(libDir, { recursive: true });
175
+ installFile(libSrc, path.join(libDir, lib), 0o644);
176
+ }
177
+ }
178
+
179
+ writeLauncherStamp(version, env);
180
+ log(`kin: managed kin ${version} installed at ${binDir}`);
181
+ return path.join(binDir, binaryName('kin', platform));
182
+ } finally {
183
+ fs.rmSync(tmp, { recursive: true, force: true });
184
+ }
185
+ }
186
+
187
+ /** First line of `<binary> --version`, or null if it cannot be run. */
188
+ export function probeBinaryVersion(binary, spawnImpl = spawnSync) {
189
+ const res = spawnImpl(binary, ['--version'], { encoding: 'utf8' });
190
+ if (res.error || res.status !== 0) return null;
191
+ const m = String(res.stdout).match(/^kin\s+(\S+)/);
192
+ return m ? m[1] : null;
193
+ }
194
+
195
+ /**
196
+ * Ensure the managed `kin` binary matching this launcher's pinned release is
197
+ * present, provisioning (or re-provisioning) when needed. Returns the binary
198
+ * path, or null when nothing usable exists and provisioning is unavailable.
199
+ *
200
+ * Policy, in order:
201
+ * - $KIN_MANAGED_BIN is an explicit user pin: use it as-is, never provision.
202
+ * - $KIN_NO_PROVISION=1: resolve-only, no network.
203
+ * - launcher stamp == target and the binary exists: run it (no probe cost).
204
+ * - stamp differs or binary missing: provision the pinned release.
205
+ * - no stamp but a binary exists (foreign install, e.g. install.sh): probe
206
+ * its version; adopt on match. On mismatch respect the foreign install —
207
+ * run it with a one-line notice — unless $KIN_LAUNCHER_ADOPT=1 opts into
208
+ * re-provisioning. Never silently downgrade someone else's install.
209
+ */
210
+ export async function ensureProvisioned(opts = {}) {
211
+ const {
212
+ env = process.env,
213
+ platform = process.platform,
214
+ arch = process.arch,
215
+ fetchImpl = fetch,
216
+ log = (line) => process.stderr.write(`${line}\n`),
217
+ spawnImpl = spawnSync,
218
+ } = opts;
219
+
220
+ const target = targetKinVersion();
221
+
222
+ if (env.KIN_MANAGED_BIN && env.KIN_MANAGED_BIN.trim()) {
223
+ return resolveManagedBinary('kin', env, platform);
224
+ }
225
+ const existing = resolveManagedBinary('kin', env, platform);
226
+ if (env.KIN_NO_PROVISION === '1') {
227
+ return existing;
228
+ }
229
+
230
+ const doProvision = () => provision(target, { env, platform, arch, fetchImpl, log });
231
+
232
+ if (!existing) {
233
+ return doProvision();
234
+ }
235
+
236
+ const stamp = readLauncherStamp(env);
237
+ if (stamp === target) {
238
+ return existing;
239
+ }
240
+ if (stamp !== null) {
241
+ return doProvision();
242
+ }
243
+
244
+ const probed = probeBinaryVersion(existing, spawnImpl);
245
+ if (probed === target) {
246
+ writeLauncherStamp(target, env);
247
+ return existing;
248
+ }
249
+ if (env.KIN_LAUNCHER_ADOPT === '1') {
250
+ return doProvision();
251
+ }
252
+ log(
253
+ `kin: using existing managed kin ${probed ?? 'unknown'} at ${managedBinaryPath('kin', env, platform)} ` +
254
+ `(this @kinlab/kin pins ${target}; set KIN_LAUNCHER_ADOPT=1 to re-provision).`,
255
+ );
256
+ return existing;
257
+ }
@@ -0,0 +1,138 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Firelock, LLC
3
+
4
+ // Managed-binary resolution for the @kinlab/kin launcher.
5
+ //
6
+ // This package is the canonical install surface for Kin: `npm i -g @kinlab/kin`
7
+ // gives you the `kin` and `kin-mcp` bins. The bins are thin launchers over a
8
+ // *managed* native binary (the real `kin` + `kin-daemon` release). This module
9
+ // owns the pure, side-effect-free part of that story — computing the platform
10
+ // target, where the managed binary is expected to live, and which release the
11
+ // launcher pins — so it is unit testable without spawning or touching the
12
+ // network. Downloading and installing live in ./provision.mjs.
13
+
14
+ import fs from 'node:fs';
15
+ import os from 'node:os';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
20
+
21
+ /** This package's own version, read from its package.json (single source). */
22
+ export function packageVersion() {
23
+ const manifest = JSON.parse(fs.readFileSync(path.join(HERE, '..', 'package.json'), 'utf8'));
24
+ return manifest.version;
25
+ }
26
+
27
+ /**
28
+ * The Kin release this launcher provisions. It tracks the package version: the
29
+ * canonical package is versioned in lockstep with the `kin` + `kin-daemon`
30
+ * release it installs (the release workflow asserts package version == tag).
31
+ */
32
+ export function targetKinVersion() {
33
+ return packageVersion();
34
+ }
35
+
36
+ /**
37
+ * Map a Node platform+arch pair to the Rust target triple of the managed
38
+ * binary. Pure and injectable so it is testable for every supported host.
39
+ */
40
+ export function platformTarget(platform = process.platform, arch = process.arch) {
41
+ const targets = {
42
+ 'darwin:arm64': 'aarch64-apple-darwin',
43
+ 'darwin:x64': 'x86_64-apple-darwin',
44
+ 'linux:arm64': 'aarch64-unknown-linux-gnu',
45
+ 'linux:x64': 'x86_64-unknown-linux-gnu',
46
+ 'win32:x64': 'x86_64-pc-windows-msvc',
47
+ 'win32:arm64': 'aarch64-pc-windows-msvc',
48
+ };
49
+ const key = `${platform}:${arch}`;
50
+ const triple = targets[key];
51
+ if (!triple) {
52
+ throw new Error(
53
+ `unsupported platform "${key}" for @kinlab/kin; supported: ${Object.keys(targets).join(', ')}`,
54
+ );
55
+ }
56
+ return triple;
57
+ }
58
+
59
+ /** Executable file name for a bin on the host platform (adds .exe on Windows). */
60
+ export function binaryName(name, platform = process.platform) {
61
+ return platform === 'win32' ? `${name}.exe` : name;
62
+ }
63
+
64
+ /** Root of Kin's managed install, honoring $KIN_HOME (default ~/.kin). */
65
+ export function kinHome(env = process.env, home = os.homedir()) {
66
+ return env.KIN_HOME && env.KIN_HOME.trim() ? env.KIN_HOME : path.join(home, '.kin');
67
+ }
68
+
69
+ /**
70
+ * Absolute path where the managed `name` binary is expected. An explicit
71
+ * $KIN_MANAGED_BIN overrides the whole computation (an explicit user pin;
72
+ * provisioning never writes through it); otherwise it is
73
+ * <kinHome>/bin/<name>[.exe] — the same directory the shell installer
74
+ * (scripts/install.sh) populates, so either lane satisfies the other.
75
+ */
76
+ export function managedBinaryPath(name = 'kin', env = process.env, platform = process.platform) {
77
+ if (env.KIN_MANAGED_BIN && env.KIN_MANAGED_BIN.trim()) {
78
+ return env.KIN_MANAGED_BIN;
79
+ }
80
+ return path.join(kinHome(env), 'bin', binaryName(name, platform));
81
+ }
82
+
83
+ /**
84
+ * Resolve the managed `name` binary to an absolute path if it exists on disk,
85
+ * else null. Never fabricates a path that is not really present.
86
+ */
87
+ export function resolveManagedBinary(name = 'kin', env = process.env, platform = process.platform) {
88
+ const candidate = managedBinaryPath(name, env, platform);
89
+ try {
90
+ return fs.existsSync(candidate) ? candidate : null;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Path of the launcher's version stamp: which Kin release this launcher last
98
+ * provisioned into <kinHome>/bin. Lets the normal launch path skip a
99
+ * `kin --version` probe; a binary installed by other means (install.sh) has no
100
+ * stamp and is probed instead of being silently trusted or clobbered.
101
+ */
102
+ export function launcherStampPath(env = process.env) {
103
+ return path.join(kinHome(env), 'bin', '.kinlab-kin-version');
104
+ }
105
+
106
+ /** The stamped provisioned version, or null when absent/unreadable. */
107
+ export function readLauncherStamp(env = process.env) {
108
+ try {
109
+ const text = fs.readFileSync(launcherStampPath(env), 'utf8').trim();
110
+ return text.length ? text : null;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+
116
+ /** Record `version` as the provisioned release (directory must exist). */
117
+ export function writeLauncherStamp(version, env = process.env) {
118
+ fs.writeFileSync(launcherStampPath(env), `${version}\n`);
119
+ }
120
+
121
+ /** Human-readable, honest explanation for when the managed binary is absent. */
122
+ export function notProvisionedMessage(name = 'kin', env = process.env, platform = process.platform) {
123
+ const expected = managedBinaryPath(name, env, platform);
124
+ const triple = (() => {
125
+ try {
126
+ return platformTarget();
127
+ } catch {
128
+ return 'unknown-target';
129
+ }
130
+ })();
131
+ return [
132
+ `kin: managed "${name}" binary not found (expected at ${expected}).`,
133
+ `Host target: ${triple}. @kinlab/kin ${packageVersion()} provisions the managed Kin`,
134
+ 'release on first run; if you are seeing this, provisioning failed or was disabled',
135
+ '(KIN_NO_PROVISION=1). Re-run with network access, set KIN_MANAGED_BIN to an existing',
136
+ 'kin binary, or install via https://github.com/firelock-ai/kin (scripts/install.sh).',
137
+ ].join('\n');
138
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@kinlab/kin",
3
+ "version": "0.2.13",
4
+ "description": "Canonical installer and launcher for Kin — provisions and runs the managed kin + kin-daemon release. MCP is one included mode (kin mcp start).",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "kin": "bin/kin.mjs",
9
+ "kin-mcp": "bin/kin-mcp.mjs"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "lib/",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "homepage": "https://github.com/firelock-ai/kin",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/firelock-ai/kin.git",
26
+ "directory": "packages/kin"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/firelock-ai/kin/issues"
30
+ },
31
+ "keywords": [
32
+ "kin",
33
+ "kinlab",
34
+ "cli",
35
+ "mcp",
36
+ "semantic"
37
+ ],
38
+ "scripts": {
39
+ "test": "node --test",
40
+ "lint": "node --check ./bin/kin.mjs && node --check ./bin/kin-mcp.mjs && node --check ./lib/resolve.mjs && node --check ./lib/provision.mjs",
41
+ "smoke": "node bin/kin.mjs --version"
42
+ }
43
+ }