stackbite 0.5.10 → 0.5.12

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 CHANGED
@@ -1,98 +1,108 @@
1
- # Stackbite through npm and npx
2
-
3
- Work Stack organizes the work. Stackbite gets it moving.
4
-
5
- Node.js 20 or newer is required. The portable runtime is available for Windows x64
6
- and Linux x64 with glibc; macOS, ARM and musl/Alpine builds are not supplied yet.
7
- Python installation and compilation are unnecessary. Archive extraction requires
8
- `tar` (included in current Windows and common Linux distributions).
9
-
10
- ## Available without an npm account
11
-
12
- Run the package attached to the verified GitHub release:
13
-
14
- ```sh
15
- npx --yes https://github.com/Shinick-Han/stackbite/releases/download/v0.5.10/stackbite-0.5.10.tgz
16
- ```
17
-
18
- Arguments go after the package URL, for example `--version` or `--status`. For a
19
- persistent command, install that same package globally:
20
-
21
- ```sh
22
- npm install --global https://github.com/Shinick-Han/stackbite/releases/download/v0.5.10/stackbite-0.5.10.tgz
23
- stackbite
24
- ```
25
-
26
- The core source repository remains private at `Shinick-Han/helm`. The separate
27
- public `Shinick-Han/stackbite` repository distributes only this thin launcher and
28
- verified compiled runtime releases; installers need no GitHub or npm login.
29
- Registry-free distribution does not require publishing rights on npm.
30
- The small JavaScript package downloads the
31
- exact pinned native release on first execution, verifies its archive and file
32
- checksums and product identity, then reuses a persistent binary cache. There is no
33
- postinstall download or provider installation. An offline cached launch is possible;
34
- the first launch needs access to GitHub release downloads.
35
-
36
- ## Short npm registry commands
37
-
38
- Once the package owner publishes `stackbite` to the public npm registry:
39
-
40
- ```sh
41
- npx --yes stackbite
42
- npm install --global stackbite
43
- stackbite
44
- ```
45
-
46
- These short commands require registry publication. Until that happens, use the
47
- verified GitHub package URL above rather than a similarly named external package.
48
- Update a registry installation with `npm install --global stackbite@latest`.
49
- For a GitHub URL installation, install the package URL from the newer release.
50
- Each npm package pins one native release; the portable app's own `stackbite update`
51
- does not change the npm package version.
52
-
53
- ## Existing data and processes
54
-
55
- `HELM_STATE_DIR` remains the explicit state override. Existing Windows installed
56
- configuration and Linux managed-launcher state are reused; otherwise state stays
57
- at `%LOCALAPPDATA%\Helm\state` or `${XDG_STATE_HOME:-~/.local/state}/helm`.
58
- Rooms, Seats, authentication, provider configuration, journals and Work Stack
59
- contracts are not renamed or migrated. Binary cache data is separate from this
60
- state. Installing or launching the npm package does not stop an existing backend.
61
- Check the connected server's capabilities before using newly added runtime features.
62
-
63
- The launcher inherits terminal input/output and passes CLI arguments directly to
64
- the native app. It does not insert a second screen renderer or an agent provider.
65
- Global npm installation creates only the `stackbite` command; it does not claim an
66
- external `helm` command or replace independent Kubernetes tooling.
67
-
68
- ## Maintainer release steps
69
-
70
- Build and verify both native platform archives first. Prepare the pinned manifest
71
- from their exact extracted bundles and archive bytes:
72
-
73
- ```sh
74
- python scripts/prepare_npm_release.py --version 0.5.10
75
- npm test
76
- npm pack
77
- ```
78
-
79
- Keep `package.json` and `packaging/npm/release.json` versions equal. Inspect `npm
80
- pack --dry-run` before publishing; the package must include the launcher and pinned
81
- manifest, and exclude native runtimes, installed state, credentials, tests and
82
- development worktrees. Test the packed package with `npx` on both platforms.
83
-
84
- Attach `stackbite-<version>.tgz` and its SHA-256 sidecar to the corresponding GitHub
85
- release in the public distribution repository. Copy only the npm file allowlist to
86
- that repository; never push the private core history, state, personal notes or
87
- credentials. Native archives must retain dependency license notices. Publishing
88
- the short name requires an authenticated owner account:
89
-
90
- ```sh
91
- npm login --registry=https://registry.npmjs.org
92
- npm publish --access public --registry=https://registry.npmjs.org
93
- ```
94
-
95
- Complete any registry-required authentication interactively; do not put credentials
96
- in this repository, prompts or package files. The repository currently specifies
97
- `UNLICENSED` for the JavaScript wrapper instead of inventing a license for the app;
98
- native runtime dependency notices remain in each platform archive.
1
+ # Stackbite through npm and npx
2
+
3
+ Work Stack organizes the work. Stackbite gets it moving.
4
+
5
+ Node.js 20 or newer is required. The portable runtime is available for Windows x64
6
+ and Linux x64 with glibc; macOS, ARM and musl/Alpine builds are not supplied yet.
7
+ Python installation and compilation are unnecessary. Archive extraction requires
8
+ `tar` (included in current Windows and common Linux distributions).
9
+
10
+ ## Install without an account
11
+
12
+ Choose one method. For a single run, use the public npm package:
13
+
14
+ ```sh
15
+ npx --yes stackbite
16
+ ```
17
+
18
+ For a persistent command, install it globally and then run it:
19
+
20
+ ```sh
21
+ npm install --global stackbite
22
+ stackbite
23
+ ```
24
+
25
+ Both methods download the verified native runtime on first execution. Global
26
+ installation alone installs the small launcher; its first `stackbite` invocation
27
+ downloads the runtime. Running both installation methods is unnecessary.
28
+
29
+ Update a registry installation with `npm install --global stackbite@latest`.
30
+
31
+ ## Direct GitHub package
32
+
33
+ Run the package attached to the verified GitHub release:
34
+
35
+ ```sh
36
+ npx --yes https://github.com/Shinick-Han/stackbite/releases/download/v0.5.12/stackbite-0.5.12.tgz
37
+ ```
38
+
39
+ Arguments go after the package URL, for example `--version` or `--status`. For a
40
+ persistent command, install that same package globally:
41
+
42
+ ```sh
43
+ npm install --global https://github.com/Shinick-Han/stackbite/releases/download/v0.5.12/stackbite-0.5.12.tgz
44
+ stackbite
45
+ ```
46
+
47
+ The core source repository remains private at `Shinick-Han/helm`. The separate
48
+ public `Shinick-Han/stackbite` repository distributes only this thin launcher and
49
+ verified compiled runtime releases; installers need no GitHub or npm login.
50
+ Registry-free distribution does not require publishing rights on npm.
51
+ The small JavaScript package downloads the
52
+ exact pinned native release on first execution, verifies its archive and file
53
+ checksums and product identity, then reuses a persistent binary cache. There is no
54
+ postinstall download or provider installation. An offline cached launch is possible;
55
+ the first launch needs access to GitHub release downloads.
56
+
57
+ For a GitHub URL installation, install the package URL from the newer release.
58
+ Each npm package pins one native release; the portable app's own `stackbite update`
59
+ does not change the npm package version.
60
+
61
+ ## Existing data and processes
62
+
63
+ `STACKBITE_STATE_DIR` is the explicit state override. The bundled native runtime
64
+ owns state discovery and migration for both npm and direct launches. It reuses
65
+ existing configured state and keeps live databases, authentication and records
66
+ in place. New state uses `%LOCALAPPDATA%\Stackbite\state` or
67
+ `${XDG_STATE_HOME:-~/.local/state}/stackbite`.
68
+ Rooms, Seats, authentication, provider configuration, journals and Work Stack
69
+ contracts are not renamed or migrated. Binary cache data is separate from this
70
+ state. Installing or launching the npm package does not stop an existing backend.
71
+ Check the connected server's capabilities before using newly added runtime features.
72
+
73
+ The launcher inherits terminal input/output and passes CLI arguments directly to
74
+ the native app. It does not insert a second screen renderer or an agent provider.
75
+ Global npm installation creates only the `stackbite` command; it does not claim an
76
+ external `helm` command or replace independent Kubernetes tooling.
77
+
78
+ ## Maintainer release steps
79
+
80
+ Build and verify both native platform archives first. Prepare the pinned manifest
81
+ from their exact extracted bundles and archive bytes:
82
+
83
+ ```sh
84
+ python scripts/prepare_npm_release.py --version 0.5.12
85
+ npm test
86
+ npm pack
87
+ ```
88
+
89
+ Keep `package.json` and `packaging/npm/release.json` versions equal. Inspect `npm
90
+ pack --dry-run` before publishing; the package must include the launcher and pinned
91
+ manifest, and exclude native runtimes, installed state, credentials, tests and
92
+ development worktrees. Test the packed package with `npx` on both platforms.
93
+
94
+ Attach `stackbite-<version>.tgz` and its SHA-256 sidecar to the corresponding GitHub
95
+ release in the public distribution repository. Copy only the npm file allowlist to
96
+ that repository; never push the private core history, state, personal notes or
97
+ credentials. Native archives must retain dependency license notices. Publishing
98
+ the short name requires an authenticated owner account:
99
+
100
+ ```sh
101
+ npm login --registry=https://registry.npmjs.org
102
+ npm publish --access public --registry=https://registry.npmjs.org
103
+ ```
104
+
105
+ Complete any registry-required authentication interactively; do not put credentials
106
+ in this repository, prompts or package files. The repository currently specifies
107
+ `UNLICENSED` for the JavaScript wrapper instead of inventing a license for the app;
108
+ native runtime dependency notices remain in each platform archive.
package/package.json CHANGED
@@ -1,12 +1,25 @@
1
1
  {
2
2
  "name": "stackbite",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "Verified native Stackbite CLI bootstrapper",
5
5
  "license": "UNLICENSED",
6
- "engines": { "node": ">=20" },
7
- "bin": { "stackbite": "packaging/npm/bin/stackbite.js" },
8
- "files": ["packaging/npm/bin/", "packaging/npm/lib/", "packaging/npm/release.json"],
9
- "scripts": { "test": "node --test packaging/npm/test/*.test.js" },
10
- "repository": { "type": "git", "url": "https://github.com/Shinick-Han/stackbite.git" },
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "bin": {
10
+ "stackbite": "packaging/npm/bin/stackbite.js"
11
+ },
12
+ "files": [
13
+ "packaging/npm/bin/",
14
+ "packaging/npm/lib/",
15
+ "packaging/npm/release.json"
16
+ ],
17
+ "scripts": {
18
+ "test": "node --test packaging/npm/test/*.test.js"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/Shinick-Han/stackbite.git"
23
+ },
11
24
  "homepage": "https://github.com/Shinick-Han/stackbite"
12
25
  }
@@ -6,7 +6,7 @@ const { createHash } = require('node:crypto');
6
6
  const { LIMITS, fail, memberPath, inside, hashFile, readSmall, exists, safeDirectory } = require('./common');
7
7
  const { download } = require('./download');
8
8
  const { extractArchive } = require('./archive');
9
- const { stateDirectory, cacheDirectory } = require('./state');
9
+ const { cacheDirectory } = require('./cache');
10
10
  const packageVersion = require('../../../package.json').version;
11
11
 
12
12
  function releaseSpec(manifest, platform = process.platform, arch = process.arch, runtime = {}) {
@@ -142,9 +142,9 @@ async function main(argv = process.argv.slice(2), options = {}) {
142
142
  const platform = options.platform || process.platform;
143
143
  const environment = options.environment || process.env;
144
144
  const spec = releaseSpec(manifest, platform, options.arch || process.arch, options);
145
- const state = await stateDirectory(environment, platform);
146
145
  const installed = await ensureBinary(spec, { ...options, cache: options.cache || cacheDirectory(environment, platform) });
147
- return launchNative(installed.executable, argv, { ...environment, HELM_HOME: path.resolve(installed.home),
148
- HELM_STATE_DIR: state }, options);
146
+ // The bundled runtime owns state discovery and migration for every entrypoint.
147
+ return launchNative(installed.executable, argv, { ...environment,
148
+ STACKBITE_HOME: path.resolve(installed.home) }, options);
149
149
  }
150
150
  module.exports = { releaseSpec, validatePayload, ensureBinary, launchNative, main };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+ const path = require('node:path');
3
+ const os = require('node:os');
4
+
5
+ function cacheDirectory(environment = process.env, platform = process.platform, home = os.homedir()) {
6
+ return platform === 'win32'
7
+ ? path.resolve(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Stackbite', 'cache')
8
+ : path.resolve(environment.XDG_CACHE_HOME || path.join(environment.HOME || home, '.cache'), 'stackbite');
9
+ }
10
+ module.exports = { cacheDirectory };
@@ -1,26 +1,26 @@
1
1
  {
2
2
  "schema": 1,
3
- "version": "0.5.10",
4
- "commit": "5af83c540e88354a05caa789bb64a663580d5a50",
3
+ "version": "0.5.12",
4
+ "commit": "6a3bc9c4ab83a7ba717948315b6c2dfafe11c739",
5
5
  "repository": "Shinick-Han/stackbite",
6
6
  "platforms": {
7
7
  "win32-x64": {
8
- "asset": "stackbite-0.5.10-windows-x64-5af83c540e88.zip",
9
- "url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.10/stackbite-0.5.10-windows-x64-5af83c540e88.zip",
10
- "sha256": "39308402c837ab602266c3dad0d335c85b0c704773ffcd76c84e0c298d3501d7",
11
- "size": 43596699,
12
- "root": "stackbite-0.5.10-windows-x64-5af83c540e88",
13
- "manifestSha256": "0da0c2d439ae7f4e1d783eb6ebfb19046cbf572b11d31b2e79911b93a85b1fb3",
14
- "metadataSha256": "376b458296be12e979c7548bf60bf51aa15c21229f4821889096161c05e31d50"
8
+ "asset": "stackbite-0.5.12-windows-x64-6a3bc9c4ab83.zip",
9
+ "url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.12/stackbite-0.5.12-windows-x64-6a3bc9c4ab83.zip",
10
+ "sha256": "d0b04507f138b6932073d4ef8352defebe9d60155c7c31233d3d83aa98d6ab02",
11
+ "size": 35322915,
12
+ "root": "stackbite-0.5.12-windows-x64-6a3bc9c4ab83",
13
+ "manifestSha256": "5c73caf3a814d9eda5a91eabb617dca72f4d1ea8d9755152cdea7dde82e946d4",
14
+ "metadataSha256": "5888357f7e9419bb3bfb89536794e35c52a45134df039f197c35d38ba660e349"
15
15
  },
16
16
  "linux-x64": {
17
- "asset": "stackbite-0.5.10-linux-x86_64-5af83c540e88.tar.gz",
18
- "url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.10/stackbite-0.5.10-linux-x86_64-5af83c540e88.tar.gz",
19
- "sha256": "f997ee78358e5d6ce114fc74a57dc24d0ed65a62a2fd50235eb7de6fdad0f259",
20
- "size": 23795505,
21
- "root": "stackbite-0.5.10-linux-x86_64-5af83c540e88",
22
- "manifestSha256": "fe1f7db461651d825a051e34eea4be85decf30e3daa3d74bb18553b4b7f0e36a",
23
- "metadataSha256": "d095f1dbec1b42f0df933f7d1f5c5b98d517b13754dfd5cd552034f47ffc98d1"
17
+ "asset": "stackbite-0.5.12-linux-x86_64-6a3bc9c4ab83.tar.gz",
18
+ "url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.12/stackbite-0.5.12-linux-x86_64-6a3bc9c4ab83.tar.gz",
19
+ "sha256": "f6001db950a45ea69e4148b7bc69dbb388f4629a4a4c5f96a48e94c291e1e212",
20
+ "size": 18488637,
21
+ "root": "stackbite-0.5.12-linux-x86_64-6a3bc9c4ab83",
22
+ "manifestSha256": "49558f4a8af0f87d6c8a60bd7140c7dbf78948903d16b913a4496489ff3df00a",
23
+ "metadataSha256": "6cd01e81aeeef24acf66bcf24bedcc11b800e7a70a1e039376e0e8ab2651b216"
24
24
  }
25
25
  }
26
26
  }
@@ -1,87 +0,0 @@
1
- 'use strict';
2
- const path = require('node:path');
3
- const os = require('node:os');
4
- const fs = require('node:fs/promises');
5
- const { readSmall, fail } = require('./common');
6
-
7
- function shellLiteral(value) {
8
- if (!value.startsWith("'") || !value.endsWith("'")) return null;
9
- const parts = value.slice(1, -1).split("'\\''");
10
- if (parts.some(part => /['\r\n\0]/.test(part))) return null;
11
- return parts.join("'");
12
- }
13
- function managedState(content) {
14
- const lines = content.split('\n');
15
- if (lines.length !== 8 || lines[0] !== '#!/bin/sh' || !/^# (Helm|Stackbite) managed launcher$/.test(lines[1])
16
- || lines[2] !== 'if [ -z "${HELM_STATE_DIR:-}" ]; then' || lines[4] !== ' export HELM_STATE_DIR'
17
- || lines[5] !== 'fi' || lines[7] !== '' || !lines[3].startsWith(' HELM_STATE_DIR=')
18
- || !lines[6].startsWith('exec ') || !lines[6].endsWith(' "$@"')) return null;
19
- const state = shellLiteral(lines[3].slice(' HELM_STATE_DIR='.length));
20
- const image = shellLiteral(lines[6].slice(5, -5));
21
- if (!state || !path.isAbsolute(state) || !image || !path.isAbsolute(image)
22
- || !['stackbite', 'helm'].includes(path.basename(image))) return null;
23
- return state;
24
- }
25
- function windowsManagedState(content, appRoot) {
26
- // Exact portable-installer grammar, including the original unmarked Helm
27
- // launcher. Values are read literally; no CMD expansion or execution occurs.
28
- const match = /^@echo off\r?\n(?:rem Stackbite managed launcher\r?\n)?setlocal\r?\nset "HELM_HOME=(?<home>[^"%\r\n\0]+)"\r?\nset "HELM_STATE_DIR=(?<state>[^"%\r\n\0]+)"\r?\n"\k<home>\\(?:stackbite|helm)\.exe" %\*\r?\nexit \/b %errorlevel%\r?\n$/.exec(content);
29
- if (!match || !path.win32.isAbsolute(match.groups.home) || !path.win32.isAbsolute(match.groups.state)) return null;
30
- const relative = path.win32.relative(appRoot.toLowerCase(), path.win32.normalize(match.groups.home).toLowerCase());
31
- if (!relative || relative === '..' || relative.startsWith('..\\') || path.win32.isAbsolute(relative)) return null;
32
- return match.groups.state;
33
- }
34
- async function launcherText(filename) {
35
- try {
36
- const info = await fs.lstat(filename);
37
- if (!info.isFile() || info.isSymbolicLink() || info.size > 16384) return null;
38
- const bytes = await readSmall(filename, 16384);
39
- const content = bytes.toString('utf8');
40
- if (content.includes('\uFFFD') && /^@echo off\r?\n(?:rem Stackbite managed launcher\r?\n)?setlocal\r?\nset "HELM_HOME=/.test(content)) {
41
- fail('Existing managed Windows launcher is not UTF-8; set HELM_STATE_DIR explicitly to preserve its state.');
42
- }
43
- return content;
44
- } catch (error) { if (error.code === 'ENOENT') return null; throw error; }
45
- }
46
- async function stateDirectory(environment = process.env, platform = process.platform, home = os.homedir()) {
47
- if (environment.HELM_STATE_DIR) return path.resolve(environment.HELM_STATE_DIR);
48
- if (platform === 'win32') {
49
- const local = environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
50
- for (const name of ['default-install.json', 'install.json']) {
51
- let bytes;
52
- try { bytes = await readSmall(path.join(local, 'Helm', name), 16384); }
53
- catch (error) { if (error.code === 'ENOENT') continue; throw error; }
54
- let config;
55
- try { config = JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/, '')); } catch { fail('Existing Helm install configuration is invalid'); }
56
- if (config && typeof config.state === 'string' && config.state && !/[\0\r\n]/.test(config.state)) return path.resolve(config.state);
57
- }
58
- const profile = environment.USERPROFILE || home;
59
- const appRoot = path.resolve(local, 'Helm', 'app');
60
- for (const name of ['stackbite.cmd', 'stackbite-portable.cmd', 'helm.cmd', 'helm-portable.cmd']) {
61
- const content = await launcherText(path.join(profile, '.local', 'bin', name));
62
- const state = content === null ? null : windowsManagedState(content, appRoot);
63
- if (state) return path.resolve(state);
64
- }
65
- return path.resolve(local, 'Helm', 'state');
66
- }
67
- const userHome = environment.HOME || home;
68
- for (const name of ['stackbite', 'helm']) {
69
- let bytes;
70
- try {
71
- const launcher = path.join(userHome, '.local', 'bin', name);
72
- const info = await fs.lstat(launcher);
73
- if (!info.isFile() || info.isSymbolicLink() || info.size > 16384) continue;
74
- bytes = await readSmall(launcher, 16384);
75
- }
76
- catch (error) { if (error.code === 'ENOENT') continue; throw error; }
77
- const state = managedState(bytes.toString('utf8'));
78
- if (state) return path.resolve(state);
79
- }
80
- return path.resolve(environment.XDG_STATE_HOME || path.join(userHome, '.local', 'state'), 'helm');
81
- }
82
- function cacheDirectory(environment = process.env, platform = process.platform, home = os.homedir()) {
83
- return platform === 'win32'
84
- ? path.resolve(environment.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Stackbite', 'cache')
85
- : path.resolve(environment.XDG_CACHE_HOME || path.join(environment.HOME || home, '.cache'), 'stackbite');
86
- }
87
- module.exports = { shellLiteral, managedState, windowsManagedState, stateDirectory, cacheDirectory };