workbite 0.1.8 → 0.1.10
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 +10 -12
- package/lib/boot-provenance.js +54 -0
- package/lib/runtime.js +67 -22
- package/package.json +3 -2
- package/release.json +15 -8
package/README.md
CHANGED
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
WorkBite installs and opens **Work Stack** (organizes the work) and
|
|
4
4
|
**StackBite** (gets it moving) as one native suite.
|
|
5
5
|
|
|
6
|
-
Launcher 0.1.
|
|
7
|
-
opens the familiar TypeScript Work Stack UI by default, which
|
|
8
|
-
`9 Execution` in the same window: select a Task, start its agent, and see
|
|
9
|
-
control its native terminal without leaving the UI. New in 0.1.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
hide the side menu in Execution, and typed, retained StackBite launch-refusal
|
|
13
|
-
diagnostics. The unified Rust UI stays
|
|
6
|
+
Launcher 0.1.10 (native bundle 0.1.10) supports **Windows x64 and Linux x64**
|
|
7
|
+
(glibc 2.28+). It opens the familiar TypeScript Work Stack UI by default, which
|
|
8
|
+
has `9 Execution` in the same window: select a Task, start its agent, and see
|
|
9
|
+
and control its native terminal without leaving the UI. New in 0.1.10: Cursor
|
|
10
|
+
CLI as a raw native provider in opted-in Rooms (you approve its trust; its
|
|
11
|
+
lifecycle/resume is not verified yet). The unified Rust UI stays
|
|
14
12
|
an explicit alternative per open: `workbite open --frontend rust` (or bare
|
|
15
13
|
`workbite --frontend rust`). Either frontend reuses the same owner and
|
|
16
14
|
StackBite backend; the choice is not saved. Leaving the view or closing the UI
|
|
17
15
|
keeps the agent running, and Task completion stays an explicit action.
|
|
18
16
|
|
|
19
|
-
Linux x64
|
|
20
|
-
|
|
21
|
-
not
|
|
17
|
+
Linux x64 support is verified for the glibc 2.28 ELF build and the POSIX PTY
|
|
18
|
+
in an AlmaLinux 8 userspace; real AI agent turns on Linux, a booted AlmaLinux 8
|
|
19
|
+
kernel and SSH/VM use are not yet proven. Updating from the 0.1.4 native bundle requires an
|
|
22
20
|
operator handover of existing backend processes; the launcher does not stop
|
|
23
21
|
them or clear startup records automatically.
|
|
24
22
|
|
|
@@ -30,7 +28,7 @@ workbite # afterwards (TypeScr
|
|
|
30
28
|
workbite --frontend rust # unified Rust UI with 9 Execution
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
Node.js 20+ on Windows x64
|
|
31
|
+
Node.js 20+ on Windows x64 or Linux x64 (glibc 2.28+).
|
|
34
32
|
|
|
35
33
|
- `workbite install` downloads the pinned bundle for your platform from
|
|
36
34
|
`github.com/Shinick-Han/WorkBite-releases`, verifies its size and SHA-256 and
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('node:fs/promises');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { execFile } = require('node:child_process');
|
|
5
|
+
// WMI LastBootUpTime is the OS boot time, not wall-clock minus uptime:
|
|
6
|
+
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-operatingsystem
|
|
7
|
+
// Use the framework WMI API directly, with no profile, PATH or module lookup.
|
|
8
|
+
const SCRIPT = String.raw`$ErrorActionPreference='Stop'
|
|
9
|
+
Add-Type -AssemblyName System.Management
|
|
10
|
+
$q = [System.Management.ManagementObjectSearcher]::new('SELECT LastBootUpTime FROM Win32_OperatingSystem')
|
|
11
|
+
try {
|
|
12
|
+
$rows = @($q.Get())
|
|
13
|
+
if ($rows.Count -ne 1) { throw 'ambiguous operating system' }
|
|
14
|
+
$r = $rows[0]
|
|
15
|
+
$t = [System.Management.ManagementDateTimeConverter]::ToDateTime([string]$r.LastBootUpTime).ToUniversalTime().Ticks
|
|
16
|
+
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SOFTWARE\Microsoft\Cryptography')
|
|
17
|
+
try { $hostId = [string]$key.GetValue('MachineGuid') } finally { $key.Dispose() }
|
|
18
|
+
[Console]::Out.Write($hostId.ToLowerInvariant() + ':' + $t.ToString([Globalization.CultureInfo]::InvariantCulture))
|
|
19
|
+
} finally { $q.Dispose() }`;
|
|
20
|
+
function validBoot(value) {
|
|
21
|
+
return value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 3
|
|
22
|
+
&& value.host !== '0'.repeat(32) && value.id !== '00000000-0000-0000-0000-000000000000'
|
|
23
|
+
&& typeof value.host === 'string' && /^[0-9a-f]{32}$/.test(value.host)
|
|
24
|
+
&& typeof value.id === 'string'
|
|
25
|
+
&& ((value.source === 'linux-boot-id' && /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(value.id))
|
|
26
|
+
|| (value.source === 'windows-wmi-boot-time' && /^[1-9][0-9]{17}$/.test(value.id)));
|
|
27
|
+
}
|
|
28
|
+
async function bootIdentity({ platform = process.platform, environment = process.env, execImpl = execFile,
|
|
29
|
+
readImpl = fs.readFile } = {}) {
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
if (platform === 'linux') result = { source: 'linux-boot-id',
|
|
33
|
+
host: (await readImpl('/etc/machine-id', 'utf8')).trim(),
|
|
34
|
+
id: (await readImpl('/proc/sys/kernel/random/boot_id', 'utf8')).trim() };
|
|
35
|
+
else if (platform === 'win32') {
|
|
36
|
+
const root = environment.SystemRoot || environment.SYSTEMROOT || 'C:\\Windows';
|
|
37
|
+
if (!/^[A-Za-z]:\\[^\x00-\x1f"*?<>|]*$/.test(root)) return null;
|
|
38
|
+
const id = await new Promise(resolve => execImpl(path.win32.join(root, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
39
|
+
['-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(SCRIPT, 'utf16le').toString('base64')],
|
|
40
|
+
{ env: { SystemRoot: root }, shell: false, windowsHide: true, timeout: 5000, maxBuffer: 4096, encoding: 'utf8' },
|
|
41
|
+
(error, stdout) => resolve(error ? '' : String(stdout).trim())));
|
|
42
|
+
const [host, ticks, extra] = id.split(':');
|
|
43
|
+
if (extra !== undefined || !/^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(host || '')) return null;
|
|
44
|
+
result = { source: 'windows-wmi-boot-time', host: host.replace(/-/g, ''), id: ticks };
|
|
45
|
+
}
|
|
46
|
+
} catch { return null; }
|
|
47
|
+
return validBoot(result) ? result : null;
|
|
48
|
+
}
|
|
49
|
+
function previousBoot(saved, current) {
|
|
50
|
+
if (!validBoot(saved) || !validBoot(current) || saved.source !== current.source || saved.host !== current.host) return false;
|
|
51
|
+
if (saved.source === 'linux-boot-id') return saved.id !== current.id;
|
|
52
|
+
return BigInt(saved.id) < BigInt(current.id);
|
|
53
|
+
}
|
|
54
|
+
module.exports = { bootIdentity, validBoot, previousBoot };
|
package/lib/runtime.js
CHANGED
|
@@ -12,6 +12,7 @@ const path = require('node:path');
|
|
|
12
12
|
const { spawn, execFile } = require('node:child_process');
|
|
13
13
|
const { randomBytes, randomUUID } = require('node:crypto');
|
|
14
14
|
const { fail, exists, safeDirectory, writeAtomic, readJson, readSmall, sha256, UUID, BACKEND_ID } = require('./common');
|
|
15
|
+
const { bootIdentity, validBoot, previousBoot } = require('./boot-provenance');
|
|
15
16
|
const { BUNDLE_V2 } = require('./install');
|
|
16
17
|
const { protectPrivateDirectories } = require('./private-state');
|
|
17
18
|
|
|
@@ -284,14 +285,17 @@ async function readyRecord(log) {
|
|
|
284
285
|
// Startup intent (private token, launch id, port) is saved before any child is
|
|
285
286
|
// spawned, so an attempt that timed out or crashed is never followed by a second
|
|
286
287
|
// manager on the same state: a later open either authenticates that same attempt
|
|
287
|
-
// or refuses.
|
|
288
|
+
// or refuses. Proven previous-boot records are archived; nothing is stopped automatically.
|
|
288
289
|
const RECOVERY = 'WorkBite never starts a second one on the same state. If it is still starting, open again shortly; '
|
|
289
290
|
+ 'if you have confirmed no process from this state is running, move that record aside and open again.';
|
|
290
291
|
const LAUNCH = /^[0-9a-f]{16}$/;
|
|
291
292
|
// Distinct from every JSON value (including null): only an absent file is "missing".
|
|
292
293
|
const MISSING = Symbol('missing record');
|
|
293
294
|
async function savedRecord(file, what) {
|
|
294
|
-
try {
|
|
295
|
+
try {
|
|
296
|
+
const bytes = await readSmall(file, 65536);
|
|
297
|
+
return { record: JSON.parse(bytes.toString('utf8')), bytes };
|
|
298
|
+
} catch (error) {
|
|
295
299
|
if (error.code === 'ENOENT') return MISSING;
|
|
296
300
|
fail(`The ${what} record ${file} is unreadable; WorkBite does not replace it. ${RECOVERY}`);
|
|
297
301
|
}
|
|
@@ -300,6 +304,31 @@ const isRecord = value => value !== null && typeof value === 'object' && !Array.
|
|
|
300
304
|
&& (value.state === 'starting' || value.state === 'ready') && typeof value.launch === 'string' && LAUNCH.test(value.launch);
|
|
301
305
|
const recordBytes = value => Buffer.from(JSON.stringify(value, null, 2) + '\n');
|
|
302
306
|
|
|
307
|
+
// Called under open.lock. Compare the exact bytes, retain a private backup before
|
|
308
|
+
// renaming the record, and refuse if it changed. Logs and native intent stay put.
|
|
309
|
+
async function archivePreviousRecord(file, saved, original, boot) {
|
|
310
|
+
if (!previousBoot(saved.boot, boot)) return false;
|
|
311
|
+
const current = await readSmall(file, 65536);
|
|
312
|
+
if (!current.equals(original)) fail('Startup record changed during recovery; refusing another launch.');
|
|
313
|
+
const backup = `${file}.previous-boot-${randomBytes(12).toString('hex')}`;
|
|
314
|
+
await fs.copyFile(file, backup, fsSync.constants.COPYFILE_EXCL);
|
|
315
|
+
if (!(await readSmall(backup, 65536)).equals(original)
|
|
316
|
+
|| !(await readSmall(file, 65536)).equals(original)) {
|
|
317
|
+
fail('Startup record changed during recovery; backup retained, launch refused.');
|
|
318
|
+
}
|
|
319
|
+
// The rename retains the original record's ACL. A separate backup survives a
|
|
320
|
+
// crash before the new current-boot intent is persisted.
|
|
321
|
+
await fs.rename(file, `${backup}.record`);
|
|
322
|
+
if (!(await readSmall(`${backup}.record`, 65536)).equals(original)) {
|
|
323
|
+
fail('Startup record changed during quarantine; archived evidence retained, launch refused.');
|
|
324
|
+
}
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
function checkBoot(saved, recordPath) {
|
|
328
|
+
if (Object.hasOwn(saved, 'boot') && !validBoot(saved.boot)) {
|
|
329
|
+
fail(`The startup boot provenance in ${recordPath} is invalid; WorkBite does not replace it.`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
303
332
|
// A v2 (execution UI) bundle reuses only processes its own startup intent marked: the owner needs
|
|
304
333
|
// this bundle's execution commands, the StackBite backend its native launch opt-in (read once at
|
|
305
334
|
// startup). A matching token or UID alone is not that proof. Checked from the record before any
|
|
@@ -322,30 +351,38 @@ function requireExecutionMarker(saved, execution, recordPath, what, extra = {})
|
|
|
322
351
|
const executionFields = (execution, extra = {}) => (execution ? { execution_bundle_version: execution.bundleVersion, ...extra } : {});
|
|
323
352
|
const NATIVE_LAUNCH = { native_launch: true };
|
|
324
353
|
|
|
325
|
-
async function ensureEngine({ commands, workspace, uid, layout, environment, spawnImpl, timeout = 60000, execution = null }) {
|
|
354
|
+
async function ensureEngine({ commands, workspace, uid, layout, environment, spawnImpl, timeout = 60000, execution = null, boot = null, identityImpl = engineIdentity }) {
|
|
326
355
|
const key = workspaceKey(workspace);
|
|
327
356
|
const recordPath = path.join(layout.owners, `${key}.json`);
|
|
328
357
|
const logFor = launch => path.join(layout.logs, `workstack-engine-${key}-${launch}.log`);
|
|
329
|
-
const
|
|
358
|
+
const snapshot = await savedRecord(recordPath, 'Work Stack owner');
|
|
359
|
+
const saved = snapshot === MISSING ? MISSING : snapshot.record;
|
|
360
|
+
const original = snapshot === MISSING ? null : snapshot.bytes;
|
|
330
361
|
if (saved !== MISSING) {
|
|
331
362
|
if (!isRecord(saved) || saved.schema !== OWNER_SCHEMA || typeof saved.workspace !== 'string'
|
|
332
363
|
|| workspaceKey(saved.workspace) !== key || saved.workspace_uid !== uid
|
|
333
|
-
|| typeof saved.token !== 'string' || !/^[0-9a-f]{64}$/.test(saved.token)
|
|
364
|
+
|| typeof saved.token !== 'string' || !/^[0-9a-f]{64}$/.test(saved.token)
|
|
365
|
+
|| (saved.state === 'ready' && (typeof saved.endpoint !== 'string' || !LOOPBACK.test(saved.endpoint)))) {
|
|
334
366
|
fail(`The Work Stack owner record ${recordPath} is invalid for this workspace; WorkBite does not replace it. ${RECOVERY}`);
|
|
335
367
|
}
|
|
368
|
+
checkBoot(saved, recordPath);
|
|
336
369
|
requireExecutionMarker(saved, execution, recordPath, 'Work Stack owner');
|
|
337
370
|
let endpoint = saved.state === 'ready' ? saved.endpoint : null;
|
|
338
371
|
if (saved.state === 'starting') {
|
|
339
372
|
const ready = await readyRecord(logFor(saved.launch));
|
|
340
373
|
if (ready?.workspace_uid === uid && ready.protocol_version === 1) endpoint = ready.endpoint;
|
|
341
374
|
}
|
|
342
|
-
if (!await
|
|
343
|
-
|
|
375
|
+
if (!await identityImpl(endpoint, saved.token, uid)) {
|
|
376
|
+
if (!await archivePreviousRecord(recordPath, saved, original, boot))
|
|
377
|
+
fail(`The Work Stack owner recorded in ${recordPath} could not be authenticated (still starting, or stopped). ${RECOVERY}`);
|
|
378
|
+
} else {
|
|
379
|
+
if (saved.state === 'starting' || validBoot(boot))
|
|
380
|
+
await writeAtomic(recordPath, recordBytes({ ...saved, ...(validBoot(boot) ? { boot } : {}), state: 'ready', endpoint }));
|
|
381
|
+
return { endpoint, reused: true, pid: saved.pid };
|
|
344
382
|
}
|
|
345
|
-
if (saved.state === 'starting') await writeAtomic(recordPath, recordBytes({ ...saved, state: 'ready', endpoint }));
|
|
346
|
-
return { endpoint, reused: true, pid: saved.pid };
|
|
347
383
|
}
|
|
348
|
-
|
|
384
|
+
if (!validBoot(boot)) fail('Trusted OS boot provenance is unavailable; no owner was started.');
|
|
385
|
+
const intent = { boot, schema: OWNER_SCHEMA, state: 'starting', workspace, workspace_uid: uid,
|
|
349
386
|
launch: randomBytes(8).toString('hex'), token: randomBytes(32).toString('hex'), ...executionFields(execution) };
|
|
350
387
|
const log = logFor(intent.launch);
|
|
351
388
|
await writeAtomic(recordPath, recordBytes(intent));
|
|
@@ -355,30 +392,37 @@ async function ensureEngine({ commands, workspace, uid, layout, environment, spa
|
|
|
355
392
|
await writeAtomic(recordPath, recordBytes({ ...intent, pid: child.pid }));
|
|
356
393
|
const ready = await waitFor(() => readyRecord(log), exited, timeout, 'Work Stack native owner', log);
|
|
357
394
|
if (ready.workspace_uid !== uid || ready.protocol_version !== 1 || !LOOPBACK.test(ready.endpoint)
|
|
358
|
-
|| !await
|
|
395
|
+
|| !await identityImpl(ready.endpoint, intent.token, uid)) fail(`Work Stack native owner identity was not proven; see ${log}`);
|
|
359
396
|
await writeAtomic(recordPath, recordBytes({ ...intent, pid: child.pid, state: 'ready', endpoint: ready.endpoint }));
|
|
360
397
|
return { endpoint: ready.endpoint, reused: false, pid: child.pid };
|
|
361
398
|
}
|
|
362
399
|
|
|
363
|
-
async function ensureStackbite({ commands, layout, backendId, environment, spawnImpl, timeout = 120000, execution = null }) {
|
|
400
|
+
async function ensureStackbite({ commands, layout, backendId, environment, spawnImpl, timeout = 120000, execution = null, boot = null, identityImpl = stackbiteIdentity }) {
|
|
364
401
|
const recordPath = layout.stackbiteRecord;
|
|
365
402
|
const logFor = launch => path.join(layout.logs, `stackbite-backend-${launch}.log`);
|
|
366
|
-
const
|
|
403
|
+
const snapshot = await savedRecord(recordPath, 'StackBite backend');
|
|
404
|
+
const saved = snapshot === MISSING ? MISSING : snapshot.record;
|
|
405
|
+
const original = snapshot === MISSING ? null : snapshot.bytes;
|
|
367
406
|
if (saved !== MISSING) {
|
|
368
407
|
if (!isRecord(saved) || saved.schema !== STACKBITE_SCHEMA || saved.backend_id !== backendId
|
|
369
408
|
|| typeof saved.url !== 'string' || LOOPBACK.exec(saved.url)?.[1] !== String(saved.port)) {
|
|
370
409
|
fail(`The StackBite backend record ${recordPath} is invalid for this state; WorkBite does not replace it. ${RECOVERY}`);
|
|
371
410
|
}
|
|
411
|
+
checkBoot(saved, recordPath);
|
|
372
412
|
requireExecutionMarker(saved, execution, recordPath, 'StackBite backend', NATIVE_LAUNCH);
|
|
373
|
-
if (!await
|
|
374
|
-
|
|
413
|
+
if (!await identityImpl(saved.url, layout.stackbite, backendId)) {
|
|
414
|
+
if (!await archivePreviousRecord(recordPath, saved, original, boot))
|
|
415
|
+
fail(`The StackBite backend recorded in ${recordPath} could not be authenticated (still starting, or stopped). ${RECOVERY}`);
|
|
416
|
+
} else {
|
|
417
|
+
if (saved.state === 'starting' || validBoot(boot))
|
|
418
|
+
await writeAtomic(recordPath, recordBytes({ ...saved, ...(validBoot(boot) ? { boot } : {}), state: 'ready' }));
|
|
419
|
+
return { url: saved.url, reused: true, pid: saved.pid };
|
|
375
420
|
}
|
|
376
|
-
if (saved.state === 'starting') await writeAtomic(recordPath, recordBytes({ ...saved, state: 'ready' }));
|
|
377
|
-
return { url: saved.url, reused: true, pid: saved.pid };
|
|
378
421
|
}
|
|
422
|
+
if (!validBoot(boot)) fail('Trusted OS boot provenance is unavailable; no backend was started.');
|
|
379
423
|
const port = await freePort();
|
|
380
424
|
const url = `http://127.0.0.1:${port}`;
|
|
381
|
-
const intent = { schema: STACKBITE_SCHEMA, state: 'starting', backend_id: backendId, launch: randomBytes(8).toString('hex'), url, port,
|
|
425
|
+
const intent = { boot, schema: STACKBITE_SCHEMA, state: 'starting', backend_id: backendId, launch: randomBytes(8).toString('hex'), url, port,
|
|
382
426
|
...executionFields(execution, NATIVE_LAUNCH) };
|
|
383
427
|
const log = logFor(intent.launch);
|
|
384
428
|
const home = path.dirname(commands.stackbite[commands.stackbite.length - 1]);
|
|
@@ -395,7 +439,7 @@ async function ensureStackbite({ commands, layout, backendId, environment, spawn
|
|
|
395
439
|
...(execution ? { STACKBITE_WORKBITE_NATIVE_LAUNCH: '1' } : {}) }, home),
|
|
396
440
|
cwd: home, log, spawnImpl });
|
|
397
441
|
await writeAtomic(recordPath, recordBytes({ ...intent, pid: child.pid }));
|
|
398
|
-
await waitFor(() =>
|
|
442
|
+
await waitFor(() => identityImpl(url, layout.stackbite, backendId), exited, timeout, 'StackBite backend', log);
|
|
399
443
|
await writeAtomic(recordPath, recordBytes({ ...intent, pid: child.pid, state: 'ready' }));
|
|
400
444
|
return { url, reused: false, pid: child.pid };
|
|
401
445
|
}
|
|
@@ -555,7 +599,7 @@ function runForeground(command, args, environment, { spawnImpl = spawn, signalSo
|
|
|
555
599
|
// commands maps each bundle executable to its argv prefix; tests substitute fixtures.
|
|
556
600
|
async function openSuite({ installed, commands, workspace, init = false, stateRoot, environment = process.env,
|
|
557
601
|
ui = true, frontend = 'typescript', spawnImpl = spawn, execImpl = execFile, platform = process.platform,
|
|
558
|
-
stdout = process.stdout, timeouts = {} }) {
|
|
602
|
+
stdout = process.stdout, timeouts = {}, bootImpl = bootIdentity }) {
|
|
559
603
|
if (!FRONTENDS.includes(frontend)) fail(`--frontend must be one of: ${FRONTENDS.join(', ')}`);
|
|
560
604
|
if (workspace !== undefined) workspace = checkedWorkspace(workspace);
|
|
561
605
|
else if (init) fail('--init needs --workspace ABS naming the new or empty folder');
|
|
@@ -596,10 +640,11 @@ async function openSuite({ installed, commands, workspace, init = false, stateRo
|
|
|
596
640
|
const uid = init ? await initializeWorkspace(commands, workspace, engineEnvironment) : await readWorkspaceUid(workspace);
|
|
597
641
|
if (remembered && uid !== remembered.uid) fail(`The workspace ${workspace} changed while opening. ${EXPLICIT}`);
|
|
598
642
|
const backendId = await ensureBackendId(layout);
|
|
643
|
+
const boot = await bootImpl({ platform, environment: base });
|
|
599
644
|
const engine = await ensureEngine({ commands, workspace, uid, layout, environment: engineEnvironment, spawnImpl,
|
|
600
|
-
timeout: timeouts.engine, execution });
|
|
645
|
+
timeout: timeouts.engine, execution, boot });
|
|
601
646
|
const stackbite = await ensureStackbite({ commands, layout, backendId, environment: base, spawnImpl,
|
|
602
|
-
timeout: timeouts.stackbite, execution });
|
|
647
|
+
timeout: timeouts.stackbite, execution, boot });
|
|
603
648
|
if (execution) {
|
|
604
649
|
await initializeNativeRuntime({ commands, layout, url: stackbite.url, environment: base, execImpl,
|
|
605
650
|
timeout: timeouts.nativeInit });
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workbite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "WorkBite (Work Stack + StackBite) verified native suite installer and launcher",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20"
|
|
8
8
|
},
|
|
9
9
|
"os": [
|
|
10
|
-
"win32"
|
|
10
|
+
"win32",
|
|
11
|
+
"linux"
|
|
11
12
|
],
|
|
12
13
|
"cpu": [
|
|
13
14
|
"x64"
|
package/release.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "workbite.release.v1",
|
|
3
3
|
"product": "WorkBite",
|
|
4
|
-
"version": "0.1.
|
|
5
|
-
"launcherVersion": "0.1.
|
|
4
|
+
"version": "0.1.10",
|
|
5
|
+
"launcherVersion": "0.1.10",
|
|
6
6
|
"repository": "Shinick-Han/WorkBite-releases",
|
|
7
|
-
"tag": "v0.1.
|
|
7
|
+
"tag": "v0.1.10",
|
|
8
8
|
"platforms": {
|
|
9
9
|
"windows-x64": {
|
|
10
|
-
"asset": "workbite-0.1.
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
13
|
-
"bundleSha256": "
|
|
14
|
-
"url": "https://github.com/Shinick-Han/WorkBite-releases/releases/download/v0.1.
|
|
10
|
+
"asset": "workbite-0.1.10-windows-x64.zip",
|
|
11
|
+
"sha256": "c3d86b7bae3226e5f9c63d70680998ec297253baa94aa1c1f39f887a12eafc9e",
|
|
12
|
+
"size": 97557746,
|
|
13
|
+
"bundleSha256": "1e89d35d4ec3efb816ee9a536fce1de6786c800804c8e03c2b5df3acfdf9399f",
|
|
14
|
+
"url": "https://github.com/Shinick-Han/WorkBite-releases/releases/download/v0.1.10/workbite-0.1.10-windows-x64.zip"
|
|
15
|
+
},
|
|
16
|
+
"linux-x64": {
|
|
17
|
+
"asset": "workbite-0.1.10-linux-x64.tar.gz",
|
|
18
|
+
"sha256": "9297ada2dbc30cc3fc7a14d74d85af0e1b4e49c8e7d6fb7d0bed230a51ca3e63",
|
|
19
|
+
"size": 81924111,
|
|
20
|
+
"bundleSha256": "a02994da01602e36e866411bc81d49632c9285c952c90b5cc50b252880c6123a",
|
|
21
|
+
"url": "https://github.com/Shinick-Han/WorkBite-releases/releases/download/v0.1.10/workbite-0.1.10-linux-x64.tar.gz"
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
24
|
}
|