@pellux/goodvibes-tui 0.19.90 → 0.19.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/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/bin/goodvibes +22 -38
- package/bin/goodvibes-daemon +22 -38
- package/bin/launcher-support.js +141 -0
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.19.92] — 2026-05-10
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
- Updated `@pellux/goodvibes-sdk` to `0.33.24` for the companion-chat
|
|
11
|
+
tool-loop finalization fix used by Home Assistant Assist conversations.
|
|
12
|
+
|
|
13
|
+
## [0.19.91] — 2026-05-10
|
|
14
|
+
|
|
15
|
+
### Changes
|
|
16
|
+
- 8f70c74e fix: install release binaries from global launchers
|
|
17
|
+
|
|
7
18
|
## [0.19.90] — 2026-05-10
|
|
8
19
|
|
|
9
20
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/mgd34msu/goodvibes-tui)
|
|
6
6
|
|
|
7
7
|
A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
|
|
8
8
|
|
package/bin/goodvibes
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { accessSync, constants } from 'node:fs';
|
|
3
2
|
import { dirname, join } from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import {
|
|
6
|
+
ensureVendoredBinary,
|
|
7
|
+
isExecutable,
|
|
8
|
+
isSourceCheckout,
|
|
9
|
+
resolveArtifactName,
|
|
10
|
+
run,
|
|
11
|
+
supportedTargetsText,
|
|
12
|
+
} from './launcher-support.js';
|
|
6
13
|
|
|
7
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
15
|
const packageRoot = join(__dirname, '..');
|
|
@@ -13,32 +20,7 @@ if (process.platform === 'win32') {
|
|
|
13
20
|
process.exit(1);
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
if (platform === 'linux' && arch === 'x64') return 'goodvibes-linux-x64';
|
|
18
|
-
if (platform === 'linux' && arch === 'arm64') return 'goodvibes-linux-arm64';
|
|
19
|
-
if (platform === 'darwin' && arch === 'x64') return 'goodvibes-macos-x64';
|
|
20
|
-
if (platform === 'darwin' && arch === 'arm64') return 'goodvibes-macos-arm64';
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isExecutable(path) {
|
|
25
|
-
try {
|
|
26
|
-
accessSync(path, constants.X_OK);
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function run(command, args) {
|
|
34
|
-
const child = spawnSync(command, args, { stdio: 'inherit' });
|
|
35
|
-
if (child.error) {
|
|
36
|
-
throw child.error;
|
|
37
|
-
}
|
|
38
|
-
process.exit(child.status ?? 1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const artifactName = resolveArtifactName(process.platform, process.arch);
|
|
23
|
+
const artifactName = resolveArtifactName('app', process.platform, process.arch);
|
|
42
24
|
const localPlatformBuild = artifactName ? join(packageRoot, 'dist', artifactName) : null;
|
|
43
25
|
const localBuild = join(packageRoot, 'dist', 'goodvibes');
|
|
44
26
|
const vendoredBinary = artifactName ? join(packageRoot, 'vendor', artifactName) : null;
|
|
@@ -55,22 +37,24 @@ if (vendoredBinary && isExecutable(vendoredBinary)) {
|
|
|
55
37
|
run(vendoredBinary, process.argv.slice(2));
|
|
56
38
|
}
|
|
57
39
|
|
|
40
|
+
if (artifactName) {
|
|
41
|
+
try {
|
|
42
|
+
const installedBinary = await ensureVendoredBinary({ packageRoot, artifactName });
|
|
43
|
+
run(installedBinary, process.argv.slice(2));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`goodvibes: failed to install release binary: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
58
49
|
const bunProbe = spawnSync('bun', ['--version'], { stdio: 'ignore' });
|
|
59
|
-
if (bunProbe.status === 0) {
|
|
50
|
+
if (bunProbe.status === 0 && isSourceCheckout(packageRoot)) {
|
|
60
51
|
run('bun', [join(packageRoot, 'src', 'main.ts'), ...process.argv.slice(2)]);
|
|
61
52
|
}
|
|
62
53
|
|
|
63
|
-
const supported = [
|
|
64
|
-
'linux-x64',
|
|
65
|
-
'linux-arm64',
|
|
66
|
-
'darwin-x64',
|
|
67
|
-
'darwin-arm64',
|
|
68
|
-
].join(', ');
|
|
69
|
-
|
|
70
54
|
console.error('goodvibes: no runnable binary is available.');
|
|
71
55
|
console.error(`platform: ${process.platform}-${process.arch}`);
|
|
72
|
-
console.error(`supported prebuilt targets: ${
|
|
56
|
+
console.error(`supported prebuilt targets: ${supportedTargetsText()}`);
|
|
73
57
|
console.error('Either:');
|
|
74
|
-
console.error(' 1.
|
|
75
|
-
console.error(' 2.
|
|
58
|
+
console.error(' 1. check network access to the GitHub release assets, or');
|
|
59
|
+
console.error(' 2. build locally with `bun run build` in a source checkout.');
|
|
76
60
|
process.exit(1);
|
package/bin/goodvibes-daemon
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { accessSync, constants } from 'node:fs';
|
|
3
2
|
import { dirname, join } from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import {
|
|
6
|
+
ensureVendoredBinary,
|
|
7
|
+
isExecutable,
|
|
8
|
+
isSourceCheckout,
|
|
9
|
+
resolveArtifactName,
|
|
10
|
+
run,
|
|
11
|
+
supportedTargetsText,
|
|
12
|
+
} from './launcher-support.js';
|
|
6
13
|
|
|
7
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
15
|
const packageRoot = join(__dirname, '..');
|
|
@@ -13,32 +20,7 @@ if (process.platform === 'win32') {
|
|
|
13
20
|
process.exit(1);
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
if (platform === 'linux' && arch === 'x64') return 'goodvibes-daemon-linux-x64';
|
|
18
|
-
if (platform === 'linux' && arch === 'arm64') return 'goodvibes-daemon-linux-arm64';
|
|
19
|
-
if (platform === 'darwin' && arch === 'x64') return 'goodvibes-daemon-macos-x64';
|
|
20
|
-
if (platform === 'darwin' && arch === 'arm64') return 'goodvibes-daemon-macos-arm64';
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isExecutable(path) {
|
|
25
|
-
try {
|
|
26
|
-
accessSync(path, constants.X_OK);
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function run(command, args) {
|
|
34
|
-
const child = spawnSync(command, args, { stdio: 'inherit' });
|
|
35
|
-
if (child.error) {
|
|
36
|
-
throw child.error;
|
|
37
|
-
}
|
|
38
|
-
process.exit(child.status ?? 1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const artifactName = resolveArtifactName(process.platform, process.arch);
|
|
23
|
+
const artifactName = resolveArtifactName('daemon', process.platform, process.arch);
|
|
42
24
|
const localPlatformBuild = artifactName ? join(packageRoot, 'dist', artifactName) : null;
|
|
43
25
|
const localBuild = join(packageRoot, 'dist', 'goodvibes-daemon');
|
|
44
26
|
const vendoredBinary = artifactName ? join(packageRoot, 'vendor', artifactName) : null;
|
|
@@ -55,22 +37,24 @@ if (vendoredBinary && isExecutable(vendoredBinary)) {
|
|
|
55
37
|
run(vendoredBinary, process.argv.slice(2));
|
|
56
38
|
}
|
|
57
39
|
|
|
40
|
+
if (artifactName) {
|
|
41
|
+
try {
|
|
42
|
+
const installedBinary = await ensureVendoredBinary({ packageRoot, artifactName });
|
|
43
|
+
run(installedBinary, process.argv.slice(2));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`goodvibes-daemon: failed to install release binary: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
58
49
|
const bunProbe = spawnSync('bun', ['--version'], { stdio: 'ignore' });
|
|
59
|
-
if (bunProbe.status === 0) {
|
|
50
|
+
if (bunProbe.status === 0 && isSourceCheckout(packageRoot)) {
|
|
60
51
|
run('bun', [join(packageRoot, 'src', 'daemon', 'cli.ts'), ...process.argv.slice(2)]);
|
|
61
52
|
}
|
|
62
53
|
|
|
63
|
-
const supported = [
|
|
64
|
-
'linux-x64',
|
|
65
|
-
'linux-arm64',
|
|
66
|
-
'darwin-x64',
|
|
67
|
-
'darwin-arm64',
|
|
68
|
-
].join(', ');
|
|
69
|
-
|
|
70
54
|
console.error('goodvibes-daemon: no runnable binary is available.');
|
|
71
55
|
console.error(`platform: ${process.platform}-${process.arch}`);
|
|
72
|
-
console.error(`supported prebuilt targets: ${
|
|
56
|
+
console.error(`supported prebuilt targets: ${supportedTargetsText()}`);
|
|
73
57
|
console.error('Either:');
|
|
74
|
-
console.error(' 1.
|
|
75
|
-
console.error(' 2.
|
|
58
|
+
console.error(' 1. check network access to the GitHub release assets, or');
|
|
59
|
+
console.error(' 2. build locally with `bun run build:daemon:<platform>` in a source checkout.');
|
|
76
60
|
process.exit(1);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { accessSync, chmodSync, constants, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const SUPPORTED_TARGETS = ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64'];
|
|
7
|
+
|
|
8
|
+
export function isExecutable(path) {
|
|
9
|
+
try {
|
|
10
|
+
accessSync(path, constants.X_OK);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function run(command, args) {
|
|
18
|
+
const child = spawnSync(command, args, { stdio: 'inherit' });
|
|
19
|
+
if (child.error) {
|
|
20
|
+
throw child.error;
|
|
21
|
+
}
|
|
22
|
+
process.exit(child.status ?? 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isSourceCheckout(packageRoot) {
|
|
26
|
+
return isExecutable(join(packageRoot, 'node_modules', '.bin', 'bun')) ||
|
|
27
|
+
isExecutable(join(packageRoot, 'node_modules', '.bin', 'tsc')) ||
|
|
28
|
+
fileExists(join(packageRoot, 'tsconfig.json'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function supportedTargetsText() {
|
|
32
|
+
return SUPPORTED_TARGETS.join(', ');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveArtifactName(kind, platform, arch) {
|
|
36
|
+
const prefix = kind === 'daemon' ? 'goodvibes-daemon' : 'goodvibes';
|
|
37
|
+
if (platform === 'linux' && arch === 'x64') return `${prefix}-linux-x64`;
|
|
38
|
+
if (platform === 'linux' && arch === 'arm64') return `${prefix}-linux-arm64`;
|
|
39
|
+
if (platform === 'darwin' && arch === 'x64') return `${prefix}-macos-x64`;
|
|
40
|
+
if (platform === 'darwin' && arch === 'arm64') return `${prefix}-macos-arm64`;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function ensureVendoredBinary({ packageRoot, artifactName }) {
|
|
45
|
+
const vendorDir = join(packageRoot, 'vendor');
|
|
46
|
+
const destination = join(vendorDir, artifactName);
|
|
47
|
+
if (isExecutable(destination)) {
|
|
48
|
+
return destination;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'));
|
|
52
|
+
const releaseBaseUrl =
|
|
53
|
+
process.env.GOODVIBES_RELEASE_BASE_URL?.trim() ||
|
|
54
|
+
`${resolveRepositoryBaseUrl(pkg)}/releases/download/v${pkg.version}`;
|
|
55
|
+
|
|
56
|
+
mkdirSync(vendorDir, { recursive: true });
|
|
57
|
+
|
|
58
|
+
const checksumText = await downloadText(`${releaseBaseUrl}/SHA256SUMS.txt`);
|
|
59
|
+
writeFileSync(join(vendorDir, 'SHA256SUMS.txt'), checksumText);
|
|
60
|
+
const checksums = parseChecksumFile(checksumText);
|
|
61
|
+
|
|
62
|
+
const tempDestination = `${destination}.download`;
|
|
63
|
+
rmSync(tempDestination, { force: true });
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const binary = await downloadBuffer(`${releaseBaseUrl}/${artifactName}`);
|
|
67
|
+
const actual = sha256(binary);
|
|
68
|
+
const expected = checksums.get(artifactName);
|
|
69
|
+
if (expected && expected !== actual) {
|
|
70
|
+
throw new Error(`checksum mismatch for ${artifactName}: expected ${expected}, got ${actual}`);
|
|
71
|
+
}
|
|
72
|
+
writeFileSync(tempDestination, binary);
|
|
73
|
+
prepareBinary(tempDestination);
|
|
74
|
+
rmSync(destination, { force: true });
|
|
75
|
+
writeFileSync(destination, readFileSync(tempDestination));
|
|
76
|
+
prepareBinary(destination);
|
|
77
|
+
} finally {
|
|
78
|
+
rmSync(tempDestination, { force: true });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return destination;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function fileExists(path) {
|
|
85
|
+
try {
|
|
86
|
+
accessSync(path, constants.F_OK);
|
|
87
|
+
return true;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function prepareBinary(path) {
|
|
94
|
+
if (process.platform !== 'win32') {
|
|
95
|
+
chmodSync(path, 0o755);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resolveRepositoryBaseUrl(pkg) {
|
|
100
|
+
const repositoryUrl = typeof pkg.repository?.url === 'string' ? pkg.repository.url : '';
|
|
101
|
+
const normalized = repositoryUrl
|
|
102
|
+
.replace(/^git\+/, '')
|
|
103
|
+
.replace(/\.git$/, '')
|
|
104
|
+
.replace(/^git@github\.com:/, 'https://github.com/');
|
|
105
|
+
if (!normalized.startsWith('https://github.com/')) {
|
|
106
|
+
throw new Error(`unsupported repository URL for binary downloads: ${repositoryUrl || '(missing)'}`);
|
|
107
|
+
}
|
|
108
|
+
return normalized;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function downloadText(url) {
|
|
112
|
+
const response = await fetch(url);
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error(`download failed (${response.status}) for ${url}`);
|
|
115
|
+
}
|
|
116
|
+
return await response.text();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function downloadBuffer(url) {
|
|
120
|
+
const response = await fetch(url);
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw new Error(`download failed (${response.status}) for ${url}`);
|
|
123
|
+
}
|
|
124
|
+
return Buffer.from(await response.arrayBuffer());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sha256(buffer) {
|
|
128
|
+
return createHash('sha256').update(buffer).digest('hex');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseChecksumFile(contents) {
|
|
132
|
+
const checksums = new Map();
|
|
133
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
134
|
+
const line = rawLine.trim();
|
|
135
|
+
if (!line) continue;
|
|
136
|
+
const match = line.match(/^([a-f0-9]{64})\s+\*?(.+)$/i);
|
|
137
|
+
if (!match) continue;
|
|
138
|
+
checksums.set(match[2], match[1].toLowerCase());
|
|
139
|
+
}
|
|
140
|
+
return checksums;
|
|
141
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.92",
|
|
4
4
|
"description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.ts",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"@anthropic-ai/vertex-sdk": "^0.16.0",
|
|
98
98
|
"@ast-grep/napi": "^0.42.0",
|
|
99
99
|
"@aws/bedrock-token-generator": "^1.1.0",
|
|
100
|
-
"@pellux/goodvibes-sdk": "0.33.
|
|
100
|
+
"@pellux/goodvibes-sdk": "0.33.24",
|
|
101
101
|
"bash-language-server": "^5.6.0",
|
|
102
102
|
"fuse.js": "^7.1.0",
|
|
103
103
|
"graphql": "^16.13.2",
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.19.
|
|
9
|
+
let _version = '0.19.92';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|