@pellux/goodvibes-tui 0.19.23 → 0.19.25
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 +21 -0
- package/README.md +5 -5
- package/bin/goodvibes +5 -0
- package/bin/goodvibes-daemon +5 -0
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/cli/completion.ts +89 -0
- package/src/cli/config-overrides.ts +159 -0
- package/src/cli/endpoints.ts +63 -0
- package/src/cli/entrypoint.ts +155 -0
- package/src/cli/help.ts +122 -0
- package/src/cli/index.ts +8 -0
- package/src/cli/management-commands.ts +576 -0
- package/src/cli/management.ts +693 -0
- package/src/cli/parser.ts +367 -0
- package/src/cli/status.ts +112 -0
- package/src/cli/tui-startup.ts +32 -0
- package/src/cli/types.ts +63 -0
- package/src/cli-flags.ts +17 -55
- package/src/config/index.ts +1 -1
- package/src/config/secrets.ts +44 -0
- package/src/core/conversation.ts +36 -13
- package/src/daemon/cli.ts +62 -11
- package/src/input/command-registry.ts +3 -0
- package/src/input/commands/guidance-runtime.ts +9 -4
- package/src/input/commands/local-runtime.ts +21 -7
- package/src/input/commands/local-setup.ts +31 -38
- package/src/input/commands/onboarding-runtime.ts +14 -0
- package/src/input/commands/runtime-services.ts +9 -0
- package/src/input/commands.ts +2 -0
- package/src/input/feed-context-factory.ts +8 -1
- package/src/input/handler-feed.ts +13 -8
- package/src/input/handler-interactions.ts +266 -0
- package/src/input/handler-modal-stack.ts +23 -3
- package/src/input/handler-modal-token-routes.ts +23 -1
- package/src/input/handler-onboarding.ts +696 -0
- package/src/input/handler-picker-routes.ts +15 -7
- package/src/input/handler-ui-state.ts +58 -0
- package/src/input/handler.ts +120 -246
- package/src/input/onboarding/handler-onboarding-routes.ts +105 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +211 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +148 -0
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +712 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +218 -0
- package/src/input/onboarding/onboarding-wizard-rules.ts +224 -0
- package/src/input/onboarding/onboarding-wizard-state.ts +354 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +642 -0
- package/src/input/onboarding/onboarding-wizard-types.ts +170 -0
- package/src/input/onboarding/onboarding-wizard.ts +594 -0
- package/src/main.ts +32 -39
- package/src/panels/builtin/operations.ts +0 -10
- package/src/panels/index.ts +0 -1
- package/src/panels/panel-manager.ts +6 -2
- package/src/renderer/conversation-overlays.ts +6 -0
- package/src/renderer/help-overlay.ts +1 -1
- package/src/renderer/onboarding/onboarding-wizard.ts +533 -0
- package/src/renderer/panel-composite.ts +42 -5
- package/src/renderer/panel-workspace-bar.ts +5 -1
- package/src/runtime/bootstrap-core.ts +1 -0
- package/src/runtime/bootstrap.ts +123 -0
- package/src/runtime/onboarding/apply.ts +685 -0
- package/src/runtime/onboarding/derivation.ts +495 -0
- package/src/runtime/onboarding/index.ts +7 -0
- package/src/runtime/onboarding/markers.ts +161 -0
- package/src/runtime/onboarding/snapshot.ts +400 -0
- package/src/runtime/onboarding/state.ts +140 -0
- package/src/runtime/onboarding/types.ts +402 -0
- package/src/runtime/onboarding/verify.ts +233 -0
- package/src/runtime/ui-services.ts +16 -0
- package/src/shell/ui-openers.ts +12 -2
- package/src/version.ts +1 -1
- package/src/panels/welcome-panel.ts +0 -64
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - lifecycle.ts: save/shutdown helpers
|
|
11
11
|
*/
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
+
import net from 'node:net';
|
|
13
14
|
import { Orchestrator } from '../core/orchestrator.ts';
|
|
14
15
|
import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp/manager';
|
|
15
16
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers/tier-prompts';
|
|
@@ -292,6 +293,83 @@ export async function bootstrapRuntime(
|
|
|
292
293
|
|
|
293
294
|
const deferredStartup = createDeferredStartupCoordinator();
|
|
294
295
|
|
|
296
|
+
interface ExternalServiceBindingSnapshot {
|
|
297
|
+
readonly daemon: {
|
|
298
|
+
readonly host: string;
|
|
299
|
+
readonly port: number;
|
|
300
|
+
};
|
|
301
|
+
readonly httpListener: {
|
|
302
|
+
readonly host: string;
|
|
303
|
+
readonly port: number;
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface ExternalServicePortState {
|
|
308
|
+
readonly daemonPortInUse: boolean;
|
|
309
|
+
readonly httpListenerPortInUse: boolean;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const readExternalServiceBindings = (): ExternalServiceBindingSnapshot => ({
|
|
313
|
+
daemon: {
|
|
314
|
+
host: String(configManager.get('controlPlane.host') ?? '127.0.0.1'),
|
|
315
|
+
port: Number(configManager.get('controlPlane.port') ?? 3421),
|
|
316
|
+
},
|
|
317
|
+
httpListener: {
|
|
318
|
+
host: String(configManager.get('httpListener.host') ?? '127.0.0.1'),
|
|
319
|
+
port: Number(configManager.get('httpListener.port') ?? 3422),
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const getProbeHosts = (host: string): readonly string[] => {
|
|
324
|
+
const normalized = host.trim().toLowerCase();
|
|
325
|
+
if (normalized === '0.0.0.0') return ['127.0.0.1'];
|
|
326
|
+
if (normalized === '::' || normalized === '[::]') return ['::1'];
|
|
327
|
+
if (normalized.length === 0) return ['127.0.0.1'];
|
|
328
|
+
return [host];
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const isTcpPortInUse = async (host: string, port: number): Promise<boolean> => new Promise((resolve) => {
|
|
332
|
+
const socket = new net.Socket();
|
|
333
|
+
let settled = false;
|
|
334
|
+
const finish = (result: boolean): void => {
|
|
335
|
+
if (settled) return;
|
|
336
|
+
settled = true;
|
|
337
|
+
socket.destroy();
|
|
338
|
+
resolve(result);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
socket.setTimeout(250);
|
|
342
|
+
socket.once('connect', () => finish(true));
|
|
343
|
+
socket.once('timeout', () => finish(false));
|
|
344
|
+
socket.once('error', () => finish(false));
|
|
345
|
+
socket.connect(port, host);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const inspectExternalPorts = async (
|
|
349
|
+
bindings: readonly ExternalServiceBindingSnapshot[],
|
|
350
|
+
): Promise<ExternalServicePortState> => {
|
|
351
|
+
const daemonTargets = new Map<string, { readonly host: string; readonly port: number }>();
|
|
352
|
+
const listenerTargets = new Map<string, { readonly host: string; readonly port: number }>();
|
|
353
|
+
for (const binding of bindings) {
|
|
354
|
+
for (const host of getProbeHosts(binding.daemon.host)) {
|
|
355
|
+
daemonTargets.set(`${host}:${binding.daemon.port}`, { host, port: binding.daemon.port });
|
|
356
|
+
}
|
|
357
|
+
for (const host of getProbeHosts(binding.httpListener.host)) {
|
|
358
|
+
listenerTargets.set(`${host}:${binding.httpListener.port}`, { host, port: binding.httpListener.port });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const [daemonResults, listenerResults] = await Promise.all([
|
|
363
|
+
Promise.all([...daemonTargets.values()].map((target) => isTcpPortInUse(target.host, target.port))),
|
|
364
|
+
Promise.all([...listenerTargets.values()].map((target) => isTcpPortInUse(target.host, target.port))),
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
daemonPortInUse: daemonResults.some(Boolean),
|
|
369
|
+
httpListenerPortInUse: listenerResults.some(Boolean),
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
|
|
295
373
|
let externalServices: ExternalServicesHandle = {
|
|
296
374
|
daemonServer: null,
|
|
297
375
|
httpListener: null,
|
|
@@ -299,6 +377,49 @@ export async function bootstrapRuntime(
|
|
|
299
377
|
async stop(): Promise<void> {},
|
|
300
378
|
};
|
|
301
379
|
let externalServicesPromise: Promise<ExternalServicesHandle> | null = null;
|
|
380
|
+
let externalServiceBindings = readExternalServiceBindings();
|
|
381
|
+
let externalServicePortState: ExternalServicePortState = {
|
|
382
|
+
daemonPortInUse: false,
|
|
383
|
+
httpListenerPortInUse: false,
|
|
384
|
+
};
|
|
385
|
+
const inspectExternalServices = () => ({
|
|
386
|
+
daemonRunning: externalServices.daemonServer !== null,
|
|
387
|
+
daemonPortInUse: externalServicePortState.daemonPortInUse,
|
|
388
|
+
httpListenerRunning: externalServices.httpListener !== null,
|
|
389
|
+
httpListenerPortInUse: externalServicePortState.httpListenerPortInUse,
|
|
390
|
+
});
|
|
391
|
+
const platformExternalServices = uiServices.platform as typeof uiServices.platform & {
|
|
392
|
+
externalServices: NonNullable<typeof uiServices.platform.externalServices>;
|
|
393
|
+
};
|
|
394
|
+
platformExternalServices.externalServices = {
|
|
395
|
+
inspect: inspectExternalServices,
|
|
396
|
+
restart: async () => {
|
|
397
|
+
if (externalServicesPromise) {
|
|
398
|
+
try {
|
|
399
|
+
externalServices = await externalServicesPromise;
|
|
400
|
+
} catch {
|
|
401
|
+
// A failed previous startup should not prevent a restart attempt.
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
await externalServices.stop();
|
|
405
|
+
const previousBindings = externalServiceBindings;
|
|
406
|
+
externalServiceBindings = readExternalServiceBindings();
|
|
407
|
+
const daemonHomeDir = join(services.homeDirectory, '.goodvibes', 'daemon');
|
|
408
|
+
const companionTokenRecord = getOrCreateCompanionToken('tui', { daemonHomeDir });
|
|
409
|
+
externalServicesPromise = startExternalServices(
|
|
410
|
+
configManager,
|
|
411
|
+
runtimeBus,
|
|
412
|
+
hookDispatcher,
|
|
413
|
+
{ sharedDaemonToken: companionTokenRecord.token },
|
|
414
|
+
services,
|
|
415
|
+
);
|
|
416
|
+
externalServices = await externalServicesPromise;
|
|
417
|
+
controlPlaneRecentEventsRef.value = (limit) => externalServices.listRecentControlPlaneEvents(limit);
|
|
418
|
+
externalServicePortState = await inspectExternalPorts([previousBindings, externalServiceBindings]);
|
|
419
|
+
requestRender();
|
|
420
|
+
return inspectExternalServices();
|
|
421
|
+
},
|
|
422
|
+
};
|
|
302
423
|
deferredStartup.schedule({
|
|
303
424
|
label: 'plugins',
|
|
304
425
|
run: async () => {
|
|
@@ -349,6 +470,7 @@ export async function bootstrapRuntime(
|
|
|
349
470
|
if (prune.failedPaths.length > 0) {
|
|
350
471
|
logger.warn(`[bootstrap] Failed to prune ${prune.failedPaths.length} stale operator-token file(s) (permission/race): ${prune.failedPaths.join(', ')}`);
|
|
351
472
|
}
|
|
473
|
+
externalServiceBindings = readExternalServiceBindings();
|
|
352
474
|
externalServicesPromise = startExternalServices(
|
|
353
475
|
configManager,
|
|
354
476
|
runtimeBus,
|
|
@@ -358,6 +480,7 @@ export async function bootstrapRuntime(
|
|
|
358
480
|
);
|
|
359
481
|
externalServices = await externalServicesPromise;
|
|
360
482
|
controlPlaneRecentEventsRef.value = (limit) => externalServices.listRecentControlPlaneEvents(limit);
|
|
483
|
+
externalServicePortState = await inspectExternalPorts([externalServiceBindings]);
|
|
361
484
|
requestRender();
|
|
362
485
|
},
|
|
363
486
|
onError: (error) => {
|