@pellux/goodvibes-tui 1.13.1 → 1.15.0
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 +104 -0
- package/README.md +6 -6
- package/bin/goodvibes +6 -0
- package/bin/goodvibes-daemon +6 -0
- package/bin/launcher-support.js +34 -1
- package/docs/foundation-artifacts/operator-contract.json +8076 -2047
- package/package.json +15 -11
- package/scripts/postinstall.js +73 -48
- package/src/cli/bundle-command.ts +5 -5
- package/src/cli/completion.ts +1 -1
- package/src/cli/completions/generate.ts +15 -3
- package/src/cli/doctor.ts +385 -0
- package/src/cli/entrypoint.ts +72 -1
- package/src/cli/help.ts +29 -9
- package/src/cli/hooks-command.ts +74 -0
- package/src/cli/hooks-report.ts +167 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/management.ts +13 -1
- package/src/cli/parser.ts +10 -2
- package/src/cli/plugin-bundles-command.ts +228 -0
- package/src/cli/plugin-command.ts +179 -0
- package/src/cli/service-posture.ts +10 -0
- package/src/cli/status.ts +275 -2
- package/src/cli/tui-startup.ts +119 -1
- package/src/cli/types.ts +3 -1
- package/src/config/index.ts +16 -0
- package/src/config/tui-extension-settings.ts +169 -0
- package/src/core/approval-alert.ts +15 -2
- package/src/core/compaction-receipt.ts +127 -0
- package/src/core/model-routing-chip.ts +82 -0
- package/src/core/permission-mode.ts +89 -0
- package/src/core/rewind-receipt.ts +83 -0
- package/src/core/rewind-turn-anchors.ts +160 -0
- package/src/core/scriptable-statusline.ts +141 -0
- package/src/core/session-auto-titler.ts +110 -0
- package/src/core/stream-event-wiring.ts +20 -0
- package/src/core/system-message-router.ts +1 -1
- package/src/core/terminal-notifier.ts +102 -0
- package/src/core/turn-event-wiring.ts +80 -1
- package/src/input/command-reference.ts +96 -0
- package/src/input/command-registry.ts +25 -0
- package/src/input/commands/channel-pairing.ts +245 -0
- package/src/input/commands/channel-runtime.ts +152 -6
- package/src/input/commands/checkin-runtime.ts +89 -0
- package/src/input/commands/checkpoint-runtime.ts +7 -179
- package/src/input/commands/ci-runtime.ts +207 -0
- package/src/input/commands/cost-attribution-format.ts +52 -0
- package/src/input/commands/cost-runtime.ts +44 -4
- package/src/input/commands/editor-runtime.ts +22 -0
- package/src/input/commands/flags-runtime.ts +328 -0
- package/src/input/commands/health-metrics-format.ts +67 -0
- package/src/input/commands/health-runtime.ts +55 -1
- package/src/input/commands/memory.ts +23 -0
- package/src/input/commands/operator-rpc.ts +95 -0
- package/src/input/commands/palette-runtime.ts +22 -0
- package/src/input/commands/permissions-provenance.ts +264 -0
- package/src/input/commands/permissions-runtime.ts +21 -0
- package/src/input/commands/planning-runtime.ts +28 -1
- package/src/input/commands/platform-access-runtime.ts +2 -97
- package/src/input/commands/principals-runtime.ts +212 -0
- package/src/input/commands/product-runtime.ts +43 -3
- package/src/input/commands/recall-files-config.ts +54 -0
- package/src/input/commands/recall-files-git.ts +57 -0
- package/src/input/commands/recall-files.ts +185 -0
- package/src/input/commands/recall-query.ts +8 -3
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/recall-shared.ts +13 -0
- package/src/input/commands/relay-runtime.ts +86 -0
- package/src/input/commands/review-runtime.ts +160 -0
- package/src/input/commands/rewind-runtime.ts +362 -0
- package/src/input/commands/schedule-nl.ts +147 -0
- package/src/input/commands/schedule-runtime.ts +29 -10
- package/src/input/commands/secret-runtime.ts +55 -0
- package/src/input/commands/session-content.ts +19 -8
- package/src/input/commands/session-workflow.ts +10 -0
- package/src/input/commands/update-runtime.ts +412 -0
- package/src/input/commands/workstream-attempt-dependents.ts +103 -0
- package/src/input/commands/workstream-attempts.ts +244 -0
- package/src/input/commands/workstream-runtime.ts +164 -26
- package/src/input/commands/worktree-runtime.ts +56 -2
- package/src/input/commands.ts +135 -80
- package/src/input/composer-capture.ts +67 -0
- package/src/input/composer-editor.ts +134 -0
- package/src/input/concealed-input.ts +94 -0
- package/src/input/exec-env-scrub-config.ts +84 -0
- package/src/input/feature-unit-layout.ts +142 -0
- package/src/input/feed-context-factory.ts +2 -0
- package/src/input/handler-feed-routes.ts +17 -0
- package/src/input/handler-feed.ts +3 -0
- package/src/input/handler-modal-routes.ts +5 -2
- package/src/input/handler-shortcuts.ts +37 -0
- package/src/input/handler.ts +24 -2
- package/src/input/keybindings.ts +11 -2
- package/src/input/onboarding/onboarding-feature-units.ts +536 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +40 -6
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +324 -629
- package/src/input/onboarding/onboarding-wizard-steps.ts +5 -5
- package/src/input/onboarding/onboarding-wizard-types.ts +6 -0
- package/src/input/relay-settings-descriptions.ts +35 -0
- package/src/input/sandbox-exec-config.ts +107 -0
- package/src/input/settings-modal-adjustment.ts +4 -7
- package/src/input/settings-modal-data.ts +178 -8
- package/src/input/settings-modal-mutations.ts +41 -13
- package/src/input/settings-modal-types.ts +42 -5
- package/src/input/settings-modal.ts +67 -15
- package/src/input/submission-router.ts +4 -1
- package/src/input/worktree-setup-config.ts +93 -0
- package/src/main.ts +43 -43
- package/src/panels/builtin/development.ts +14 -0
- package/src/panels/diff-review-model.ts +189 -0
- package/src/panels/diff-review-panel.ts +395 -0
- package/src/panels/fleet-panel-format.ts +114 -2
- package/src/panels/fleet-panel.ts +65 -78
- package/src/panels/fleet-read-model.ts +91 -11
- package/src/panels/fleet-stop.ts +19 -4
- package/src/panels/modals/memory-modal.ts +11 -2
- package/src/permissions/hunk-selection.ts +86 -5
- package/src/permissions/prompt.ts +108 -7
- package/src/permissions/sandbox-exec-gate.ts +162 -0
- package/src/renderer/settings-modal-helpers.ts +21 -1
- package/src/renderer/settings-modal.ts +70 -44
- package/src/renderer/shell-surface.ts +23 -1
- package/src/renderer/system-message.ts +4 -0
- package/src/renderer/tool-result-summary.ts +95 -5
- package/src/renderer/ui-factory.ts +22 -0
- package/src/runtime/bootstrap-command-context.ts +15 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -2
- package/src/runtime/bootstrap-core.ts +38 -38
- package/src/runtime/bootstrap-notifier-sync.ts +41 -0
- package/src/runtime/bootstrap-shell.ts +8 -0
- package/src/runtime/bootstrap.ts +21 -23
- package/src/runtime/context-accounting-source.ts +103 -0
- package/src/runtime/conversation-rewind-port.ts +130 -0
- package/src/runtime/fleet-needs-input-push.ts +70 -0
- package/src/runtime/install-self-check-startup.ts +36 -0
- package/src/runtime/install-self-check.ts +149 -0
- package/src/runtime/onboarding/fast-path.ts +93 -0
- package/src/runtime/process-lifecycle.ts +32 -24
- package/src/runtime/relay-reachability-bridge.ts +53 -0
- package/src/runtime/release-artifacts.ts +109 -0
- package/src/runtime/services.ts +33 -33
- package/src/runtime/trust/workspace-registration.ts +184 -0
- package/src/runtime/trust/workspace-trust.ts +202 -0
- package/src/runtime/ui-services.ts +2 -1
- package/src/runtime/update-check.ts +113 -0
- package/src/runtime/workspace-checkpointing.ts +32 -0
- package/src/runtime/workstream-attempts-validation.ts +77 -0
- package/src/runtime/workstream-draft-edits.ts +95 -0
- package/src/runtime/workstream-draft-store.ts +101 -0
- package/src/runtime/workstream-draft-types.ts +42 -0
- package/src/runtime/workstream-services.ts +70 -39
- package/src/shell/blocking-input.ts +29 -0
- package/src/shell/ui-openers.ts +65 -0
- package/src/verification/verification-ledger.ts +56 -2
- package/src/version.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +0 -130
- package/src/runtime/gateway-verbs.ts +0 -47
- package/src/runtime/render-scheduler.ts +0 -90
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -13,45 +13,37 @@ import { join } from 'node:path';
|
|
|
13
13
|
import { Orchestrator, type OrchestratorUserInputOptions } from '../core/orchestrator.ts';
|
|
14
14
|
import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp';
|
|
15
15
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
16
|
-
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
16
|
+
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
17
17
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
18
18
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
19
19
|
import type { InputHistory } from '../input/input-history.ts';
|
|
20
|
-
import type { GitStatusProvider } from '../renderer/git-status.ts';
|
|
21
|
-
import type { GitHeaderInfo } from '../renderer/git-status.ts';
|
|
20
|
+
import type { GitStatusProvider, GitHeaderInfo } from '../renderer/git-status.ts';
|
|
22
21
|
import type { SelectionManager } from '../input/selection.ts';
|
|
23
22
|
import type { Compositor } from '../renderer/compositor.ts';
|
|
24
23
|
|
|
25
24
|
import type { RuntimeContext, BootstrapOptions } from './context.ts';
|
|
26
|
-
import { shutdownRuntime, fireSessionStart, saveSession } from '@/runtime/index.ts';
|
|
27
|
-
import { createTaskManager } from '@/runtime/index.ts';
|
|
28
|
-
import { OpsControlPlane } from '@/runtime/index.ts';
|
|
29
|
-
import { AcpTaskAdapter } from '@/runtime/index.ts';
|
|
30
25
|
import type { SystemMessageRouter } from '../core/system-message-router.ts';
|
|
31
|
-
import { emitSessionReady, emitSessionStarted } from '@/runtime/index.ts';
|
|
32
26
|
import {
|
|
33
|
-
|
|
34
|
-
writeLastSessionPointer,
|
|
27
|
+
shutdownRuntime, fireSessionStart, saveSession, createTaskManager, OpsControlPlane, AcpTaskAdapter,
|
|
28
|
+
emitSessionReady, emitSessionStarted, loadLastConversation, writeLastSessionPointer,
|
|
29
|
+
scheduleBackgroundMcpDiscovery, startBackgroundProviderRegistration, restoreSavedModel, startExternalServices,
|
|
30
|
+
type ExternalServicesHandle, type HostServiceStatus, createHttpTransport, createDeferredStartupCoordinator,
|
|
35
31
|
} from '@/runtime/index.ts';
|
|
36
|
-
import { scheduleBackgroundMcpDiscovery, startBackgroundProviderRegistration } from '@/runtime/index.ts';
|
|
37
|
-
import { restoreSavedModel } from '@/runtime/index.ts';
|
|
38
|
-
import { startExternalServices, type ExternalServicesHandle, type HostServiceStatus } from '@/runtime/index.ts';
|
|
39
|
-
import { createHttpTransport } from '@/runtime/index.ts';
|
|
40
32
|
import { foldLegacySpineStore, deriveSpineFooterStatus } from '@pellux/goodvibes-sdk/platform/runtime/session-spine';
|
|
41
33
|
import { createTuiSpineTransport, type SpineSessionsClient } from './session-spine-transport.ts';
|
|
42
34
|
import { syncMemorySpineToHostStatus, type MemorySpineActiveRef } from './memory-spine-transport.ts';
|
|
43
35
|
import { pruneStaleOperatorTokens } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
44
36
|
import { resolveDaemonCompanionToken, workspaceOperatorTokenCandidates } from './operator-token-cleanup.ts';
|
|
45
37
|
import type { UiRuntimeServices } from './ui-services.ts';
|
|
46
|
-
import { createDeferredStartupCoordinator } from '@/runtime/index.ts';
|
|
47
38
|
import { initializeBootstrapCore } from './bootstrap-core.ts';
|
|
48
39
|
import { createBootstrapShell } from './bootstrap-shell.ts';
|
|
49
40
|
import { announceResumeState } from './resume-notice.ts';
|
|
41
|
+
import { announceInstallSelfCheck } from './install-self-check-startup.ts';
|
|
50
42
|
import { buildSharedOrchestratorCoreServices, refreshMemoryRecallSnapshot } from './orchestrator-core-services.ts';
|
|
51
|
-
import {
|
|
52
|
-
import { DaemonServer } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
53
|
-
import { HttpListener } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
43
|
+
import { createContextAccountingSource } from './context-accounting-source.ts';
|
|
44
|
+
import { DaemonServer, HttpListener } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
54
45
|
import { createSafeHostServeFactory } from '../daemon/safe-serve.ts';
|
|
46
|
+
import { buildRelayExternalServiceMethods } from './relay-reachability-bridge.ts';
|
|
55
47
|
import { startMcpConfigAutoReload } from '../mcp/runtime-reload.ts';
|
|
56
48
|
|
|
57
49
|
type ExternalServiceFactories = NonNullable<Parameters<typeof startExternalServices>[4]>;
|
|
@@ -253,6 +245,13 @@ export async function bootstrapRuntime(
|
|
|
253
245
|
});
|
|
254
246
|
conversation.setSessionLineageTracker(services.sessionLineageTracker);
|
|
255
247
|
|
|
248
|
+
// Bind context_accounting's session source to the live Orchestrator (see context-accounting-source.ts).
|
|
249
|
+
const contextAccountingSourceHandle = createContextAccountingSource({
|
|
250
|
+
orchestrator, providerRegistry, sessionLineageTracker: services.sessionLineageTracker, runtimeBus, sessionId: runtime.sessionId,
|
|
251
|
+
});
|
|
252
|
+
services.contextAccountingHolder.setSource(contextAccountingSourceHandle.source);
|
|
253
|
+
bootstrapUnsubs.push(contextAccountingSourceHandle.dispose);
|
|
254
|
+
|
|
256
255
|
const acpManager = new AcpManager({
|
|
257
256
|
requestPermission: (request) => permissionPromptRef.requestPermission(request),
|
|
258
257
|
runtimeBus,
|
|
@@ -317,9 +316,8 @@ export async function bootstrapRuntime(
|
|
|
317
316
|
}).catch(() => {
|
|
318
317
|
// Best-effort — never let the resume notice block or crash boot.
|
|
319
318
|
});
|
|
320
|
-
|
|
321
|
-
const inputHistory = shell
|
|
322
|
-
const lastGitInfoRef = shell.lastGitInfoRef;
|
|
319
|
+
announceInstallSelfCheck(systemMessageRouter);
|
|
320
|
+
const { gitStatusProvider, inputHistory, lastGitInfoRef } = shell;
|
|
323
321
|
// W1.6 FIX 2: dispose the header's live-repo-state poll (git-status.ts
|
|
324
322
|
// startPolling) on shutdown, same pattern as acpTaskSyncInterval above.
|
|
325
323
|
bootstrapUnsubs.push(() => gitStatusProvider.stopPolling());
|
|
@@ -494,8 +492,7 @@ export async function bootstrapRuntime(
|
|
|
494
492
|
});
|
|
495
493
|
|
|
496
494
|
let externalServices: ExternalServicesHandle = {
|
|
497
|
-
daemonServer: null,
|
|
498
|
-
httpListener: null,
|
|
495
|
+
daemonServer: null, httpListener: null,
|
|
499
496
|
daemonStatus: createPendingServiceStatus('daemon'),
|
|
500
497
|
httpListenerStatus: createPendingServiceStatus('httpListener'),
|
|
501
498
|
listRecentControlPlaneEvents: () => [],
|
|
@@ -506,6 +503,7 @@ export async function bootstrapRuntime(
|
|
|
506
503
|
externalServices: NonNullable<typeof uiServices.platform.externalServices>;
|
|
507
504
|
};
|
|
508
505
|
platformExternalServices.externalServices = {
|
|
506
|
+
...buildRelayExternalServiceMethods(() => externalServices.daemonServer),
|
|
509
507
|
inspect: inspectExternalServices,
|
|
510
508
|
restart: async () => {
|
|
511
509
|
if (externalServicesPromise) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Orchestrator } from '../core/orchestrator.ts';
|
|
2
|
+
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
3
|
+
import type { SessionLineageTracker } from '@pellux/goodvibes-sdk/platform/core';
|
|
4
|
+
import type { ContextAccountingSource } from '@pellux/goodvibes-sdk/platform/tools';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Minimal runtime-bus surface this module needs: subscribe by event type.
|
|
8
|
+
* A narrow structural type (not the full RuntimeEventBus) so this module
|
|
9
|
+
* doesn't pull in `runtime/index.ts` — bootstrap.ts imports this module
|
|
10
|
+
* directly, and index.ts re-exports bootstrap.ts, so a barrel import here
|
|
11
|
+
* would be a 3-file cycle (see check-architecture.ts's cycle detector).
|
|
12
|
+
*/
|
|
13
|
+
export interface CompactionActivityBus {
|
|
14
|
+
on(eventType: string, callback: () => void): () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Builds the interactive session's ContextAccountingSource — the honest
|
|
19
|
+
* backing for the SDK's `context_accounting` tool. Bound onto
|
|
20
|
+
* `RuntimeServices.contextAccountingHolder` once, at bootstrap (see
|
|
21
|
+
* bootstrap.ts, right after the live Orchestrator is constructed).
|
|
22
|
+
*
|
|
23
|
+
* Every field here reads a REAL, already-tracked value:
|
|
24
|
+
* - getTurnInjections() → Orchestrator.getTurnInjections() (the same bounded
|
|
25
|
+
* ring `/recall injections` renders from).
|
|
26
|
+
* - getTokenState() → Orchestrator.usage / lastInputTokens (the same
|
|
27
|
+
* counters the footer's context bar and CostTrackerPanel read), plus the
|
|
28
|
+
* current model's context window from the provider registry.
|
|
29
|
+
* - getCompactionState() → compactionCount from SessionLineageTracker (the
|
|
30
|
+
* same counter session-maintenance and /compact read); isCompacting is
|
|
31
|
+
* tracked from real 'compaction' domain lifecycle events on the runtime
|
|
32
|
+
* bus, since the Orchestrator has no public accessor for it — NOT
|
|
33
|
+
* fabricated. Without a runtime bus (headless/test callers), isCompacting
|
|
34
|
+
* honestly stays false rather than guessing.
|
|
35
|
+
*/
|
|
36
|
+
export interface ContextAccountingSourceDeps {
|
|
37
|
+
readonly orchestrator: Pick<Orchestrator, 'getTurnInjections' | 'usage' | 'lastInputTokens'>;
|
|
38
|
+
readonly providerRegistry: Pick<ProviderRegistry, 'getCurrentModel' | 'getContextWindowForModel'>;
|
|
39
|
+
readonly sessionLineageTracker: Pick<SessionLineageTracker, 'getCompactionCount'>;
|
|
40
|
+
readonly runtimeBus?: CompactionActivityBus | null | undefined;
|
|
41
|
+
readonly sessionId: string;
|
|
42
|
+
/** Human-readable scope label, e.g. "main session". Defaults to 'main session'. */
|
|
43
|
+
readonly scope?: string | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Events that mark a compaction run starting. */
|
|
47
|
+
const COMPACTION_START_EVENT_TYPES = [
|
|
48
|
+
'COMPACTION_AUTOCOMPACT',
|
|
49
|
+
'COMPACTION_REACTIVE',
|
|
50
|
+
'COMPACTION_MICROCOMPACT',
|
|
51
|
+
'COMPACTION_COLLAPSE',
|
|
52
|
+
] as const;
|
|
53
|
+
|
|
54
|
+
/** Events that mark a compaction run ending (successfully, rejected, or failed). */
|
|
55
|
+
const COMPACTION_END_EVENT_TYPES = [
|
|
56
|
+
'COMPACTION_DONE',
|
|
57
|
+
'COMPACTION_FAILED',
|
|
58
|
+
'COMPACTION_RECEIPT',
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
export interface ContextAccountingSourceHandle {
|
|
62
|
+
readonly source: ContextAccountingSource;
|
|
63
|
+
/** Unsubscribe the compaction-activity listeners. Call on session teardown. */
|
|
64
|
+
readonly dispose: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createContextAccountingSource(deps: ContextAccountingSourceDeps): ContextAccountingSourceHandle {
|
|
68
|
+
let isCompacting = false;
|
|
69
|
+
const unsubs: Array<() => void> = [];
|
|
70
|
+
if (deps.runtimeBus) {
|
|
71
|
+
const bus = deps.runtimeBus;
|
|
72
|
+
for (const type of COMPACTION_START_EVENT_TYPES) {
|
|
73
|
+
unsubs.push(bus.on(type, () => { isCompacting = true; }));
|
|
74
|
+
}
|
|
75
|
+
for (const type of COMPACTION_END_EVENT_TYPES) {
|
|
76
|
+
unsubs.push(bus.on(type, () => { isCompacting = false; }));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const source: ContextAccountingSource = {
|
|
81
|
+
scope: deps.scope ?? 'main session',
|
|
82
|
+
sessionId: deps.sessionId,
|
|
83
|
+
getTurnInjections: () => deps.orchestrator.getTurnInjections(),
|
|
84
|
+
getTokenState: () => {
|
|
85
|
+
const currentModel = deps.providerRegistry.getCurrentModel();
|
|
86
|
+
const contextWindow = deps.providerRegistry.getContextWindowForModel(currentModel);
|
|
87
|
+
return {
|
|
88
|
+
measured: { ...deps.orchestrator.usage },
|
|
89
|
+
lastInputTokens: deps.orchestrator.lastInputTokens,
|
|
90
|
+
contextWindow: contextWindow > 0 ? contextWindow : null,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
getCompactionState: () => ({
|
|
94
|
+
isCompacting,
|
|
95
|
+
compactionCount: deps.sessionLineageTracker.getCompactionCount(),
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
source,
|
|
101
|
+
dispose: () => { for (const unsub of unsubs) unsub(); },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// conversation-rewind-port.ts — the TUI's ConversationManager as the SDK unified
|
|
3
|
+
// rewind service's RewindConversationPort.
|
|
4
|
+
//
|
|
5
|
+
// The SDK's UnifiedRewindService (platform/rewind) joins files rewind (workspace
|
|
6
|
+
// checkpoints) with conversation rewind through two ports. The conversation port
|
|
7
|
+
// is "a daemon-hosted mutable conversation store": preview() reports how many
|
|
8
|
+
// messages would truncate to a recorded turn boundary, and rewind() performs the
|
|
9
|
+
// truncation and captures the pre-/post-truncation snapshots so the reversal can
|
|
10
|
+
// be undone and re-applied. The truncation boundary is the message count recorded
|
|
11
|
+
// for the anchor's turnId at TURN_COMPLETED (rewind-turn-anchors.ts) — the same
|
|
12
|
+
// join key files rewind uses against the workspace checkpoint.
|
|
13
|
+
//
|
|
14
|
+
// This module is the single implementation of that port for the TUI, consumed by
|
|
15
|
+
// BOTH the in-process /rewind command (bound to one session's conversation) and
|
|
16
|
+
// the composed daemon's rewind.plan/apply verbs (resolving the live conversation
|
|
17
|
+
// per anchor.sessionId from the registry below), so the truncation semantics
|
|
18
|
+
// cannot drift between the two surfaces.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
import type {
|
|
22
|
+
RewindAnchor,
|
|
23
|
+
RewindConversationOutcome,
|
|
24
|
+
RewindConversationPort,
|
|
25
|
+
RewindConversationPreview,
|
|
26
|
+
} from '@pellux/goodvibes-sdk/platform/rewind';
|
|
27
|
+
import type { ConversationManager } from '../core/conversation.ts';
|
|
28
|
+
import { resolveTurnAnchor } from '../core/rewind-turn-anchors.ts';
|
|
29
|
+
|
|
30
|
+
type ConversationJson = Parameters<ConversationManager['fromJSON']>[0];
|
|
31
|
+
|
|
32
|
+
/** The port plus the reversal accessors the TUI-side undo/redo needs. */
|
|
33
|
+
export interface ConversationRewindPort extends RewindConversationPort {
|
|
34
|
+
/** Restore the pre-truncation conversation (the /undo direction). */
|
|
35
|
+
restoreBefore(undoSnapshotId: string): boolean;
|
|
36
|
+
/** Restore the post-truncation conversation (the /redo direction). */
|
|
37
|
+
restoreAfter(undoSnapshotId: string): boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** One truncation's captured state — the target conversation and its snapshots. */
|
|
41
|
+
interface SnapshotPair {
|
|
42
|
+
readonly conv: ConversationManager;
|
|
43
|
+
readonly before: ConversationJson;
|
|
44
|
+
readonly after: ConversationJson;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Build a conversation rewind port. `resolveConversation` maps an anchor's
|
|
49
|
+
* sessionId to the live ConversationManager — for the single-session /rewind
|
|
50
|
+
* command it returns the one bound conversation; for the daemon it looks the
|
|
51
|
+
* session up in the live registry. A null resolution means no live conversation
|
|
52
|
+
* for that session, reported as "nothing to drop" rather than a fabricated count.
|
|
53
|
+
*/
|
|
54
|
+
export function createConversationRewindPort(
|
|
55
|
+
resolveConversation: (sessionId: string) => ConversationManager | null,
|
|
56
|
+
): ConversationRewindPort {
|
|
57
|
+
const snapshots = new Map<string, SnapshotPair>();
|
|
58
|
+
|
|
59
|
+
function keepFor(anchor: RewindAnchor): { conv: ConversationManager | null; keep: number; total: number } {
|
|
60
|
+
const conv = resolveConversation(anchor.sessionId);
|
|
61
|
+
if (!conv) return { conv: null, keep: 0, total: 0 };
|
|
62
|
+
const total = conv.getMessageCount();
|
|
63
|
+
const rec = anchor.turnId ? resolveTurnAnchor(anchor.sessionId, anchor.turnId) : null;
|
|
64
|
+
const keep = rec ? Math.min(rec.messageCount, total) : total;
|
|
65
|
+
return { conv, keep, total };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function restore(snapshot: SnapshotPair | undefined, which: 'before' | 'after'): boolean {
|
|
69
|
+
if (!snapshot) return false;
|
|
70
|
+
snapshot.conv.fromJSON(snapshot[which]);
|
|
71
|
+
snapshot.conv.rebuildHistory();
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
async preview(anchor: RewindAnchor): Promise<RewindConversationPreview> {
|
|
77
|
+
const { keep, total } = keepFor(anchor);
|
|
78
|
+
return { messagesToDrop: Math.max(0, total - keep), messagesRemaining: keep };
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async rewind(anchor: RewindAnchor): Promise<RewindConversationOutcome> {
|
|
82
|
+
const { conv, keep, total } = keepFor(anchor);
|
|
83
|
+
const undoSnapshotId = `rwc_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
84
|
+
if (!conv) return { droppedMessages: 0, undoSnapshotId };
|
|
85
|
+
const before = conv.toJSON() as ConversationJson;
|
|
86
|
+
conv.removeMessagesAfter(keep);
|
|
87
|
+
conv.rebuildHistory();
|
|
88
|
+
const after = conv.toJSON() as ConversationJson;
|
|
89
|
+
snapshots.set(undoSnapshotId, { conv, before, after });
|
|
90
|
+
return { droppedMessages: Math.max(0, total - keep), undoSnapshotId };
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
restoreBefore(undoSnapshotId: string): boolean {
|
|
94
|
+
return restore(snapshots.get(undoSnapshotId), 'before');
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
restoreAfter(undoSnapshotId: string): boolean {
|
|
98
|
+
return restore(snapshots.get(undoSnapshotId), 'after');
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Live per-session conversation registry — the daemon-hosted mutable store the
|
|
105
|
+
// composed daemon's rewind.plan/apply verbs resolve conversations from. The TUI
|
|
106
|
+
// registers its active session's ConversationManager at bootstrap; a session
|
|
107
|
+
// with no registration reports conversation rewind as "nothing to drop".
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
const liveConversations = new Map<string, ConversationManager>();
|
|
111
|
+
|
|
112
|
+
/** Register a session's live conversation so the daemon rewind verbs can serve it. */
|
|
113
|
+
export function registerSessionConversation(sessionId: string, conversation: ConversationManager): void {
|
|
114
|
+
if (sessionId) liveConversations.set(sessionId, conversation);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Drop a session's conversation registration. */
|
|
118
|
+
export function unregisterSessionConversation(sessionId: string): void {
|
|
119
|
+
liveConversations.delete(sessionId);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The conversation rewind port the composed daemon threads into
|
|
124
|
+
* registerGatewayVerbGroups — it resolves each anchor's live conversation from
|
|
125
|
+
* the registry above, so the daemon's own rewind verbs serve conversation scope
|
|
126
|
+
* live in this process.
|
|
127
|
+
*/
|
|
128
|
+
export function createSessionConversationRewindPort(): ConversationRewindPort {
|
|
129
|
+
return createConversationRewindPort((sessionId) => liveConversations.get(sessionId) ?? null);
|
|
130
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fleet-needs-input-push.ts — wires the fleet "needs-input" browser-push fan-out
|
|
3
|
+
* that the SDK's registerGatewayVerbGroups (reached through the terminal-shell's
|
|
4
|
+
* attachWsOnlyGatewayVerbHandlers wrapper) gates on two deps: `runtimeBus` and
|
|
5
|
+
* `sessionPresence` (see push/service.ts's attachFleetNeedsInputSource). When a
|
|
6
|
+
* fleet node blocks on the operator, it pushes an "Input needed" notification
|
|
7
|
+
* carrying the session/node deep link to every registered device — suppressed
|
|
8
|
+
* while an operator surface is already attached to that node's session.
|
|
9
|
+
*
|
|
10
|
+
* TWO seams have to be live for a real push, not just descriptor presence:
|
|
11
|
+
* 1. Fleet lifecycle deltas (FLEET_NODE_BLOCKED_ON_USER etc.) have to reach the
|
|
12
|
+
* runtime bus's 'fleet' domain in the first place. That is
|
|
13
|
+
* attachFleetEmitBridge, diffing the process registry's own coalesced
|
|
14
|
+
* snapshot tick into per-node lifecycle events. The terminal-shell package's
|
|
15
|
+
* createArchivableFleetRegistry only builds the registry — it does not
|
|
16
|
+
* attach the bridge — so the composition root that owns both the registry
|
|
17
|
+
* and the bus (this file) is the one place responsible for it. The SDK's
|
|
18
|
+
* own daemon composition (platform/runtime/services.ts) attaches this same
|
|
19
|
+
* bridge right after building its process registry.
|
|
20
|
+
* 2. registerGatewayVerbGroups has to be handed `runtimeBus` (to subscribe onto
|
|
21
|
+
* that domain) and `sessionPresence` (to suppress a push when an operator is
|
|
22
|
+
* already looking at the session).
|
|
23
|
+
*
|
|
24
|
+
* sessionPresence mirrors the SDK's own daemon composition: a session counts as
|
|
25
|
+
* attended when any participant heartbeated within the freshness window below.
|
|
26
|
+
* Reimplemented here on the broker's public getSession()/SharedSessionRecord
|
|
27
|
+
* shape rather than imported, because the SDK does not expose its own
|
|
28
|
+
* hasFreshSurfaceParticipant helper as public API.
|
|
29
|
+
*/
|
|
30
|
+
import { attachFleetEmitBridge } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
31
|
+
import type { ArchivableProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
32
|
+
import type { SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
33
|
+
import type { RuntimeEventBus } from '@/runtime/index.ts';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* How recently a participant must have been seen to count as actively
|
|
37
|
+
* attached. Mirrors the SDK's own (private) SURFACE_ROUTE_FRESHNESS_MS.
|
|
38
|
+
*/
|
|
39
|
+
const SURFACE_ATTACHED_FRESHNESS_MS = 2 * 60 * 1000;
|
|
40
|
+
|
|
41
|
+
export interface FleetNeedsInputPushDeps {
|
|
42
|
+
readonly runtimeBus: RuntimeEventBus;
|
|
43
|
+
readonly sessionPresence: { isAttached(sessionId: string): boolean };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Attach the fleet emit-bridge (registry snapshot deltas -> runtime bus `fleet`
|
|
48
|
+
* domain) and build the presence lookup, returning both as the deps
|
|
49
|
+
* `attachWsOnlyGatewayVerbHandlers` spreads into its call so the needs-input
|
|
50
|
+
* push source registers for real instead of gracefully degrading to absent.
|
|
51
|
+
*/
|
|
52
|
+
export function wireFleetNeedsInputPush(input: {
|
|
53
|
+
readonly registry: ArchivableProcessRegistry;
|
|
54
|
+
readonly runtimeBus: RuntimeEventBus;
|
|
55
|
+
readonly sessionBroker: Pick<SharedSessionBroker, 'getSession'>;
|
|
56
|
+
}): FleetNeedsInputPushDeps {
|
|
57
|
+
attachFleetEmitBridge({ registry: input.registry, bus: input.runtimeBus });
|
|
58
|
+
const sessionBroker = input.sessionBroker;
|
|
59
|
+
return {
|
|
60
|
+
runtimeBus: input.runtimeBus,
|
|
61
|
+
sessionPresence: {
|
|
62
|
+
isAttached: (sessionId: string) => {
|
|
63
|
+
const session = sessionBroker.getSession(sessionId);
|
|
64
|
+
if (!session) return false;
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
return session.participants.some((participant) => now - participant.lastSeenAt < SURFACE_ATTACHED_FRESHNESS_MS);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install-self-check-startup.ts — the boot-time surfacing of the install
|
|
3
|
+
* self-check (install-self-check.ts). Called once from the runtime bootstrap,
|
|
4
|
+
* it wires the real host inputs (process exec path, the packaged root, the
|
|
5
|
+
* real daemon resolution, and existsSync) into the pure evaluator and prints
|
|
6
|
+
* any findings through the same SystemMessageRouter the rest of startup uses.
|
|
7
|
+
*
|
|
8
|
+
* It is intentionally cheap (existsSync-level, no network) and non-fatal:
|
|
9
|
+
* every failure is swallowed so a self-check can never block or crash boot.
|
|
10
|
+
* Each finding is printed as one high-priority system message ending in the
|
|
11
|
+
* exact repair command for the detected install kind.
|
|
12
|
+
*
|
|
13
|
+
* This module lives in the runtime layer and imports the daemon resolver from
|
|
14
|
+
* the cli layer; nothing in cli imports it back, so no import cycle forms.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
|
+
import { getGoodVibesPackageRoot, resolveGoodVibesDaemonExecutable } from '../cli/service-posture.ts';
|
|
19
|
+
import { runInstallSelfCheck } from './install-self-check.ts';
|
|
20
|
+
import type { SystemMessageRouter } from '../core/system-message-router.ts';
|
|
21
|
+
|
|
22
|
+
export function announceInstallSelfCheck(router: SystemMessageRouter): void {
|
|
23
|
+
try {
|
|
24
|
+
const findings = runInstallSelfCheck({
|
|
25
|
+
execPath: process.execPath,
|
|
26
|
+
packageRoot: getGoodVibesPackageRoot(),
|
|
27
|
+
daemon: resolveGoodVibesDaemonExecutable(),
|
|
28
|
+
fileExists: existsSync,
|
|
29
|
+
});
|
|
30
|
+
for (const finding of findings) {
|
|
31
|
+
router.high(`[Install] ${finding.summary} ${finding.detail} Repair: ${finding.repairCommand}`);
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Best-effort — an install self-check must never block or crash boot.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install-self-check.ts — cheap, non-fatal detection of an incomplete
|
|
3
|
+
* install, run at TUI startup and re-surfaced by `goodvibes doctor`.
|
|
4
|
+
*
|
|
5
|
+
* A packaged install (the standalone `install.sh` binary, or the
|
|
6
|
+
* `bun add -g` vendored package) is only usable if its release binaries are
|
|
7
|
+
* actually present: the app binary the user is running, plus the separate
|
|
8
|
+
* background daemon binary. Two things can go wrong after a partial or
|
|
9
|
+
* interrupted install:
|
|
10
|
+
* 1. the vendored release binaries were never downloaded into <root>/vendor
|
|
11
|
+
* (scripts/postinstall.js downloads them for packaged installs), and
|
|
12
|
+
* 2. the daemon launch path resolves to nothing runnable, so any
|
|
13
|
+
* daemon/control-plane/listener/web surface silently fails to start.
|
|
14
|
+
*
|
|
15
|
+
* This module decides those two facts from injected inputs only — an
|
|
16
|
+
* install kind, a package root, the resolved daemon executable, and a
|
|
17
|
+
* `fileExists` predicate — so it is pure and testable with fake paths and
|
|
18
|
+
* never touches the network. The production wiring (real existsSync + the
|
|
19
|
+
* real daemon resolution) lives in install-self-check-startup.ts and in the
|
|
20
|
+
* CLI status/doctor entrypoint; this file only makes the judgement.
|
|
21
|
+
*
|
|
22
|
+
* A `source` checkout run via `bun src/main.ts` is NOT an incomplete
|
|
23
|
+
* install — there is no vendor directory to populate and the daemon runs
|
|
24
|
+
* from source — so it is never flagged.
|
|
25
|
+
*
|
|
26
|
+
* Install-kind detection and the per-kind repair one-liner are reused from
|
|
27
|
+
* update-check.ts rather than re-derived here.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { join } from 'node:path';
|
|
31
|
+
import { detectInstallKind, fallbackUpdateCommand, type InstallKind } from './update-check.ts';
|
|
32
|
+
import { resolveArtifactNames } from './release-artifacts.ts';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The subset of a resolved daemon executable this check needs. Structurally
|
|
36
|
+
* matches the CLI's DaemonExecutableResolution without importing it from the
|
|
37
|
+
* cli layer (that would create an import cycle: cli imports this pure module,
|
|
38
|
+
* so this module must not import back into cli).
|
|
39
|
+
*/
|
|
40
|
+
export interface DaemonPathResolution {
|
|
41
|
+
readonly command: string;
|
|
42
|
+
/** How the path was resolved. 'fallback' means no runnable file was found — a bare PATH command. */
|
|
43
|
+
readonly source: string;
|
|
44
|
+
readonly absolute: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type InstallSelfCheckFindingId = 'missing-vendor-binaries' | 'broken-daemon-path';
|
|
48
|
+
|
|
49
|
+
export interface InstallSelfCheckFinding {
|
|
50
|
+
readonly id: InstallSelfCheckFindingId;
|
|
51
|
+
readonly summary: string;
|
|
52
|
+
readonly detail: string;
|
|
53
|
+
/** The exact command to run to repair this install kind. */
|
|
54
|
+
readonly repairCommand: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface InstallSelfCheckInput {
|
|
58
|
+
readonly installKind: InstallKind;
|
|
59
|
+
readonly packageRoot: string;
|
|
60
|
+
readonly platform: string;
|
|
61
|
+
readonly arch: string;
|
|
62
|
+
readonly daemon: DaemonPathResolution;
|
|
63
|
+
readonly fileExists: (path: string) => boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The exact repair command per install kind. A standalone `binary` install is
|
|
68
|
+
* repaired by re-running the installer (which places both binaries); the other
|
|
69
|
+
* non-binary kinds reuse update-check.ts's fallbackUpdateCommand verbatim.
|
|
70
|
+
*/
|
|
71
|
+
export function repairCommandForInstallKind(kind: InstallKind): string {
|
|
72
|
+
if (kind === 'binary') return 'curl -fsSL https://goodvibes.sh/install.sh | sh';
|
|
73
|
+
return fallbackUpdateCommand(kind);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Decide, from injected inputs only, whether this install is missing pieces.
|
|
78
|
+
* Returns zero findings for a source checkout (complete by definition) and for
|
|
79
|
+
* a healthy packaged install.
|
|
80
|
+
*/
|
|
81
|
+
export function evaluateInstallSelfCheck(input: InstallSelfCheckInput): InstallSelfCheckFinding[] {
|
|
82
|
+
if (input.installKind === 'source') return [];
|
|
83
|
+
|
|
84
|
+
const findings: InstallSelfCheckFinding[] = [];
|
|
85
|
+
const repairCommand = repairCommandForInstallKind(input.installKind);
|
|
86
|
+
const artifacts = resolveArtifactNames(input.platform, input.arch);
|
|
87
|
+
|
|
88
|
+
// (1) Missing vendored binaries. Only the vendored package install ships a
|
|
89
|
+
// <root>/vendor directory (postinstall.js downloads app + daemon there); a
|
|
90
|
+
// standalone compiled binary has none, so this check applies only to the
|
|
91
|
+
// bun-global-package kind.
|
|
92
|
+
if (input.installKind === 'bun-global-package' && artifacts) {
|
|
93
|
+
const vendorDir = join(input.packageRoot, 'vendor');
|
|
94
|
+
const missing = [artifacts.app, artifacts.daemon].filter(
|
|
95
|
+
(name) => !input.fileExists(join(vendorDir, name)),
|
|
96
|
+
);
|
|
97
|
+
if (missing.length > 0) {
|
|
98
|
+
findings.push({
|
|
99
|
+
id: 'missing-vendor-binaries',
|
|
100
|
+
summary: 'Vendored release binaries are missing from this install.',
|
|
101
|
+
detail: `${missing.join(', ')} not found under ${vendorDir}.`,
|
|
102
|
+
repairCommand,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// (2) Broken daemon path. The daemon runs as a separate binary; if the
|
|
108
|
+
// resolver fell back to a bare PATH command (nothing runnable was found on
|
|
109
|
+
// the packaged search paths) or the resolved absolute path does not exist,
|
|
110
|
+
// any daemon/control-plane/listener/web surface will fail to start.
|
|
111
|
+
const daemonMissing = input.daemon.source === 'fallback'
|
|
112
|
+
|| (input.daemon.absolute && !input.fileExists(input.daemon.command));
|
|
113
|
+
if (daemonMissing) {
|
|
114
|
+
findings.push({
|
|
115
|
+
id: 'broken-daemon-path',
|
|
116
|
+
summary: 'The background daemon binary could not be located.',
|
|
117
|
+
detail: input.daemon.source === 'fallback'
|
|
118
|
+
? `No daemon executable was found on the packaged search paths; the launcher falls back to a bare "${input.daemon.command}" on PATH.`
|
|
119
|
+
: `The resolved daemon path ${input.daemon.command} does not exist.`,
|
|
120
|
+
repairCommand,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return findings;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Convenience wrapper that detects the install kind from an exec path (reusing
|
|
129
|
+
* update-check.ts) and then evaluates. Platform/arch default to the running
|
|
130
|
+
* host but are injectable for tests. The daemon resolution and fileExists are
|
|
131
|
+
* always injected so this stays free of process/filesystem coupling of its own.
|
|
132
|
+
*/
|
|
133
|
+
export function runInstallSelfCheck(input: {
|
|
134
|
+
readonly execPath: string;
|
|
135
|
+
readonly packageRoot: string;
|
|
136
|
+
readonly daemon: DaemonPathResolution;
|
|
137
|
+
readonly fileExists: (path: string) => boolean;
|
|
138
|
+
readonly platform?: string;
|
|
139
|
+
readonly arch?: string;
|
|
140
|
+
}): InstallSelfCheckFinding[] {
|
|
141
|
+
return evaluateInstallSelfCheck({
|
|
142
|
+
installKind: detectInstallKind(input.execPath),
|
|
143
|
+
packageRoot: input.packageRoot,
|
|
144
|
+
platform: input.platform ?? process.platform,
|
|
145
|
+
arch: input.arch ?? process.arch,
|
|
146
|
+
daemon: input.daemon,
|
|
147
|
+
fileExists: input.fileExists,
|
|
148
|
+
});
|
|
149
|
+
}
|