@pellux/goodvibes-tui 0.19.19 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "0.19.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.35",
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 companionTokenRecord = getOrCreateCompanionToken('tui', { daemonHomeDir: join(homeDirectory, '.goodvibes', 'daemon') });
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
 
@@ -36,7 +36,8 @@ import {
36
36
  import { startBackgroundProviderRegistration } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-background';
37
37
  import { restoreSavedModel } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-helpers';
38
38
  import { startExternalServices, type ExternalServicesHandle } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-services';
39
- 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';
40
41
  import type { UiRuntimeServices } from './ui-services.ts';
41
42
  import { createDeferredStartupCoordinator } from '@pellux/goodvibes-sdk/platform/runtime/deferred-startup';
42
43
  import { initializeBootstrapCore } from './bootstrap-core.ts';
@@ -331,7 +332,23 @@ export async function bootstrapRuntime(
331
332
  // Register the persistent companion-pairing token as the daemon's shared
332
333
  // bearer, so tokens scanned from the /qrcode panel's QR actually
333
334
  // authenticate against the embedded daemon this surface starts.
334
- const companionTokenRecord = getOrCreateCompanionToken('tui', { daemonHomeDir: join(services.homeDirectory, '.goodvibes', 'daemon') });
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
+ }
335
352
  externalServicesPromise = startExternalServices(
336
353
  configManager,
337
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.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;