@pellux/goodvibes-tui 0.19.16 → 0.19.20
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 +38 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1555 -496
- package/package.json +2 -2
- package/src/daemon/cli.ts +16 -1
- package/src/panels/tool-inspector-panel.ts +11 -16
- package/src/runtime/bootstrap.ts +20 -2
- package/src/runtime/operator-token-cleanup.ts +28 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.20",
|
|
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",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@anthropic-ai/vertex-sdk": "^0.16.0",
|
|
91
91
|
"@ast-grep/napi": "^0.42.0",
|
|
92
92
|
"@aws/bedrock-token-generator": "^1.1.0",
|
|
93
|
-
"@pellux/goodvibes-sdk": "0.21.
|
|
93
|
+
"@pellux/goodvibes-sdk": "0.21.36",
|
|
94
94
|
"bash-language-server": "^5.6.0",
|
|
95
95
|
"fuse.js": "^7.1.0",
|
|
96
96
|
"graphql": "^16.13.2",
|
package/src/daemon/cli.ts
CHANGED
|
@@ -12,11 +12,13 @@ import { GlobalNetworkTransportInstaller } from '@pellux/goodvibes-sdk/platform/
|
|
|
12
12
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
|
|
13
13
|
import {
|
|
14
14
|
getOrCreateCompanionToken,
|
|
15
|
+
pruneStaleOperatorTokens,
|
|
15
16
|
buildCompanionConnectionInfo,
|
|
16
17
|
encodeConnectionPayload,
|
|
17
18
|
formatConnectionBlock,
|
|
18
19
|
} from '@pellux/goodvibes-sdk/platform/pairing/index';
|
|
19
20
|
import { generateQrMatrix, renderQrToString } from '@pellux/goodvibes-sdk/platform/pairing/qr-generator';
|
|
21
|
+
import { workspaceOperatorTokenCandidates } from '../runtime/operator-token-cleanup.ts';
|
|
20
22
|
import {
|
|
21
23
|
scan,
|
|
22
24
|
loadPersistedProviders,
|
|
@@ -147,7 +149,20 @@ async function main(): Promise<void> {
|
|
|
147
149
|
const { daemonToken, httpToken } = readDaemonCliTokens(process.env);
|
|
148
150
|
|
|
149
151
|
// If no explicit daemon token is set, use the companion token so mobile apps can connect.
|
|
150
|
-
const
|
|
152
|
+
const daemonHomeDir = join(homeDirectory, '.goodvibes', 'daemon');
|
|
153
|
+
const companionTokenRecord = getOrCreateCompanionToken('tui', { daemonHomeDir });
|
|
154
|
+
// F3 resolution (TUI 0.19.20): remove stale pre-0.21.28 workspace-scoped operator
|
|
155
|
+
// token files so only the canonical <daemonHomeDir>/operator-tokens.json survives.
|
|
156
|
+
const prune = pruneStaleOperatorTokens({
|
|
157
|
+
daemonHomeDir,
|
|
158
|
+
candidatePaths: workspaceOperatorTokenCandidates(workingDir),
|
|
159
|
+
});
|
|
160
|
+
if (prune.prunedPaths.length > 0) {
|
|
161
|
+
logger.info('daemon: pruned stale operator-token files', { count: prune.prunedPaths.length, paths: prune.prunedPaths });
|
|
162
|
+
}
|
|
163
|
+
if (prune.failedPaths.length > 0) {
|
|
164
|
+
logger.warn('daemon: failed to prune stale operator-token files (permission/race)', { count: prune.failedPaths.length, paths: prune.failedPaths });
|
|
165
|
+
}
|
|
151
166
|
const effectiveDaemonToken = daemonToken ?? companionTokenRecord.token;
|
|
152
167
|
const effectiveHttpToken = httpToken ?? effectiveDaemonToken;
|
|
153
168
|
|
|
@@ -75,14 +75,16 @@ function truncateJson(val: unknown, maxLen = 120): string {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function summarizeResult(result: unknown): string | undefined {
|
|
78
|
+
// SDK OBS-05 (0.21.31+): TOOL_SUCCEEDED/TOOL_FAILED.result is a ToolResultSummary
|
|
79
|
+
// { kind: 'text' | 'json' | 'binary' | 'error' | 'empty'; byteSize: number; preview?: string }.
|
|
78
80
|
if (!result || typeof result !== 'object') return undefined;
|
|
79
81
|
const record = result as Record<string, unknown>;
|
|
80
|
-
if (typeof record.
|
|
81
|
-
const compact = record.
|
|
82
|
+
if (typeof record.preview === 'string' && record.preview.trim()) {
|
|
83
|
+
const compact = record.preview.replace(/\s+/g, ' ').trim();
|
|
82
84
|
return compact.length > 72 ? `${compact.slice(0, 69)}\u2026` : compact;
|
|
83
85
|
}
|
|
84
|
-
if (typeof record.
|
|
85
|
-
return record.
|
|
86
|
+
if (typeof record.kind === 'string' && typeof record.byteSize === 'number') {
|
|
87
|
+
return `${record.kind} (${record.byteSize}B)`;
|
|
86
88
|
}
|
|
87
89
|
return undefined;
|
|
88
90
|
}
|
|
@@ -382,17 +384,16 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
382
384
|
this.markDirty();
|
|
383
385
|
}));
|
|
384
386
|
|
|
387
|
+
// NOTE: After SDK OBS-05 (0.21.31), TOOL_SUCCEEDED/TOOL_FAILED.result is a ToolResultSummary
|
|
388
|
+
// ({ kind, byteSize, preview? }) rather than the raw ToolResult object. The previous
|
|
389
|
+
// `_policyAudit` extraction is no longer reachable via this event — policy audit metadata
|
|
390
|
+
// must be sourced from a different channel (approval broker / tool result store) if the
|
|
391
|
+
// Tool Inspector is to display it in future.
|
|
385
392
|
this.unsubs.push(this.toolEvents.on('TOOL_SUCCEEDED', (data) => {
|
|
386
393
|
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
387
394
|
if (rec) {
|
|
388
395
|
rec.endMs = Date.now();
|
|
389
396
|
rec.result = data.result;
|
|
390
|
-
if (data.result && typeof data.result === 'object') {
|
|
391
|
-
const audit = (data.result as { _policyAudit?: { actionTaken?: string; spillBackend?: string; policyId?: string } })._policyAudit;
|
|
392
|
-
rec.policyAction = audit?.actionTaken;
|
|
393
|
-
rec.spillBackend = audit?.spillBackend;
|
|
394
|
-
rec.outputClass = audit?.policyId ?? rec.outputClass;
|
|
395
|
-
}
|
|
396
397
|
rec.resultSummary = summarizeResult(data.result);
|
|
397
398
|
}
|
|
398
399
|
this.markDirty();
|
|
@@ -404,12 +405,6 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
404
405
|
rec.endMs = Date.now();
|
|
405
406
|
rec.result = data.result;
|
|
406
407
|
rec.error = data.error;
|
|
407
|
-
if (data.result && typeof data.result === 'object') {
|
|
408
|
-
const audit = (data.result as { _policyAudit?: { actionTaken?: string; spillBackend?: string; policyId?: string } })._policyAudit;
|
|
409
|
-
rec.policyAction = audit?.actionTaken;
|
|
410
|
-
rec.spillBackend = audit?.spillBackend;
|
|
411
|
-
rec.outputClass = audit?.policyId ?? rec.outputClass;
|
|
412
|
-
}
|
|
413
408
|
rec.resultSummary = summarizeResult(data.result) ?? data.error;
|
|
414
409
|
}
|
|
415
410
|
this.markDirty();
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - main.ts: terminal setup, render loop, stdin/stdout handlers
|
|
10
10
|
* - lifecycle.ts: save/shutdown helpers
|
|
11
11
|
*/
|
|
12
|
+
import { join } from 'node:path';
|
|
12
13
|
import { Orchestrator } from '../core/orchestrator.ts';
|
|
13
14
|
import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp/manager';
|
|
14
15
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers/tier-prompts';
|
|
@@ -35,7 +36,8 @@ import {
|
|
|
35
36
|
import { startBackgroundProviderRegistration } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-background';
|
|
36
37
|
import { restoreSavedModel } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-helpers';
|
|
37
38
|
import { startExternalServices, type ExternalServicesHandle } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-services';
|
|
38
|
-
import { getOrCreateCompanionToken } from '@pellux/goodvibes-sdk/platform/pairing/companion-token';
|
|
39
|
+
import { getOrCreateCompanionToken, pruneStaleOperatorTokens } from '@pellux/goodvibes-sdk/platform/pairing/companion-token';
|
|
40
|
+
import { workspaceOperatorTokenCandidates } from './operator-token-cleanup.ts';
|
|
39
41
|
import type { UiRuntimeServices } from './ui-services.ts';
|
|
40
42
|
import { createDeferredStartupCoordinator } from '@pellux/goodvibes-sdk/platform/runtime/deferred-startup';
|
|
41
43
|
import { initializeBootstrapCore } from './bootstrap-core.ts';
|
|
@@ -330,7 +332,23 @@ export async function bootstrapRuntime(
|
|
|
330
332
|
// Register the persistent companion-pairing token as the daemon's shared
|
|
331
333
|
// bearer, so tokens scanned from the /qrcode panel's QR actually
|
|
332
334
|
// authenticate against the embedded daemon this surface starts.
|
|
333
|
-
const
|
|
335
|
+
const daemonHomeDir = join(services.homeDirectory, '.goodvibes', 'daemon');
|
|
336
|
+
const companionTokenRecord = getOrCreateCompanionToken('tui', { daemonHomeDir });
|
|
337
|
+
// F3 resolution (TUI 0.19.20): remove stale pre-0.21.28 workspace-scoped operator
|
|
338
|
+
// token files so only the canonical <daemonHomeDir>/operator-tokens.json survives.
|
|
339
|
+
// The prune is best-effort — it silently skips missing files, no-ops when tokens
|
|
340
|
+
// already match, and records un-deletable candidates in `failedPaths` for logging.
|
|
341
|
+
// See `pruneStaleOperatorTokens` in the SDK for semantics.
|
|
342
|
+
const prune = pruneStaleOperatorTokens({
|
|
343
|
+
daemonHomeDir,
|
|
344
|
+
candidatePaths: workspaceOperatorTokenCandidates(services.workingDirectory),
|
|
345
|
+
});
|
|
346
|
+
if (prune.prunedPaths.length > 0) {
|
|
347
|
+
logger.info(`[bootstrap] Pruned ${prune.prunedPaths.length} stale operator-token file(s): ${prune.prunedPaths.join(', ')}`);
|
|
348
|
+
}
|
|
349
|
+
if (prune.failedPaths.length > 0) {
|
|
350
|
+
logger.warn(`[bootstrap] Failed to prune ${prune.failedPaths.length} stale operator-token file(s) (permission/race): ${prune.failedPaths.join(', ')}`);
|
|
351
|
+
}
|
|
334
352
|
externalServicesPromise = startExternalServices(
|
|
335
353
|
configManager,
|
|
336
354
|
runtimeBus,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* operator-token-cleanup.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared helper that enumerates the legacy workspace-scoped `operator-tokens.json`
|
|
5
|
+
* locations the TUI has written at various pre-0.21.28 versions. Used by both the
|
|
6
|
+
* in-process bootstrap path (`src/runtime/bootstrap.ts`) and the standalone daemon
|
|
7
|
+
* CLI (`src/daemon/cli.ts`) so F3 (stale-token pruning) has a single source of
|
|
8
|
+
* truth for where to look.
|
|
9
|
+
*
|
|
10
|
+
* Adding a new legacy location: append to `workspaceOperatorTokenCandidates` and
|
|
11
|
+
* the new path will be inspected on the next daemon boot.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Return the list of absolute operator-tokens.json paths the TUI may have written
|
|
18
|
+
* at legacy (pre-0.21.28) workspace-scoped locations under `workingDirectory`.
|
|
19
|
+
*
|
|
20
|
+
* The canonical, current-SDK location is `<daemonHomeDir>/operator-tokens.json`;
|
|
21
|
+
* this helper is strictly for legacy-cleanup candidates.
|
|
22
|
+
*/
|
|
23
|
+
export function workspaceOperatorTokenCandidates(workingDirectory: string): readonly string[] {
|
|
24
|
+
return [
|
|
25
|
+
join(workingDirectory, '.goodvibes', 'operator-tokens.json'),
|
|
26
|
+
join(workingDirectory, '.goodvibes', 'tui', 'operator-tokens.json'),
|
|
27
|
+
];
|
|
28
|
+
}
|
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.20';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|