stackbite 0.5.90 → 0.5.92
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/package.json +1 -1
- package/packaging/npm/lib/bootstrap.js +39 -10
- package/packaging/npm/release.json +23 -23
package/package.json
CHANGED
|
@@ -82,10 +82,10 @@ function boundedRunner(limit) {
|
|
|
82
82
|
}
|
|
83
83
|
return task => new Promise((resolve, reject) => { queue.push({ task, resolve, reject }); next(); });
|
|
84
84
|
}
|
|
85
|
-
// Bounded concurrency overlaps file reads; every
|
|
85
|
+
// Bounded concurrency overlaps selected file reads; every selected member is hashed in full.
|
|
86
86
|
const HASH_CONCURRENCY = 8;
|
|
87
87
|
// Walks the payload; every file or in-root link must resolve to a bounded regular file and pinned members must match.
|
|
88
|
-
async function payloadFiles(root, expected, hashes) {
|
|
88
|
+
async function payloadFiles(root, expected, hashes, includePhone) {
|
|
89
89
|
const actual = new Set(), run = boundedRunner(HASH_CONCURRENCY), checks = [];
|
|
90
90
|
let total = 0, entries = 0;
|
|
91
91
|
async function checkHash(relative, resolved) {
|
|
@@ -93,6 +93,9 @@ async function payloadFiles(root, expected, hashes) {
|
|
|
93
93
|
}
|
|
94
94
|
async function visit(directoryPath) {
|
|
95
95
|
for (const name of await fs.readdir(directoryPath)) {
|
|
96
|
+
// Optional Phone is checked at activation. Do not traverse an unused tree,
|
|
97
|
+
// including a missing or redirected tree; selected runtime links cannot use it.
|
|
98
|
+
if (!includePhone && directoryPath === root && name === 'phone') continue;
|
|
96
99
|
if (++entries > LIMITS.members * 2) fail('Cached release has too many entries');
|
|
97
100
|
const target = path.join(directoryPath, name), relative = path.relative(root, target).split(path.sep).join('/');
|
|
98
101
|
memberPath(relative);
|
|
@@ -101,6 +104,7 @@ async function payloadFiles(root, expected, hashes) {
|
|
|
101
104
|
if (!entry.isFile() && !entry.isSymbolicLink()) fail('Release contains a special file');
|
|
102
105
|
const resolved = await fs.realpath(target);
|
|
103
106
|
if (!inside(root, resolved)) fail('Release link escapes the cache root');
|
|
107
|
+
if (!includePhone && path.relative(root, resolved).split(path.sep)[0] === 'phone') fail('Runtime link resolves into optional Phone payload');
|
|
104
108
|
const resolvedInfo = await fs.lstat(resolved);
|
|
105
109
|
if (!resolvedInfo.isFile() || resolvedInfo.size > LIMITS.member) fail('Release link does not resolve to a bounded regular file');
|
|
106
110
|
total += resolvedInfo.size;
|
|
@@ -137,8 +141,8 @@ async function checkProductIdentity(root, spec, metadata) {
|
|
|
137
141
|
|| ((spec.platform === 'darwin' || value.platform !== undefined) && value.platform !== spec.host)) fail('Release product/version/commit identity mismatch');
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
|
-
async function nativeExecutable(root, spec) {
|
|
141
|
-
const executable = path.join(root, spec.platform === 'win32' ? '
|
|
144
|
+
async function nativeExecutable(root, spec, name = 'stackbite') {
|
|
145
|
+
const executable = path.join(root, name + (spec.platform === 'win32' ? '.exe' : ''));
|
|
142
146
|
const image = await fs.lstat(executable);
|
|
143
147
|
if (!image.isFile() || image.isSymbolicLink()) fail('Native release executable is missing or a link');
|
|
144
148
|
const file = await fs.open(executable, 'r');
|
|
@@ -153,17 +157,40 @@ async function nativeExecutable(root, spec) {
|
|
|
153
157
|
if (spec.platform !== 'win32' && process.platform !== 'win32' && !(image.mode & 0o111)) fail('Native release executable has no execute permission');
|
|
154
158
|
return executable;
|
|
155
159
|
}
|
|
156
|
-
|
|
160
|
+
// The release pins the whole payload before this mapping. Old releases keep
|
|
161
|
+
// their original engine; a present native entry is never silently bypassed.
|
|
162
|
+
async function publicExecutable(root, spec, hashes) {
|
|
163
|
+
const engine = await nativeExecutable(root, spec);
|
|
164
|
+
const name = 'stackbite-launcher' + (spec.platform === 'win32' ? '.exe' : '');
|
|
165
|
+
if (!Object.hasOwn(hashes, name)) {
|
|
166
|
+
if (await exists(path.join(root, name))) fail('Native entry is present without a release pin');
|
|
167
|
+
return engine;
|
|
168
|
+
}
|
|
169
|
+
const manifest = JSON.parse((await readSmall(path.join(root, 'manifest.json'))).toString('utf8'));
|
|
170
|
+
const entry = manifest.binaries?.['stackbite-launcher'], info = entry?.build_info;
|
|
171
|
+
const target = { win32: 'x86_64-pc-windows-msvc', linux: 'x86_64-unknown-linux-gnu', darwin: 'aarch64-apple-darwin' }[spec.platform];
|
|
172
|
+
if (manifest.schema !== 'stackbite-rust-terminal-build-v1' || manifest.product !== 'Stackbite'
|
|
173
|
+
|| manifest.version !== spec.version || manifest.commit !== spec.commit || manifest.target !== target || manifest.protocol !== 1
|
|
174
|
+
|| !entry || Object.keys(entry).sort().join(',') !== 'build_info,path,sha256'
|
|
175
|
+
|| entry.path !== name || entry.sha256 !== hashes[name] || manifest.artifacts?.[name]?.sha256 !== hashes[name]
|
|
176
|
+
|| manifest.artifacts?.[name]?.kind !== 'binary' || !Number.isSafeInteger(manifest.artifacts?.[name]?.size) || manifest.artifacts[name].size < 1
|
|
177
|
+
|| !info || Object.keys(info).sort().join(',') !== 'binary,commit,product,protocol,target,version'
|
|
178
|
+
|| info.product !== 'Stackbite' || info.binary !== 'stackbite-launcher' || info.version !== spec.version
|
|
179
|
+
|| info.commit !== spec.commit || info.target !== target || info.protocol !== 1) fail('Native entry package identity or pin is invalid');
|
|
180
|
+
return nativeExecutable(root, spec, 'stackbite-launcher');
|
|
181
|
+
}
|
|
182
|
+
async function validatePayload(directory, spec, { includePhone = true } = {}) {
|
|
157
183
|
const root = await payloadRoot(directory);
|
|
158
184
|
const { hashes, metadata } = await pinnedDocuments(root, spec);
|
|
159
185
|
const expected = expectedMembers(hashes);
|
|
160
|
-
const
|
|
186
|
+
if (!includePhone) for (const name of expected) if (name.startsWith('phone/')) expected.delete(name);
|
|
187
|
+
const actual = await payloadFiles(root, expected, hashes, includePhone);
|
|
161
188
|
await checkMemberSet(root, spec, hashes, expected, actual);
|
|
162
189
|
await checkProductIdentity(root, spec, metadata);
|
|
163
|
-
return
|
|
190
|
+
return publicExecutable(root, spec, hashes);
|
|
164
191
|
}
|
|
165
192
|
async function ensureBinary(spec, { cache = cacheDirectory(), downloadImpl = download, extractImpl = extractArchive,
|
|
166
|
-
checkOnly = false, reportCached = false, progress = () => {} } = {}) {
|
|
193
|
+
checkOnly = false, reportCached = false, includePhone = false, progress = () => {} } = {}) {
|
|
167
194
|
cache = path.resolve(cache);
|
|
168
195
|
if (checkOnly) {
|
|
169
196
|
if (!await existingCacheDirectory(cache)) fail('Pinned runtime is not cached; run stackbite setup.');
|
|
@@ -173,7 +200,7 @@ async function ensureBinary(spec, { cache = cacheDirectory(), downloadImpl = dow
|
|
|
173
200
|
const target = path.join(cache, `v1-${cacheKey}`);
|
|
174
201
|
if (await exists(target)) {
|
|
175
202
|
if (checkOnly || reportCached) progress('validate');
|
|
176
|
-
const installed = { home: target, executable: await validatePayload(target, spec) };
|
|
203
|
+
const installed = { home: target, executable: await validatePayload(target, spec, { includePhone }) };
|
|
177
204
|
if (checkOnly || reportCached) progress('ready');
|
|
178
205
|
return installed;
|
|
179
206
|
}
|
|
@@ -231,10 +258,12 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
231
258
|
const platform = options.platform || process.platform;
|
|
232
259
|
const environment = options.environment || process.env;
|
|
233
260
|
const spec = releaseSpec(manifest, platform, options.arch || process.arch, options);
|
|
234
|
-
//
|
|
261
|
+
// First install always verifies everything. Cached TUI/runtime starts defer only
|
|
262
|
+
// Phone; explicit phone/setup/removal commands retain the full pinned check.
|
|
235
263
|
const installed = await timing.phase(environment, 'launcher.ensure', () => ensureBinary(spec, { ...options,
|
|
236
264
|
cache: options.cache || cacheDirectory(environment, platform),
|
|
237
265
|
checkOnly: removing || setup?.checkOnly || false, reportCached: Boolean(setup),
|
|
266
|
+
includePhone: removing || Boolean(setup) || argv[0] === 'phone',
|
|
238
267
|
progress: options.progress || stageReporter(options.stderr || process.stderr) }));
|
|
239
268
|
if (setup) return { code: 0, signal: null };
|
|
240
269
|
timing.mark(environment, 'launcher.exec');
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"commit": "
|
|
3
|
+
"version": "0.5.92",
|
|
4
|
+
"commit": "cc476a31b6ff0464f623372fe15fff3ff311d942",
|
|
5
5
|
"repository": "Shinick-Han/stackbite",
|
|
6
6
|
"platforms": {
|
|
7
7
|
"win32-x64": {
|
|
8
|
-
"asset": "stackbite-0.5.
|
|
9
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
10
|
-
"sha256": "
|
|
11
|
-
"size":
|
|
12
|
-
"root": "stackbite-0.5.
|
|
13
|
-
"manifestSha256": "
|
|
14
|
-
"metadataSha256": "
|
|
8
|
+
"asset": "stackbite-0.5.92-windows-x64-cc476a31b6ff.zip",
|
|
9
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.92/stackbite-0.5.92-windows-x64-cc476a31b6ff.zip",
|
|
10
|
+
"sha256": "e27e3f909e93965412cfae83a8eea6a2bdbe49126104f452464a7913f2aa03d9",
|
|
11
|
+
"size": 95304318,
|
|
12
|
+
"root": "stackbite-0.5.92-windows-x64-cc476a31b6ff",
|
|
13
|
+
"manifestSha256": "b784ca757ac89eb62c3226f81e71e5467b99259bfa46f1c785fff8f457b8e152",
|
|
14
|
+
"metadataSha256": "5414656b14f65921506b664123e9b0526cdd33f253c27ef122f0e335d6dda736"
|
|
15
15
|
},
|
|
16
16
|
"linux-x64": {
|
|
17
|
-
"asset": "stackbite-0.5.
|
|
18
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
19
|
-
"sha256": "
|
|
20
|
-
"size":
|
|
21
|
-
"root": "stackbite-0.5.
|
|
22
|
-
"manifestSha256": "
|
|
23
|
-
"metadataSha256": "
|
|
17
|
+
"asset": "stackbite-0.5.92-linux-x86_64-cc476a31b6ff.tar.gz",
|
|
18
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.92/stackbite-0.5.92-linux-x86_64-cc476a31b6ff.tar.gz",
|
|
19
|
+
"sha256": "15b82d52a8e1b1636fb0d69af27c881259a7b8efe3a808789020adbe0090698c",
|
|
20
|
+
"size": 75399110,
|
|
21
|
+
"root": "stackbite-0.5.92-linux-x86_64-cc476a31b6ff",
|
|
22
|
+
"manifestSha256": "ac8ece054b13ab8b8dd4f4994fa18c6aee7de70e0bf5617346de90a3abc320df",
|
|
23
|
+
"metadataSha256": "61c66bab03bd7ee73ca425c814f02740c1f72c0d47391fe85bbb778a81aaf273"
|
|
24
24
|
},
|
|
25
25
|
"darwin-arm64": {
|
|
26
|
-
"asset": "stackbite-0.5.
|
|
27
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
28
|
-
"sha256": "
|
|
29
|
-
"size":
|
|
30
|
-
"root": "stackbite-0.5.
|
|
31
|
-
"manifestSha256": "
|
|
32
|
-
"metadataSha256": "
|
|
26
|
+
"asset": "stackbite-0.5.92-macos-arm64-cc476a31b6ff.tar.gz",
|
|
27
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.92/stackbite-0.5.92-macos-arm64-cc476a31b6ff.tar.gz",
|
|
28
|
+
"sha256": "c12210996e9ad9431f40db11abc57f4e14207285509164577da087b7e368f198",
|
|
29
|
+
"size": 30498979,
|
|
30
|
+
"root": "stackbite-0.5.92-macos-arm64-cc476a31b6ff",
|
|
31
|
+
"manifestSha256": "d4c4d72b78bd456de8fb9623cc8f658d79c0d75724a9250008bf0f2a0df0f025",
|
|
32
|
+
"metadataSha256": "d3d4f7903aab26562b91603c123d4817ebcb8a398e55c8063f756ca06ddf9a37"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|