@pellux/goodvibes-tui 0.19.62 → 0.19.64
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 +39 -0
- package/README.md +7 -4
- package/bin/goodvibes +1 -1
- package/bin/goodvibes-daemon +1 -1
- package/package.json +9 -3
- package/scripts/check-bun.sh +20 -0
- package/scripts/postinstall.js +1 -1
- package/src/cli/help.ts +0 -1
- package/src/cli/package-verification.ts +4 -0
- package/src/cli/service-command.ts +5 -1
- package/src/cli/service-posture.ts +170 -6
- package/src/config/goodvibes-home-audit.ts +465 -0
- package/src/input/feed-context-factory.ts +3 -0
- package/src/input/handler-feed-routes.ts +73 -0
- package/src/input/handler-feed.ts +4 -0
- package/src/input/handler-shortcuts.ts +2 -0
- package/src/input/handler.ts +11 -2
- package/src/main.ts +13 -17
- package/src/panels/file-explorer-panel.ts +8 -0
- package/src/panels/scrollable-list-panel.ts +12 -0
- package/src/panels/types.ts +4 -0
- package/src/renderer/help-overlay.ts +1 -1
- package/src/verification/live-verifier.ts +430 -0
- package/src/verification/verification-ledger.ts +242 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,45 @@ All notable changes to GoodVibes TUI.
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## [0.19.64] — 2026-05-05
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Fixed the packaged platform TUI binaries so the `sqlite-vec` JavaScript wrapper is bundled into the executable instead of being resolved from `/$bunfs/root` at startup.
|
|
15
|
+
- Kept only the platform-native sqlite-vec addon external and shipped beside the binary under `dist/lib/sqlite-vec-<platform>-<arch>/`.
|
|
16
|
+
- Fixed `goodvibes --version` and `goodvibes-daemon --version` for installed wrappers so they report the GoodVibes package version instead of the consuming project's `npm_package_version`.
|
|
17
|
+
- Added release smoke coverage that runs the compiled TUI binary and verifies installed `goodvibes` and `goodvibes-daemon` wrappers so startup/version failures block publication.
|
|
18
|
+
|
|
19
|
+
### Verified
|
|
20
|
+
- `bun run scripts/build.ts --target linux-x64`
|
|
21
|
+
- `bun run smoke:tui`
|
|
22
|
+
- `bun test src/test/cli/help.test.ts src/test/cli/package-verification.test.ts --timeout 30000`
|
|
23
|
+
- `bun run tsc --noEmit --pretty false`
|
|
24
|
+
- `bun run smoke:daemon`
|
|
25
|
+
- local `npm pack` install smoke with `GOODVIBES_ASSET_SOURCE_DIR=dist`
|
|
26
|
+
- `/home/buzzkill/Projects/goodvibes-tui/dist/goodvibes-linux-x64 --version` from `/home/buzzkill/Projects/ttest1`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [0.19.63] — 2026-05-05
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Made the npm registry install path explicitly Bun-first: `bun add -g @pellux/goodvibes-tui` is now the documented global install command, while `npm install -g` requires Bun to already be installed.
|
|
34
|
+
- Switched the published `goodvibes`, `goodvibes-daemon`, and postinstall entrypoints to Bun.
|
|
35
|
+
- Added a preinstall Bun availability check so npm users get a clear error before postinstall runs.
|
|
36
|
+
- Added package verification coverage for Bun shebangs and the Bun preinstall script.
|
|
37
|
+
- Updated service management so daemon services use the global daemon state directory and resolve packaged/vendored daemon binaries before falling back to source or PATH wrappers.
|
|
38
|
+
- Added mouse-wheel routing for scrollable panels such as the file explorer without stealing normal transcript scrolling.
|
|
39
|
+
|
|
40
|
+
### Verified
|
|
41
|
+
- `bun test src/test/cli/package-verification.test.ts src/test/cli/service-posture.test.ts src/test/daemon/service-manager.test.ts src/test/cli-flags.test.ts --timeout 30000`
|
|
42
|
+
- `bun run tsc --noEmit --pretty false`
|
|
43
|
+
- `bun run publish:check`
|
|
44
|
+
- `bun run package:install-check`
|
|
45
|
+
- `bun run build`
|
|
46
|
+
- `git diff --check`
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
11
50
|
## [0.19.62] — 2026-05-05
|
|
12
51
|
|
|
13
52
|
### Changed
|
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
|
|
|
@@ -14,13 +14,15 @@ A terminal-native AI coding, operations, automation, knowledge, and integration
|
|
|
14
14
|
|
|
15
15
|
## Start Here
|
|
16
16
|
|
|
17
|
-
Install from npm:
|
|
17
|
+
GoodVibes is a Bun program. Install Bun first and make sure `bun` is on `PATH`, then install GoodVibes from the npm registry:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
|
|
20
|
+
bun add -g @pellux/goodvibes-tui
|
|
21
21
|
goodvibes
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
`npm install -g @pellux/goodvibes-tui` is also supported, but it does not install Bun for you. The package preinstall check fails with a clear message if `bun` is missing from `PATH`.
|
|
25
|
+
|
|
24
26
|
Or run from source:
|
|
25
27
|
|
|
26
28
|
```sh
|
|
@@ -41,7 +43,8 @@ Common entrypoints:
|
|
|
41
43
|
Release distribution:
|
|
42
44
|
|
|
43
45
|
- GitHub Releases are the primary distribution path for compiled binaries
|
|
44
|
-
- `
|
|
46
|
+
- `bun add -g @pellux/goodvibes-tui` is the recommended global install path; the package is hosted on the npm registry and Bun installs from that registry directly
|
|
47
|
+
- `npm install -g @pellux/goodvibes-tui` is supported on Linux, macOS, and WSL when Bun is already installed; the preinstall check verifies Bun, and the install script downloads the matching TUI and daemon binaries for the current platform
|
|
45
48
|
- native Windows is not supported; use WSL on Windows
|
|
46
49
|
|
|
47
50
|
Common paths:
|
package/bin/goodvibes
CHANGED
package/bin/goodvibes-daemon
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.64",
|
|
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",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"!src/test",
|
|
18
18
|
"!src/**/*.test.ts",
|
|
19
19
|
"!src/**/__tests__",
|
|
20
|
+
"scripts/check-bun.sh",
|
|
20
21
|
"scripts/postinstall.js",
|
|
21
22
|
"README.md",
|
|
22
23
|
"CHANGELOG.md",
|
|
@@ -37,12 +38,14 @@
|
|
|
37
38
|
"build:daemon:macos-x64": "bun run scripts/build.ts --target daemon-macos-x64",
|
|
38
39
|
"build:daemon:macos-arm64": "bun run scripts/build.ts --target daemon-macos-arm64",
|
|
39
40
|
"smoke:daemon": "bun run scripts/post-build-smoke.ts",
|
|
41
|
+
"smoke:tui": "bun run scripts/smoke-tui-binary.ts",
|
|
40
42
|
"build:windows": "bun build src/main.ts --compile --target=bun-windows-x64 --outfile dist/goodvibes-windows.exe",
|
|
41
43
|
"build:all-shell": "bun run build:linux-x64 && bun run build:linux-arm64 && bun run build:macos-x64 && bun run build:macos-arm64 && bun run build:windows",
|
|
42
44
|
"test": "bun run scripts/run-tests.ts",
|
|
43
45
|
"version": "bun run scripts/prebuild.ts",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
+
"preinstall": "sh scripts/check-bun.sh",
|
|
47
|
+
"postinstall": "bun scripts/postinstall.js",
|
|
48
|
+
"postbuild": "bun scripts/postinstall.js --no-download",
|
|
46
49
|
"release": "bun run scripts/release.ts",
|
|
47
50
|
"release:dry": "bun run scripts/release.ts --dry-run",
|
|
48
51
|
"publish:package": "bun run scripts/publish-package.ts",
|
|
@@ -54,7 +57,10 @@
|
|
|
54
57
|
"build:all": "bun run scripts/build.ts --all",
|
|
55
58
|
"perf:check": "bun run scripts/perf-check.ts",
|
|
56
59
|
"architecture:check": "bun run scripts/check-architecture.ts",
|
|
60
|
+
"audit:home": "bun run scripts/audit-goodvibes-home.ts",
|
|
57
61
|
"foundation:artifacts": "bun run scripts/export-foundation-artifacts.ts",
|
|
62
|
+
"verification:ledger": "bun run scripts/verification-ledger.ts",
|
|
63
|
+
"verification:live": "bun run scripts/verify-live.ts",
|
|
58
64
|
"eval:gate": "bun run scripts/eval-gate.ts",
|
|
59
65
|
"eval:gate:verbose": "bun run scripts/eval-gate.ts --verbose",
|
|
60
66
|
"eval:baseline": "bun run scripts/eval-gate.ts --save-baseline"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
5
|
+
cat >&2 <<'EOF'
|
|
6
|
+
goodvibes-tui requires Bun.
|
|
7
|
+
|
|
8
|
+
Install Bun first, then install GoodVibes from the npm registry with:
|
|
9
|
+
|
|
10
|
+
bun add -g @pellux/goodvibes-tui
|
|
11
|
+
|
|
12
|
+
npm install -g @pellux/goodvibes-tui also works, but Bun must already be installed and available on PATH.
|
|
13
|
+
EOF
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if ! bun --version >/dev/null 2>&1; then
|
|
18
|
+
echo "goodvibes-tui requires a working Bun executable on PATH." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
package/scripts/postinstall.js
CHANGED
package/src/cli/help.ts
CHANGED
|
@@ -16,7 +16,6 @@ function readJsonVersion(path: string): string | null {
|
|
|
16
16
|
export function getPackageVersion(): string {
|
|
17
17
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
return readJsonVersion(join(here, '..', '..', 'package.json'))
|
|
19
|
-
?? process.env.npm_package_version
|
|
20
19
|
?? VERSION;
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -7,6 +7,7 @@ export interface PackageCliBinVerification {
|
|
|
7
7
|
readonly target: string;
|
|
8
8
|
readonly exists: boolean;
|
|
9
9
|
readonly executable: boolean;
|
|
10
|
+
readonly usesBunShebang: boolean;
|
|
10
11
|
readonly hasLocalPlatformBuildFallback: boolean;
|
|
11
12
|
readonly hasLocalBuildFallback: boolean;
|
|
12
13
|
readonly hasVendoredBinaryFallback: boolean;
|
|
@@ -35,6 +36,7 @@ const REQUIRED_TARBALL_PATHS = [
|
|
|
35
36
|
'src/daemon/cli.ts',
|
|
36
37
|
'bin/goodvibes',
|
|
37
38
|
'bin/goodvibes-daemon',
|
|
39
|
+
'scripts/check-bun.sh',
|
|
38
40
|
'scripts/postinstall.js',
|
|
39
41
|
'.goodvibes/GOODVIBES.md',
|
|
40
42
|
] as const;
|
|
@@ -58,6 +60,7 @@ function verifyBin(root: string, command: typeof REQUIRED_BIN_COMMANDS[number],
|
|
|
58
60
|
target: target ?? '',
|
|
59
61
|
exists: Boolean(target) && existsSync(binPath),
|
|
60
62
|
executable: Boolean(target) && hasExecutableBit(binPath),
|
|
63
|
+
usesBunShebang: source.startsWith('#!/usr/bin/env bun'),
|
|
61
64
|
hasLocalPlatformBuildFallback: source.includes("dist', artifactName"),
|
|
62
65
|
hasLocalBuildFallback: source.includes(expectedLocalBuild),
|
|
63
66
|
hasVendoredBinaryFallback: source.includes('vendor'),
|
|
@@ -92,6 +95,7 @@ export function verifyPackageCliInstall(root: string): PackageCliVerificationRep
|
|
|
92
95
|
if (!item.target) issues.push(`package.json bin is missing ${item.command}.`);
|
|
93
96
|
if (!item.exists) issues.push(`bin target does not exist: ${item.command} -> ${item.target}`);
|
|
94
97
|
if (!item.executable) issues.push(`bin target is not executable: ${item.command} -> ${item.target}`);
|
|
98
|
+
if (!item.usesBunShebang) issues.push(`bin target does not use Bun shebang: ${item.command} -> ${item.target}`);
|
|
95
99
|
if (!item.hasLocalPlatformBuildFallback) issues.push(`bin target lacks local platform dist fallback: ${item.command}`);
|
|
96
100
|
if (!item.hasLocalBuildFallback) issues.push(`bin target lacks local dist fallback: ${item.command}`);
|
|
97
101
|
if (!item.hasVendoredBinaryFallback) issues.push(`bin target lacks vendored binary fallback: ${item.command}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
1
2
|
import type { CliCommandRuntime } from './management.ts';
|
|
2
|
-
import { buildCliServicePosture, createPlatformServiceManager, formatCliServicePosture } from './service-posture.ts';
|
|
3
|
+
import { buildCliServicePosture, createPlatformServiceManager, formatCliServicePosture, getServiceStateRoot } from './service-posture.ts';
|
|
3
4
|
import type { CliCommandOutput } from './types.ts';
|
|
4
5
|
|
|
5
6
|
function enableServicePosture(runtime: CliCommandRuntime): void {
|
|
@@ -21,6 +22,9 @@ export async function handleServiceCommand(runtime: CliCommandRuntime): Promise<
|
|
|
21
22
|
if (sub === 'install' || sub === 'start' || sub === 'restart' || sub === 'stop' || sub === 'uninstall') {
|
|
22
23
|
const manager = createPlatformServiceManager(runtime);
|
|
23
24
|
if (sub === 'install' || sub === 'start' || sub === 'restart') enableServicePosture(runtime);
|
|
25
|
+
if (sub === 'install' || sub === 'start' || sub === 'restart') {
|
|
26
|
+
mkdirSync(getServiceStateRoot(runtime), { recursive: true });
|
|
27
|
+
}
|
|
24
28
|
const result =
|
|
25
29
|
sub === 'install' ? manager.install()
|
|
26
30
|
: sub === 'start' ? manager.start()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { closeSync, existsSync, openSync, readSync, statSync } from 'node:fs';
|
|
1
|
+
import { accessSync, closeSync, constants, existsSync, openSync, readSync, realpathSync, statSync } from 'node:fs';
|
|
2
2
|
import net from 'node:net';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { basename, delimiter, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { PlatformServiceManager } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
5
6
|
import type { ManagedServiceStatus } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
6
7
|
import type { ConfigManager } from '../config/index.ts';
|
|
@@ -56,6 +57,31 @@ const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: stri
|
|
|
56
57
|
{ id: 'web', label: 'web surface', enabledKey: 'web.enabled' },
|
|
57
58
|
];
|
|
58
59
|
|
|
60
|
+
const SOURCE_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
61
|
+
|
|
62
|
+
export interface DaemonExecutableResolutionOptions {
|
|
63
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
64
|
+
readonly argv?: readonly string[];
|
|
65
|
+
readonly execPath?: string;
|
|
66
|
+
readonly packageRoot?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DaemonExecutableResolution {
|
|
70
|
+
readonly command: string;
|
|
71
|
+
readonly source: 'env' | 'sibling' | 'package' | 'path' | 'fallback';
|
|
72
|
+
readonly absolute: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface ServiceDefinitionOverride {
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly description: string;
|
|
78
|
+
readonly workingDirectory: string;
|
|
79
|
+
readonly command: string;
|
|
80
|
+
readonly args: readonly string[];
|
|
81
|
+
readonly env: Readonly<Record<string, string>>;
|
|
82
|
+
readonly restartOnFailure: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
59
85
|
function connectHostForBindHost(host: string): string {
|
|
60
86
|
if (host === '0.0.0.0' || host === '::') return '127.0.0.1';
|
|
61
87
|
return host || '127.0.0.1';
|
|
@@ -76,8 +102,129 @@ async function probeTcp(host: string, port: number, timeoutMs = 750): Promise<bo
|
|
|
76
102
|
});
|
|
77
103
|
}
|
|
78
104
|
|
|
105
|
+
function isExecutable(path: string): boolean {
|
|
106
|
+
try {
|
|
107
|
+
accessSync(path, constants.X_OK);
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function platformDaemonArtifactName(platform = process.platform, arch = process.arch): string {
|
|
115
|
+
if (platform === 'linux' && arch === 'x64') return 'goodvibes-daemon-linux-x64';
|
|
116
|
+
if (platform === 'linux' && arch === 'arm64') return 'goodvibes-daemon-linux-arm64';
|
|
117
|
+
if (platform === 'darwin' && arch === 'x64') return 'goodvibes-daemon-macos-x64';
|
|
118
|
+
if (platform === 'darwin' && arch === 'arm64') return 'goodvibes-daemon-macos-arm64';
|
|
119
|
+
if (platform === 'win32') return 'goodvibes-daemon-windows.exe';
|
|
120
|
+
return 'goodvibes-daemon';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function executablePathCandidates(path: string): string[] {
|
|
124
|
+
if (!path.trim() || !isAbsolute(path)) return [];
|
|
125
|
+
const dir = dirname(path);
|
|
126
|
+
const name = basename(path);
|
|
127
|
+
const artifact = platformDaemonArtifactName();
|
|
128
|
+
const candidates = [
|
|
129
|
+
join(dir, 'goodvibes-daemon'),
|
|
130
|
+
join(dir, artifact),
|
|
131
|
+
];
|
|
132
|
+
if (name.startsWith('goodvibes-') && !name.startsWith('goodvibes-daemon-')) {
|
|
133
|
+
candidates.push(join(dir, name.replace(/^goodvibes-/, 'goodvibes-daemon-')));
|
|
134
|
+
}
|
|
135
|
+
if (name === 'goodvibes') candidates.push(join(dir, 'goodvibes-daemon'));
|
|
136
|
+
return [...new Set(candidates)];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resolveCommandFromPath(command: string, pathValue: string | undefined): string | null {
|
|
140
|
+
const pathEntries = (pathValue ?? '').split(delimiter).filter(Boolean);
|
|
141
|
+
const extensions = process.platform === 'win32'
|
|
142
|
+
? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
|
|
143
|
+
: [''];
|
|
144
|
+
for (const entry of pathEntries) {
|
|
145
|
+
for (const extension of extensions) {
|
|
146
|
+
const candidate = join(entry, `${command}${extension}`);
|
|
147
|
+
if (isExecutable(candidate)) return candidate;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function firstExecutable(paths: readonly string[]): string | null {
|
|
154
|
+
for (const path of paths) {
|
|
155
|
+
if (isExecutable(path)) return path;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function packageArtifactForBinWrapper(path: string): string | null {
|
|
161
|
+
let realPath = path;
|
|
162
|
+
try {
|
|
163
|
+
realPath = realpathSync(path);
|
|
164
|
+
} catch {
|
|
165
|
+
// Keep the original path if resolving a symlink fails.
|
|
166
|
+
}
|
|
167
|
+
if (basename(realPath) !== 'goodvibes-daemon') return null;
|
|
168
|
+
const binDir = dirname(realPath);
|
|
169
|
+
if (basename(binDir) !== 'bin') return null;
|
|
170
|
+
const packageRoot = dirname(binDir);
|
|
171
|
+
return firstExecutable([
|
|
172
|
+
join(packageRoot, 'vendor', platformDaemonArtifactName()),
|
|
173
|
+
join(packageRoot, 'dist', platformDaemonArtifactName()),
|
|
174
|
+
join(packageRoot, 'dist', 'goodvibes-daemon'),
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function resolveGoodVibesDaemonExecutable(
|
|
179
|
+
options: DaemonExecutableResolutionOptions = {},
|
|
180
|
+
): DaemonExecutableResolution {
|
|
181
|
+
const env = options.env ?? process.env;
|
|
182
|
+
const override = env.GOODVIBES_DAEMON_COMMAND?.trim();
|
|
183
|
+
if (override) {
|
|
184
|
+
return { command: override, source: 'env', absolute: isAbsolute(override) };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const packageRoot = options.packageRoot ?? SOURCE_PACKAGE_ROOT;
|
|
188
|
+
const packaged = firstExecutable([
|
|
189
|
+
join(packageRoot, 'dist', platformDaemonArtifactName()),
|
|
190
|
+
join(packageRoot, 'dist', 'goodvibes-daemon'),
|
|
191
|
+
join(packageRoot, 'vendor', platformDaemonArtifactName()),
|
|
192
|
+
join(packageRoot, 'bin', 'goodvibes-daemon'),
|
|
193
|
+
]);
|
|
194
|
+
if (packaged) return { command: packaged, source: 'package', absolute: true };
|
|
195
|
+
|
|
196
|
+
const argv = options.argv ?? process.argv;
|
|
197
|
+
const execPath = options.execPath ?? process.execPath;
|
|
198
|
+
const sibling = firstExecutable([
|
|
199
|
+
...executablePathCandidates(execPath),
|
|
200
|
+
...executablePathCandidates(argv[1] ?? ''),
|
|
201
|
+
]);
|
|
202
|
+
if (sibling) {
|
|
203
|
+
return {
|
|
204
|
+
command: packageArtifactForBinWrapper(sibling) ?? sibling,
|
|
205
|
+
source: 'sibling',
|
|
206
|
+
absolute: true,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const pathResolved = resolveCommandFromPath('goodvibes-daemon', env.PATH);
|
|
211
|
+
if (pathResolved) {
|
|
212
|
+
return {
|
|
213
|
+
command: packageArtifactForBinWrapper(pathResolved) ?? pathResolved,
|
|
214
|
+
source: 'path',
|
|
215
|
+
absolute: true,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { command: 'goodvibes-daemon', source: 'fallback', absolute: false };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function getServiceStateRoot(runtime: CliServiceRuntime): string {
|
|
223
|
+
return join(runtime.homeDirectory, '.goodvibes', 'daemon');
|
|
224
|
+
}
|
|
225
|
+
|
|
79
226
|
function pidFilePath(runtime: CliServiceRuntime, platform: ManagedServiceStatus['platform']): string {
|
|
80
|
-
return join(runtime
|
|
227
|
+
return join(getServiceStateRoot(runtime), 'service', `${platform}.pid`);
|
|
81
228
|
}
|
|
82
229
|
|
|
83
230
|
function readLogPosture(path: string | undefined, tailBytes: number): CliServiceLogPosture {
|
|
@@ -122,11 +269,28 @@ function endpointsConflict(a: CliServiceEndpointPosture, b: CliServiceEndpointPo
|
|
|
122
269
|
}
|
|
123
270
|
|
|
124
271
|
export function createPlatformServiceManager(runtime: CliServiceRuntime): PlatformServiceManager {
|
|
272
|
+
const daemonHomeDir = getServiceStateRoot(runtime);
|
|
273
|
+
const serviceName = String(runtime.configManager.get('service.serviceName') ?? 'goodvibes').trim() || 'goodvibes';
|
|
274
|
+
const daemonExecutable = resolveGoodVibesDaemonExecutable();
|
|
275
|
+
const definition: ServiceDefinitionOverride = {
|
|
276
|
+
name: serviceName,
|
|
277
|
+
description: 'GoodVibes daemon, control-plane, listener, and web host',
|
|
278
|
+
workingDirectory: daemonHomeDir,
|
|
279
|
+
command: daemonExecutable.command,
|
|
280
|
+
args: [],
|
|
281
|
+
env: {
|
|
282
|
+
GOODVIBES_DAEMON_HOME: daemonHomeDir,
|
|
283
|
+
GOODVIBES_DAEMON_TOKEN: process.env.GOODVIBES_DAEMON_TOKEN ?? '',
|
|
284
|
+
GOODVIBES_HTTP_TOKEN: process.env.GOODVIBES_HTTP_TOKEN ?? '',
|
|
285
|
+
NODE_ENV: process.env.NODE_ENV ?? 'production',
|
|
286
|
+
},
|
|
287
|
+
restartOnFailure: runtime.configManager.get('service.restartOnFailure') === true,
|
|
288
|
+
};
|
|
125
289
|
return new PlatformServiceManager(runtime.configManager, {
|
|
126
|
-
workingDirectory: runtime.
|
|
290
|
+
workingDirectory: runtime.homeDirectory,
|
|
127
291
|
homeDirectory: runtime.homeDirectory,
|
|
128
|
-
surfaceRoot: '
|
|
129
|
-
|
|
292
|
+
surfaceRoot: 'daemon',
|
|
293
|
+
definitionOverride: definition,
|
|
130
294
|
defaultServiceName: 'goodvibes',
|
|
131
295
|
defaultServiceDescription: 'GoodVibes daemon, control-plane, listener, and web host',
|
|
132
296
|
});
|