@pellux/goodvibes-agent 0.1.56 → 0.1.57
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/.goodvibes/GOODVIBES.md +1 -1
- package/CHANGELOG.md +14 -9
- package/README.md +3 -3
- package/docs/README.md +1 -1
- package/docs/getting-started.md +3 -3
- package/docs/release-and-publishing.md +2 -2
- package/package.json +1 -3
- package/src/agent/routine-schedule-args.ts +219 -0
- package/src/agent/routine-schedule-format.ts +173 -0
- package/src/agent/routine-schedule-promotion.ts +3 -811
- package/src/agent/routine-schedule-receipts.ts +502 -0
- package/src/cli/agent-knowledge-command.ts +6 -6
- package/src/cli/help.ts +3 -25
- package/src/cli/package-verification.ts +23 -16
- package/src/cli/redaction.ts +4 -1
- package/src/cli/routines-command.ts +10 -6
- package/src/cli/service-posture.ts +47 -280
- package/src/cli/status.ts +0 -1
- package/src/config/secret-config.ts +0 -2
- package/src/input/agent-workspace-categories.ts +219 -0
- package/src/input/agent-workspace-editors.ts +143 -0
- package/src/input/agent-workspace-snapshot.ts +265 -0
- package/src/input/agent-workspace-types.ts +142 -0
- package/src/input/agent-workspace.ts +22 -766
- package/src/input/commands/agent-runtime-profile-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -4
- package/src/input/commands/guidance-runtime.ts +1 -2
- package/src/input/commands/health-runtime.ts +3 -65
- package/src/input/commands/knowledge.ts +7 -7
- package/src/input/commands/local-setup-review.ts +0 -61
- package/src/input/commands/local-setup-transfer.ts +0 -3
- package/src/input/commands/local-setup.ts +2 -15
- package/src/input/commands/planning-runtime.ts +4 -1
- package/src/input/commands/platform-access-runtime.ts +1 -10
- package/src/input/commands/platform-services-runtime.ts +0 -1
- package/src/input/commands/recall-query.ts +1 -1
- package/src/input/commands/routines-runtime.ts +10 -6
- package/src/input/commands/schedule-runtime.ts +10 -6
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +1 -14
- package/src/input/commands.ts +0 -4
- package/src/input/handler-onboarding.ts +10 -120
- package/src/input/onboarding/onboarding-wizard-apply.ts +5 -196
- package/src/input/onboarding/onboarding-wizard-constants.ts +8 -119
- package/src/input/onboarding/onboarding-wizard-helpers.ts +2 -53
- package/src/input/onboarding/onboarding-wizard-rules.ts +2 -236
- package/src/input/onboarding/onboarding-wizard-state.ts +1 -69
- package/src/input/onboarding/onboarding-wizard-steps.ts +584 -737
- package/src/input/onboarding/onboarding-wizard-types.ts +8 -26
- package/src/input/onboarding/onboarding-wizard.ts +4 -109
- package/src/input/settings-modal-agent-policy.ts +10 -0
- package/src/input/settings-modal-types.ts +2 -4
- package/src/input/settings-modal.ts +3 -1
- package/src/input/submission-router.ts +0 -1
- package/src/panels/approval-panel.ts +1 -2
- package/src/panels/builtin/operations.ts +1 -2
- package/src/panels/knowledge-panel.ts +2 -2
- package/src/panels/project-planning-panel.ts +4 -1
- package/src/panels/provider-health-domains.ts +0 -22
- package/src/panels/provider-health-panel.ts +1 -5
- package/src/panels/session-browser-panel.ts +0 -5
- package/src/panels/tasks-panel.ts +2 -64
- package/src/renderer/agent-workspace.ts +1 -1
- package/src/renderer/help-overlay.ts +1 -2
- package/src/renderer/semantic-diff.ts +1 -1
- package/src/renderer/settings-modal-helpers.ts +0 -16
- package/src/renderer/settings-modal.ts +3 -5
- package/src/runtime/bootstrap-hook-bridge.ts +0 -3
- package/src/runtime/bootstrap-shell.ts +2 -1
- package/src/runtime/bootstrap.ts +1 -1
- package/src/runtime/index.ts +0 -1
- package/src/runtime/onboarding/derivation.ts +1 -28
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +1 -4
- package/src/runtime/services.ts +4 -23
- package/src/runtime/ui-read-models.ts +4 -3
- package/src/shell/service-settings-sync.ts +15 -244
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/wrfc-agent-guard.ts +3 -3
- package/src/verification/live-verifier.ts +11 -5
- package/src/verification/verification-ledger.ts +3 -6
- package/src/version.ts +1 -1
- package/src/input/commands/agent-externalized-tui.ts +0 -73
- package/src/input/commands/cloudflare-runtime.ts +0 -385
- package/src/input/handler-onboarding-cloudflare.ts +0 -322
- package/src/input/onboarding/onboarding-runtime-status.ts +0 -87
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +0 -494
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +0 -199
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +0 -130
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +0 -762
- package/src/runtime/cloudflare-control-plane.ts +0 -350
- package/src/runtime/sandbox-public-gaps.ts +0 -358
|
@@ -4,30 +4,15 @@ import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
4
4
|
import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
5
5
|
import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
|
|
6
6
|
import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardApplyFeedback } from './onboarding/onboarding-wizard.ts';
|
|
7
|
-
import { handleCloudflareOnboardingActionForHandler, maybeProvisionCloudflareOnFinalApplyForHandler } from './handler-onboarding-cloudflare.ts';
|
|
8
7
|
import { applyOnboardingRequest, collectOnboardingSnapshot, verifyOnboardingRequest } from '../runtime/onboarding/index.ts';
|
|
9
8
|
import type { OnboardingApplyRequest, OnboardingVerificationItem } from '../runtime/onboarding/index.ts';
|
|
10
9
|
import type { ModelPickerTarget } from './model-picker.ts';
|
|
11
10
|
import { captureOnboardingWizardSnapshot, restoreOnboardingWizardSnapshot } from './handler-ui-state.ts';
|
|
12
11
|
import type { InputHandler } from './handler.ts';
|
|
13
|
-
import {
|
|
14
|
-
formatRuntimeActiveSuccessMessage,
|
|
15
|
-
getRuntimeEndpointStatus,
|
|
16
|
-
isRuntimeEndpointActive,
|
|
17
|
-
isRuntimeEndpointOccupyingConfiguredPort,
|
|
18
|
-
runtimePortDiagnostic,
|
|
19
|
-
type OnboardingExternalServiceState,
|
|
20
|
-
type OnboardingRuntimeEndpoint,
|
|
21
|
-
} from './onboarding/onboarding-runtime-status.ts';
|
|
22
12
|
|
|
23
13
|
export interface OnboardingRuntimePosture {
|
|
24
|
-
readonly
|
|
25
|
-
readonly
|
|
26
|
-
readonly restartOnFailure: boolean;
|
|
27
|
-
readonly expectedDaemon: boolean;
|
|
28
|
-
readonly expectedHttpListener: boolean;
|
|
29
|
-
readonly serverBacked: boolean;
|
|
30
|
-
readonly remoteExposure: boolean;
|
|
14
|
+
readonly externalServiceManaged: true;
|
|
15
|
+
readonly mutationAllowed: false;
|
|
31
16
|
}
|
|
32
17
|
|
|
33
18
|
function extractAuthorizationCode(input: string): string | null {
|
|
@@ -42,16 +27,6 @@ function extractAuthorizationCode(input: string): string | null {
|
|
|
42
27
|
}
|
|
43
28
|
}
|
|
44
29
|
|
|
45
|
-
function isLoopbackHostValue(value: string | null | undefined): boolean {
|
|
46
|
-
const normalized = (value ?? '').trim().toLowerCase();
|
|
47
|
-
if (normalized.length === 0) return false;
|
|
48
|
-
return normalized === 'localhost'
|
|
49
|
-
|| normalized === '::1'
|
|
50
|
-
|| normalized === '[::1]'
|
|
51
|
-
|| normalized === '0:0:0:0:0:0:0:1'
|
|
52
|
-
|| /^127(?:\.\d{1,3}){3}$/.test(normalized);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
30
|
function onboardingVerificationStatusRank(item: OnboardingVerificationItem): number {
|
|
56
31
|
if (item.status === 'fail') return 3;
|
|
57
32
|
if (item.status === 'warn') return 2;
|
|
@@ -87,53 +62,6 @@ function formatOnboardingApplyCompletionMessage(items: readonly OnboardingVerifi
|
|
|
87
62
|
].join('\n');
|
|
88
63
|
}
|
|
89
64
|
|
|
90
|
-
function getRuntimeEndpointBinding(
|
|
91
|
-
handler: InputHandler,
|
|
92
|
-
request: OnboardingApplyRequest,
|
|
93
|
-
endpoint: OnboardingRuntimeEndpoint,
|
|
94
|
-
): { readonly label: string; readonly host: string; readonly port: number } {
|
|
95
|
-
const hostKey = endpoint === 'daemon' ? 'controlPlane.host' : 'httpListener.host';
|
|
96
|
-
const portKey = endpoint === 'daemon' ? 'controlPlane.port' : 'httpListener.port';
|
|
97
|
-
const fallbackHost = '127.0.0.1';
|
|
98
|
-
const fallbackPort = endpoint === 'daemon' ? 3421 : 3422;
|
|
99
|
-
const rawHost = handler.getOnboardingConfigValue(request, hostKey);
|
|
100
|
-
const rawPort = handler.getOnboardingConfigValue(request, portKey);
|
|
101
|
-
const parsedPort = typeof rawPort === 'number' ? rawPort : Number(rawPort);
|
|
102
|
-
return {
|
|
103
|
-
label: endpoint === 'daemon' ? 'GoodVibes daemon' : 'HTTP listener',
|
|
104
|
-
host: String(rawHost ?? fallbackHost),
|
|
105
|
-
port: Number.isFinite(parsedPort) ? parsedPort : fallbackPort,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function formatRuntimeActiveFailureMessage(
|
|
110
|
-
handler: InputHandler,
|
|
111
|
-
request: OnboardingApplyRequest,
|
|
112
|
-
endpoint: OnboardingRuntimeEndpoint,
|
|
113
|
-
state: OnboardingExternalServiceState | undefined,
|
|
114
|
-
): string {
|
|
115
|
-
const binding = getRuntimeEndpointBinding(handler, request, endpoint);
|
|
116
|
-
const portInUse = endpoint === 'daemon' ? state?.daemonPortInUse : state?.httpListenerPortInUse;
|
|
117
|
-
const status = getRuntimeEndpointStatus(state, endpoint);
|
|
118
|
-
const impact = endpoint === 'daemon'
|
|
119
|
-
? 'browser, LAN, and service-backed GoodVibes surfaces may be unavailable until the daemon is running there.'
|
|
120
|
-
: 'incoming webhooks and event surfaces will not receive traffic until the listener is running there.';
|
|
121
|
-
return `${binding.label} is enabled for ${binding.host}:${binding.port}, but onboarding could not confirm an embedded or verified external service after restart. ${runtimePortDiagnostic(binding, portInUse, status)} Settings were saved; ${impact}`;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function formatRuntimeStoppedFailureMessage(
|
|
125
|
-
handler: InputHandler,
|
|
126
|
-
request: OnboardingApplyRequest,
|
|
127
|
-
endpoint: OnboardingRuntimeEndpoint,
|
|
128
|
-
state?: OnboardingExternalServiceState,
|
|
129
|
-
): string {
|
|
130
|
-
const binding = getRuntimeEndpointBinding(handler, request, endpoint);
|
|
131
|
-
const status = getRuntimeEndpointStatus(state, endpoint);
|
|
132
|
-
const disabledSurface = endpoint === 'daemon' ? 'server-backed surfaces' : 'incoming event surfaces';
|
|
133
|
-
const statusDetail = status ? ` ${runtimePortDiagnostic(binding, undefined, status)}` : '';
|
|
134
|
-
return `${binding.label} was disabled for ${disabledSurface}, but ${binding.host}:${binding.port} is still occupied. Settings were saved; another GoodVibes process or external service may still be running on that port.${statusDetail}`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
65
|
function showOnboardingApplyFeedbackForHandler(handler: InputHandler, feedback: OnboardingWizardApplyFeedback): void {
|
|
138
66
|
handler.onboardingWizard.setApplyFeedback(feedback);
|
|
139
67
|
const reviewIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === 'review');
|
|
@@ -241,18 +169,6 @@ export async function handleOnboardingActionForHandler(handler: InputHandler, ac
|
|
|
241
169
|
continueOnboardingSection(handler);
|
|
242
170
|
return;
|
|
243
171
|
}
|
|
244
|
-
if (action.startsWith('cloudflare-')) {
|
|
245
|
-
await handleCloudflareOnboardingActionForHandler(handler, action as Extract<OnboardingWizardAction,
|
|
246
|
-
| 'cloudflare-token-requirements'
|
|
247
|
-
| 'cloudflare-create-operational-token'
|
|
248
|
-
| 'cloudflare-discover'
|
|
249
|
-
| 'cloudflare-validate'
|
|
250
|
-
| 'cloudflare-provision'
|
|
251
|
-
| 'cloudflare-verify'
|
|
252
|
-
| 'cloudflare-disable'
|
|
253
|
-
>);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
172
|
if (action !== 'apply') return;
|
|
257
173
|
if (handler.onboardingApplyPending) return;
|
|
258
174
|
const blockers = handler.onboardingWizard.getBlockingFieldLabels();
|
|
@@ -298,8 +214,6 @@ export async function handleOnboardingActionForHandler(handler: InputHandler, ac
|
|
|
298
214
|
? { ...item, status: 'warn' }
|
|
299
215
|
: item));
|
|
300
216
|
verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...runtimeWarnings]);
|
|
301
|
-
const cloudflareItems = await maybeProvisionCloudflareOnFinalApplyForHandler(handler);
|
|
302
|
-
verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...cloudflareItems]);
|
|
303
217
|
}
|
|
304
218
|
} catch (error) {
|
|
305
219
|
showOnboardingApplyFeedbackForHandler(handler, {
|
|
@@ -564,44 +478,20 @@ export function getOnboardingConfigValueForHandler(handler: InputHandler, reques
|
|
|
564
478
|
}
|
|
565
479
|
|
|
566
480
|
export function getOnboardingRuntimePostureForHandler(handler: InputHandler, request: OnboardingApplyRequest): OnboardingRuntimePosture {
|
|
567
|
-
const getConfigValue = (key: string): unknown => handler.getOnboardingConfigValue(request, key);
|
|
568
|
-
const serviceEnabled = getConfigValue('service.enabled') === true;
|
|
569
|
-
const serviceAutostart = getConfigValue('service.autostart') === true;
|
|
570
|
-
const restartOnFailure = getConfigValue('service.restartOnFailure') === true;
|
|
571
|
-
const daemonEnabled = getConfigValue('danger.daemon') === true || getConfigValue('controlPlane.enabled') === true;
|
|
572
|
-
const listenerEnabled = getConfigValue('danger.httpListener') === true;
|
|
573
|
-
const webEnabled = getConfigValue('web.enabled') === true;
|
|
574
|
-
const controlPlaneRemote = getConfigValue('controlPlane.hostMode') === 'network'
|
|
575
|
-
|| (getConfigValue('controlPlane.hostMode') === 'custom'
|
|
576
|
-
&& !isLoopbackHostValue(String(getConfigValue('controlPlane.host') ?? '')))
|
|
577
|
-
|| getConfigValue('controlPlane.allowRemote') === true;
|
|
578
|
-
const listenerRemote = getConfigValue('httpListener.hostMode') === 'network'
|
|
579
|
-
|| (getConfigValue('httpListener.hostMode') === 'custom'
|
|
580
|
-
&& !isLoopbackHostValue(String(getConfigValue('httpListener.host') ?? '')));
|
|
581
|
-
const webRemote = getConfigValue('web.hostMode') === 'network'
|
|
582
|
-
|| (getConfigValue('web.hostMode') === 'custom'
|
|
583
|
-
&& !isLoopbackHostValue(String(getConfigValue('web.host') ?? '')));
|
|
584
|
-
const remoteExposure = controlPlaneRemote || listenerRemote || webRemote;
|
|
585
|
-
|
|
586
481
|
return {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
restartOnFailure,
|
|
590
|
-
expectedDaemon: daemonEnabled || webEnabled,
|
|
591
|
-
expectedHttpListener: listenerEnabled,
|
|
592
|
-
serverBacked: serviceEnabled || daemonEnabled || listenerEnabled || webEnabled,
|
|
593
|
-
remoteExposure,
|
|
482
|
+
externalServiceManaged: true,
|
|
483
|
+
mutationAllowed: false,
|
|
594
484
|
};
|
|
595
485
|
}
|
|
596
486
|
|
|
597
487
|
export async function restartOnboardingExternalServicesIfNeededForHandler(handler: InputHandler, request: OnboardingApplyRequest): Promise<OnboardingVerificationItem[]> {
|
|
598
488
|
const externalServices = handler.uiServices.platform.externalServices;
|
|
599
489
|
const state = externalServices?.inspect();
|
|
600
|
-
const
|
|
490
|
+
const serviceStatus = state?.daemonStatus?.reason ?? (state?.daemonRunning ? 'external GoodVibes service appears active' : 'external GoodVibes service is not verified from this shell');
|
|
601
491
|
return [{
|
|
602
|
-
id: 'runtime:external-
|
|
492
|
+
id: 'runtime:external-service-owned',
|
|
603
493
|
status: 'pass',
|
|
604
|
-
message: `GoodVibes Agent did not start, stop,
|
|
494
|
+
message: `GoodVibes Agent did not start, stop, restart, or reconfigure service lifecycle. ${serviceStatus}`,
|
|
605
495
|
target: 'service',
|
|
606
496
|
}];
|
|
607
497
|
}
|
|
@@ -610,11 +500,11 @@ export function verifyOnboardingRuntimePostureForHandler(handler: InputHandler,
|
|
|
610
500
|
const externalServices = handler.uiServices.platform.externalServices;
|
|
611
501
|
const externalState = externalServices?.inspect();
|
|
612
502
|
return [{
|
|
613
|
-
id: 'runtime:external-
|
|
503
|
+
id: 'runtime:external-service-owned',
|
|
614
504
|
status: 'pass',
|
|
615
505
|
message: externalState
|
|
616
|
-
? '
|
|
617
|
-
: '
|
|
506
|
+
? 'GoodVibes service lifecycle is externally managed; Agent onboarding did not request shutdown, startup, restart, bind, or surface changes.'
|
|
507
|
+
: 'GoodVibes service lifecycle is externally managed; no local service controller is required for Agent onboarding.',
|
|
618
508
|
target: 'service',
|
|
619
509
|
}];
|
|
620
510
|
}
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OnboardingApplyOperation, OnboardingApplyRequest } from '../../runtime/onboarding/index.ts';
|
|
2
2
|
import { formatProviderModel } from '../../config/provider-model.ts';
|
|
3
|
-
import { getServerSurfaceFeatureFlags } from '../../runtime/surface-feature-flags.ts';
|
|
4
|
-
import {
|
|
5
|
-
buildCloudflareApiTokenRef,
|
|
6
|
-
buildCloudflareOperationalTokenRef,
|
|
7
|
-
getCloudflareBatchMode,
|
|
8
|
-
getCloudflareComponentSelection,
|
|
9
|
-
getCloudflareSetupSource,
|
|
10
|
-
shouldShowCloudflareStep,
|
|
11
|
-
} from './onboarding-wizard-cloudflare.ts';
|
|
12
|
-
import {
|
|
13
|
-
EXTERNAL_SURFACE_SPECS,
|
|
14
|
-
getExternalSurfaceAutoStartDefaultValue,
|
|
15
|
-
getExternalSurfaceAutoStartFieldId,
|
|
16
|
-
isExternalSurfaceSelectedByDefault,
|
|
17
|
-
} from './onboarding-wizard-external-surfaces.ts';
|
|
18
|
-
import { buildGoodVibesSecretKey, buildGoodVibesSecretRef, isSecretReferenceValue } from './onboarding-wizard-helpers.ts';
|
|
19
3
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
20
4
|
|
|
21
5
|
export function buildOnboardingApplyRequest(controller: OnboardingWizardController): OnboardingApplyRequest {
|
|
@@ -26,16 +10,6 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
26
10
|
): void => {
|
|
27
11
|
operations.push({ kind: 'set-config', key, value });
|
|
28
12
|
};
|
|
29
|
-
const enableFeatureFlags = (flagIds: readonly string[]): void => {
|
|
30
|
-
for (const flagId of flagIds) setConfig(`featureFlags.${flagId}`, 'enabled');
|
|
31
|
-
};
|
|
32
|
-
const acknowledge = (target: OnboardingAcknowledgementTarget, fieldId: string): void => {
|
|
33
|
-
operations.push({
|
|
34
|
-
kind: 'acknowledge',
|
|
35
|
-
target,
|
|
36
|
-
acknowledged: controller.getBooleanFieldValue(fieldId, false),
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
13
|
const setSecret = (key: string, value: string): void => {
|
|
40
14
|
if (value.length === 0) return;
|
|
41
15
|
const medium = controller.getSelectedSecretMedium();
|
|
@@ -47,36 +21,10 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
47
21
|
medium,
|
|
48
22
|
});
|
|
49
23
|
};
|
|
50
|
-
const setMaskedConfig = (
|
|
51
|
-
key: Extract<OnboardingApplyOperation, { kind: 'set-config' }>['key'],
|
|
52
|
-
value: string,
|
|
53
|
-
): void => {
|
|
54
|
-
if (value.length === 0 || isSecretReferenceValue(value)) {
|
|
55
|
-
setConfig(key, value);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const secretKey = buildGoodVibesSecretKey(key);
|
|
60
|
-
setSecret(secretKey, value);
|
|
61
|
-
setConfig(key, buildGoodVibesSecretRef(secretKey));
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const requestedAdminPassword = controller.getStringFieldValue('accounts.admin-password', '');
|
|
65
|
-
const shouldEnsureAuthUser = controller.requiresAuthBootstrap() || requestedAdminPassword.length > 0;
|
|
66
|
-
if (shouldEnsureAuthUser) {
|
|
67
|
-
operations.push({
|
|
68
|
-
kind: 'ensure-auth-user',
|
|
69
|
-
username: controller.getStringFieldValue('accounts.admin-username', controller.getDefaultAdminUsername()),
|
|
70
|
-
password: requestedAdminPassword,
|
|
71
|
-
roles: ['admin'],
|
|
72
|
-
createSession: true,
|
|
73
|
-
retireBootstrapCredential: controller.hasBootstrapCredentialPresent(),
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
24
|
|
|
77
|
-
// GoodVibes Agent
|
|
78
|
-
//
|
|
79
|
-
//
|
|
25
|
+
// GoodVibes Agent onboarding owns only Agent-local setup and provider
|
|
26
|
+
// routing. Server lifecycle, non-Agent entrypoints, and non-Agent knowledge
|
|
27
|
+
// segments are managed elsewhere.
|
|
80
28
|
|
|
81
29
|
const defaultModel = controller.modelSelectionState.get('main');
|
|
82
30
|
if (defaultModel && defaultModel.enabled !== false && defaultModel.providerId.length > 0 && defaultModel.modelId.length > 0) {
|
|
@@ -86,152 +34,13 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
86
34
|
setConfig('behavior.hitlMode', controller.getStringFieldValue('experience.hitl', controller.runtimeSnapshot?.runtimeDefaults.behavior.hitlMode ?? 'balanced'));
|
|
87
35
|
setConfig('behavior.guidanceMode', controller.getStringFieldValue('experience.guidance', controller.runtimeSnapshot?.runtimeDefaults.behavior.guidanceMode ?? 'minimal'));
|
|
88
36
|
setConfig('permissions.mode', controller.getStringFieldValue('experience.permissions', controller.runtimeSnapshot?.runtimeDefaults.permissionsMode ?? 'prompt'));
|
|
89
|
-
setConfig('storage.secretPolicy', controller.getStringFieldValue('
|
|
37
|
+
setConfig('storage.secretPolicy', controller.getStringFieldValue('agent-setup.secret-policy', controller.runtimeSnapshot?.runtimeDefaults.secretStoragePolicy ?? 'preferred_secure'));
|
|
90
38
|
|
|
91
39
|
setSecret('OPENAI_API_KEY', controller.getStringFieldValue('providers.openai-api-key', ''));
|
|
92
40
|
|
|
93
|
-
if (shouldShowCloudflareStep(controller)) {
|
|
94
|
-
addCloudflareOperations(controller, operations, setSecret);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const externalIntegrations = controller.isCapabilitySelected('external-integrations');
|
|
98
|
-
const enabledExternalSurfaceIds: string[] = [];
|
|
99
|
-
for (const surface of EXTERNAL_SURFACE_SPECS) {
|
|
100
|
-
const selected = externalIntegrations
|
|
101
|
-
&& controller.getBooleanFieldValue(
|
|
102
|
-
surface.enabledFieldId,
|
|
103
|
-
isExternalSurfaceSelectedByDefault(surface, controller.runtimeSnapshot),
|
|
104
|
-
);
|
|
105
|
-
const autoStart = selected
|
|
106
|
-
&& controller.getStringFieldValue(
|
|
107
|
-
getExternalSurfaceAutoStartFieldId(surface),
|
|
108
|
-
getExternalSurfaceAutoStartDefaultValue(surface, controller.runtimeSnapshot),
|
|
109
|
-
) === 'yes';
|
|
110
|
-
setConfig(surface.enabledConfigKey, autoStart);
|
|
111
|
-
if (!selected) continue;
|
|
112
|
-
enabledExternalSurfaceIds.push(surface.id);
|
|
113
|
-
|
|
114
|
-
for (const setupField of surface.fields) {
|
|
115
|
-
const fallback = setupField.defaultValue(controller.runtimeSnapshot);
|
|
116
|
-
if (setupField.valueType === 'number') {
|
|
117
|
-
setConfig(
|
|
118
|
-
setupField.configKey,
|
|
119
|
-
controller.getNumberFieldValue(
|
|
120
|
-
setupField.id,
|
|
121
|
-
setupField.defaultNumber ?? (Number.parseInt(fallback, 10) || 0),
|
|
122
|
-
setupField.min,
|
|
123
|
-
setupField.max,
|
|
124
|
-
),
|
|
125
|
-
);
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const value = controller.getStringFieldValue(setupField.id, fallback);
|
|
130
|
-
if (setupField.kind === 'masked') setMaskedConfig(setupField.configKey, value);
|
|
131
|
-
else setConfig(setupField.configKey, value);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
enableFeatureFlags(getServerSurfaceFeatureFlags({
|
|
135
|
-
serverBacked: false,
|
|
136
|
-
web: false,
|
|
137
|
-
externalSurfaces: enabledExternalSurfaceIds,
|
|
138
|
-
}));
|
|
139
|
-
|
|
140
|
-
acknowledge('providers', 'providers.reviewed');
|
|
141
|
-
acknowledge('subscriptions', 'accounts.subscriptions');
|
|
142
|
-
acknowledge('auth', 'accounts.auth');
|
|
143
|
-
|
|
144
41
|
return {
|
|
145
42
|
mode: controller.mode,
|
|
146
43
|
source: 'wizard',
|
|
147
44
|
operations,
|
|
148
45
|
};
|
|
149
46
|
}
|
|
150
|
-
|
|
151
|
-
function addCloudflareOperations(
|
|
152
|
-
controller: OnboardingWizardController,
|
|
153
|
-
operations: OnboardingApplyOperation[],
|
|
154
|
-
setSecret: (key: string, value: string) => void,
|
|
155
|
-
): void {
|
|
156
|
-
const setConfig = (
|
|
157
|
-
key: Extract<OnboardingApplyOperation, { kind: 'set-config' }>['key'],
|
|
158
|
-
value: unknown,
|
|
159
|
-
): void => {
|
|
160
|
-
operations.push({ kind: 'set-config', key, value });
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const config = controller.runtimeSnapshot?.config.cloudflare;
|
|
164
|
-
const enabledDefault = controller.isCapabilitySelected('cloudflare-batch') || config?.enabled === true;
|
|
165
|
-
const enabled = controller.getBooleanFieldValue('cloudflare.enabled', enabledDefault);
|
|
166
|
-
const components = getCloudflareComponentSelection(controller);
|
|
167
|
-
const batchMode = enabled ? getCloudflareBatchMode(controller) : 'off';
|
|
168
|
-
const setupSource = getCloudflareSetupSource(controller);
|
|
169
|
-
const existingApiTokenRef = config?.apiTokenRef ?? '';
|
|
170
|
-
let apiTokenRef = existingApiTokenRef;
|
|
171
|
-
|
|
172
|
-
if (!enabled) {
|
|
173
|
-
setConfig('cloudflare.enabled', false);
|
|
174
|
-
setConfig('batch.mode', 'off');
|
|
175
|
-
setConfig('batch.queueBackend', 'local');
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (setupSource === 'operational-env') {
|
|
180
|
-
apiTokenRef = buildCloudflareApiTokenRef(
|
|
181
|
-
controller.getStringFieldValue('cloudflare.operational-env-name', 'CLOUDFLARE_API_TOKEN'),
|
|
182
|
-
);
|
|
183
|
-
} else if (setupSource === 'operational-token') {
|
|
184
|
-
const token = controller.getStringFieldValue('cloudflare.operational-token', '');
|
|
185
|
-
if (token.length > 0) {
|
|
186
|
-
setSecret('CLOUDFLARE_API_TOKEN', token);
|
|
187
|
-
apiTokenRef = buildCloudflareOperationalTokenRef();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
setConfig('cloudflare.enabled', true);
|
|
192
|
-
setConfig('cloudflare.freeTierMode', controller.getStringFieldValue('cloudflare.free-tier-mode', config?.freeTierMode === false ? 'no' : 'yes') === 'yes');
|
|
193
|
-
setConfig('cloudflare.accountId', controller.getStringFieldValue('cloudflare.account-id', config?.accountId ?? ''));
|
|
194
|
-
setConfig('cloudflare.apiTokenRef', apiTokenRef);
|
|
195
|
-
setConfig('cloudflare.zoneId', controller.getStringFieldValue('cloudflare.zone-id', config?.zoneId ?? ''));
|
|
196
|
-
setConfig('cloudflare.zoneName', controller.getStringFieldValue('cloudflare.zone-name', config?.zoneName ?? ''));
|
|
197
|
-
setConfig('cloudflare.workerName', controller.getStringFieldValue('cloudflare.worker-name', config?.workerName ?? 'goodvibes-batch-worker'));
|
|
198
|
-
setConfig('cloudflare.workerSubdomain', controller.getStringFieldValue('cloudflare.worker-subdomain', config?.workerSubdomain ?? ''));
|
|
199
|
-
setConfig('cloudflare.workerHostname', controller.getStringFieldValue('cloudflare.worker-hostname', config?.workerHostname ?? ''));
|
|
200
|
-
setConfig('cloudflare.workerBaseUrl', controller.getStringFieldValue('cloudflare.worker-base-url', config?.workerBaseUrl ?? ''));
|
|
201
|
-
setConfig('cloudflare.daemonBaseUrl', controller.getStringFieldValue('cloudflare.daemon-base-url', config?.daemonBaseUrl ?? ''));
|
|
202
|
-
setConfig('cloudflare.daemonHostname', controller.getStringFieldValue('cloudflare.daemon-hostname', config?.daemonHostname ?? ''));
|
|
203
|
-
setConfig('cloudflare.workerCron', controller.getStringFieldValue('cloudflare.worker-cron', config?.workerCron ?? '*/5 * * * *'));
|
|
204
|
-
setConfig('cloudflare.queueName', controller.getStringFieldValue('cloudflare.queue-name', config?.queueName ?? 'goodvibes-batch'));
|
|
205
|
-
setConfig('cloudflare.deadLetterQueueName', controller.getStringFieldValue('cloudflare.dead-letter-queue-name', config?.deadLetterQueueName ?? 'goodvibes-batch-dlq'));
|
|
206
|
-
setConfig('cloudflare.tunnelName', controller.getStringFieldValue('cloudflare.tunnel-name', config?.tunnelName ?? 'goodvibes-agent-daemon'));
|
|
207
|
-
setConfig('cloudflare.tunnelId', controller.getStringFieldValue('cloudflare.tunnel-id', config?.tunnelId ?? ''));
|
|
208
|
-
setConfig('cloudflare.tunnelTokenRef', controller.getStringFieldValue('cloudflare.tunnel-token-ref', config?.tunnelTokenRef ?? ''));
|
|
209
|
-
setConfig('cloudflare.accessAppId', controller.getStringFieldValue('cloudflare.access-app-id', config?.accessAppId ?? ''));
|
|
210
|
-
setConfig('cloudflare.accessServiceTokenId', controller.getStringFieldValue('cloudflare.access-service-token-id', config?.accessServiceTokenId ?? ''));
|
|
211
|
-
setConfig('cloudflare.accessServiceTokenRef', controller.getStringFieldValue('cloudflare.access-service-token-ref', config?.accessServiceTokenRef ?? ''));
|
|
212
|
-
setConfig('cloudflare.kvNamespaceName', controller.getStringFieldValue('cloudflare.kv-namespace-name', config?.kvNamespaceName ?? 'goodvibes-runtime'));
|
|
213
|
-
setConfig('cloudflare.kvNamespaceId', controller.getStringFieldValue('cloudflare.kv-namespace-id', config?.kvNamespaceId ?? ''));
|
|
214
|
-
setConfig('cloudflare.durableObjectNamespaceName', controller.getStringFieldValue('cloudflare.do-namespace-name', config?.durableObjectNamespaceName ?? 'GoodVibesCoordinator'));
|
|
215
|
-
setConfig('cloudflare.durableObjectNamespaceId', controller.getStringFieldValue('cloudflare.do-namespace-id', config?.durableObjectNamespaceId ?? ''));
|
|
216
|
-
setConfig('cloudflare.r2BucketName', controller.getStringFieldValue('cloudflare.r2-bucket-name', config?.r2BucketName ?? 'goodvibes-artifacts'));
|
|
217
|
-
setConfig('cloudflare.secretsStoreName', controller.getStringFieldValue('cloudflare.secrets-store-name', config?.secretsStoreName ?? 'goodvibes'));
|
|
218
|
-
setConfig('cloudflare.secretsStoreId', controller.getStringFieldValue('cloudflare.secrets-store-id', config?.secretsStoreId ?? ''));
|
|
219
|
-
setConfig('cloudflare.maxQueueOpsPerDay', controller.getNumberFieldValue('cloudflare.max-queue-ops-per-day', config?.maxQueueOpsPerDay ?? 10000, 1));
|
|
220
|
-
setConfig('batch.mode', batchMode);
|
|
221
|
-
setConfig('batch.queueBackend', batchMode !== 'off' && components.queues ? 'cloudflare' : 'local');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function addNetworkOperations(
|
|
225
|
-
_controller: OnboardingWizardController,
|
|
226
|
-
_operations: OnboardingApplyOperation[],
|
|
227
|
-
_customNetwork: boolean,
|
|
228
|
-
_enabled: {
|
|
229
|
-
readonly controlPlane: boolean;
|
|
230
|
-
readonly controlPlaneRemote: boolean;
|
|
231
|
-
readonly httpListener: boolean;
|
|
232
|
-
readonly web: boolean;
|
|
233
|
-
},
|
|
234
|
-
): void {
|
|
235
|
-
// Agent onboarding intentionally never mutates daemon/listener/web bind posture.
|
|
236
|
-
// Network fields are advisory for the external daemon owner.
|
|
237
|
-
}
|
|
@@ -1,57 +1,20 @@
|
|
|
1
|
-
import type { OnboardingStep1CapabilityItem } from '../../runtime/onboarding/index.ts';
|
|
2
1
|
import type { OnboardingWizardRadioOption, OnboardingWizardStepId } from './onboarding-wizard-types.ts';
|
|
3
2
|
|
|
4
3
|
export const STEP_ORDER: readonly OnboardingWizardStepId[] = [
|
|
5
|
-
'
|
|
6
|
-
'network',
|
|
7
|
-
'access',
|
|
8
|
-
'external-services',
|
|
9
|
-
'cloudflare',
|
|
4
|
+
'agent-setup',
|
|
10
5
|
'provider-access',
|
|
11
6
|
'default-model',
|
|
7
|
+
'agent-communication',
|
|
8
|
+
'agent-tools',
|
|
9
|
+
'agent-knowledge',
|
|
10
|
+
'agent-local-state',
|
|
11
|
+
'agent-automation',
|
|
12
|
+
'agent-voice-media',
|
|
13
|
+
'agent-delegation',
|
|
12
14
|
'experience',
|
|
13
15
|
'review',
|
|
14
16
|
];
|
|
15
17
|
|
|
16
|
-
export const DEFAULT_CAPABILITIES: readonly OnboardingStep1CapabilityItem[] = [
|
|
17
|
-
{
|
|
18
|
-
id: 'local-tui-only',
|
|
19
|
-
label: 'Agent Local Only (External Daemon)',
|
|
20
|
-
selected: true,
|
|
21
|
-
detail: 'Use GoodVibes Agent in this terminal and connect only to an externally managed daemon. Agent does not enable service mode, HTTP listeners, external app surfaces, or network setup.',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: 'browser-access',
|
|
25
|
-
label: 'Open GoodVibes in a Browser',
|
|
26
|
-
selected: false,
|
|
27
|
-
detail: 'Review browser access requirements for the externally managed daemon. Agent records intent but does not start web services.',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: 'network-access',
|
|
31
|
-
label: 'Let other devices use GoodVibes',
|
|
32
|
-
selected: false,
|
|
33
|
-
detail: 'Review external daemon surfaces that are reachable from other devices on your LAN. Local authentication is required.',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: 'webhook-events',
|
|
37
|
-
label: 'Receive webhooks or events from other tools',
|
|
38
|
-
selected: false,
|
|
39
|
-
detail: 'Review the external HTTP listener required for incoming webhooks, callbacks, and automation events.',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: 'external-integrations',
|
|
43
|
-
label: 'Connect GoodVibes to external apps and services',
|
|
44
|
-
selected: false,
|
|
45
|
-
detail: 'Enable setup screens for Slack, Discord, Telegram, Home Assistant, Teams, Matrix, and other app surfaces you choose.',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: 'cloudflare-batch',
|
|
49
|
-
label: 'Use Cloudflare for batch or remote daemon work',
|
|
50
|
-
selected: false,
|
|
51
|
-
detail: 'Optionally configure Cloudflare Workers and Queues for explicit or eligible background batch jobs. The external daemon still owns execution.',
|
|
52
|
-
},
|
|
53
|
-
];
|
|
54
|
-
|
|
55
18
|
export const REASONING_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
56
19
|
{ id: 'instant', label: 'Instant', hint: 'Lowest-latency routing.' },
|
|
57
20
|
{ id: 'low', label: 'Low', hint: 'Compact reasoning for quick work.' },
|
|
@@ -59,11 +22,6 @@ export const REASONING_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
|
59
22
|
{ id: 'high', label: 'High', hint: 'Deeper reasoning for complex tasks.' },
|
|
60
23
|
];
|
|
61
24
|
|
|
62
|
-
export const NETWORK_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
63
|
-
{ id: 'local-network-default', label: 'Local Network (Default)', hint: 'Review the default LAN-facing external daemon setup for browser, control-plane, and event features.' },
|
|
64
|
-
{ id: 'custom', label: 'Custom', hint: 'Review IP addresses and ports for each external daemon surface.' },
|
|
65
|
-
];
|
|
66
|
-
|
|
67
25
|
export const HITL_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
68
26
|
{ id: 'quiet', label: 'Quiet', hint: 'Only interrupt for important attention requests.' },
|
|
69
27
|
{ id: 'balanced', label: 'Balanced', hint: 'Show important activity without turning Agent into a log stream.' },
|
|
@@ -87,72 +45,3 @@ export const SECRET_POLICY_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
|
87
45
|
{ id: 'require_secure', label: 'Require secure storage', hint: 'Refuse plaintext secret persistence.' },
|
|
88
46
|
{ id: 'plaintext_allowed', label: 'Allow plaintext storage', hint: 'Permit local plaintext secret storage.' },
|
|
89
47
|
];
|
|
90
|
-
|
|
91
|
-
export const TELEGRAM_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
92
|
-
{ id: 'webhook', label: 'Webhook', hint: 'Receive Telegram updates through a webhook.' },
|
|
93
|
-
{ id: 'polling', label: 'Polling', hint: 'The external daemon polls Telegram for updates.' },
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
export const WHATSAPP_PROVIDER_OPTIONS: readonly OnboardingWizardRadioOption[] = [
|
|
97
|
-
{ id: 'meta-cloud', label: 'Meta Cloud', hint: 'Use the Meta Cloud API.' },
|
|
98
|
-
{ id: 'bridge', label: 'Bridge', hint: 'Use a configured bridge service.' },
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
export const INBOUND_EXTERNAL_SURFACE_IDS = new Set<string>([
|
|
102
|
-
'bluebubbles',
|
|
103
|
-
'discord',
|
|
104
|
-
'googleChat',
|
|
105
|
-
'homeassistant',
|
|
106
|
-
'imessage',
|
|
107
|
-
'mattermost',
|
|
108
|
-
'matrix',
|
|
109
|
-
'msteams',
|
|
110
|
-
'ntfy',
|
|
111
|
-
'signal',
|
|
112
|
-
'slack',
|
|
113
|
-
'telegram',
|
|
114
|
-
'webhook',
|
|
115
|
-
'whatsapp',
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
export const REQUIRED_EXTERNAL_SETUP_FIELD_IDS = new Set<string>([
|
|
119
|
-
'external-services.slack.bot-token',
|
|
120
|
-
'external-services.slack.signing-secret',
|
|
121
|
-
'external-services.discord.bot-token',
|
|
122
|
-
'external-services.discord.application-id',
|
|
123
|
-
'external-services.discord.public-key',
|
|
124
|
-
'external-services.telegram.bot-token',
|
|
125
|
-
'external-services.telegram.webhook-secret',
|
|
126
|
-
'external-services.webhook.default-target',
|
|
127
|
-
'external-services.homeassistant.instance-url',
|
|
128
|
-
'external-services.homeassistant.access-token',
|
|
129
|
-
'external-services.homeassistant.webhook-secret',
|
|
130
|
-
'external-services.google-chat.webhook-url',
|
|
131
|
-
'external-services.google-chat.verification-token',
|
|
132
|
-
'external-services.signal.bridge-url',
|
|
133
|
-
'external-services.signal.account',
|
|
134
|
-
'external-services.signal.token',
|
|
135
|
-
'external-services.whatsapp.access-token',
|
|
136
|
-
'external-services.whatsapp.verify-token',
|
|
137
|
-
'external-services.whatsapp.phone-number-id',
|
|
138
|
-
'external-services.imessage.bridge-url',
|
|
139
|
-
'external-services.imessage.account',
|
|
140
|
-
'external-services.imessage.token',
|
|
141
|
-
'external-services.msteams.app-id',
|
|
142
|
-
'external-services.msteams.app-password',
|
|
143
|
-
'external-services.msteams.tenant-id',
|
|
144
|
-
'external-services.bluebubbles.server-url',
|
|
145
|
-
'external-services.bluebubbles.password',
|
|
146
|
-
'external-services.mattermost.base-url',
|
|
147
|
-
'external-services.mattermost.bot-token',
|
|
148
|
-
'external-services.matrix.homeserver-url',
|
|
149
|
-
'external-services.matrix.access-token',
|
|
150
|
-
'external-services.matrix.user-id',
|
|
151
|
-
]);
|
|
152
|
-
|
|
153
|
-
export const NETWORK_HOST_FIELD_IDS = new Set<string>([
|
|
154
|
-
'network.shared-ip-address',
|
|
155
|
-
'network.service-ip',
|
|
156
|
-
'network.browser-ip',
|
|
157
|
-
'network.webhook-ip',
|
|
158
|
-
]);
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { deriveOnboardingStepState, type OnboardingStep1CapabilityItem, type OnboardingStepDerivationState } from '../../runtime/onboarding/index.ts';
|
|
3
|
-
import { DEFAULT_CAPABILITIES } from './onboarding-wizard-constants.ts';
|
|
4
|
-
import { EXTERNAL_SURFACE_SPECS, type ExternalSurfaceSetupFieldSpec, type ExternalSurfaceSpec } from './onboarding-wizard-external-surfaces.ts';
|
|
1
|
+
import type { OnboardingStepDerivationState } from '../../runtime/onboarding/index.ts';
|
|
5
2
|
import type { OnboardingWizardAcknowledgementFieldDefinition, OnboardingWizardModelSelection, OnboardingWizardRuntimeHydration } from './onboarding-wizard-types.ts';
|
|
6
3
|
export {
|
|
7
4
|
buildGoodVibesSecretKey,
|
|
@@ -15,42 +12,10 @@ export function clamp(value: number, min: number, max: number): number {
|
|
|
15
12
|
return Math.max(min, Math.min(max, value));
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
export function countSelected(items: readonly OnboardingStep1CapabilityItem[]): number {
|
|
19
|
-
return items.filter((item) => item.selected).length;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
export function normalizeText(value: string | null | undefined): string {
|
|
23
16
|
return (value ?? '').trim();
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
export function isValidHostValue(value: string): boolean {
|
|
27
|
-
const normalized = value.trim();
|
|
28
|
-
if (normalized.length === 0) return false;
|
|
29
|
-
if (/\s/.test(normalized)) return false;
|
|
30
|
-
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized)) return false;
|
|
31
|
-
if (normalized.includes('/')) return false;
|
|
32
|
-
if (normalized.includes(':')) {
|
|
33
|
-
const unwrapped = normalized.startsWith('[') && normalized.endsWith(']')
|
|
34
|
-
? normalized.slice(1, -1)
|
|
35
|
-
: normalized;
|
|
36
|
-
if (isIP(unwrapped) === 0) return false;
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function isLoopbackAddress(value: string): boolean {
|
|
42
|
-
const normalized = value.trim().toLowerCase();
|
|
43
|
-
return normalized === 'localhost'
|
|
44
|
-
|| normalized === '::1'
|
|
45
|
-
|| normalized === '[::1]'
|
|
46
|
-
|| normalized === '0:0:0:0:0:0:0:1'
|
|
47
|
-
|| /^127(?:\.\d{1,3}){3}$/.test(normalized);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function uniqueNonEmpty(values: readonly string[]): readonly string[] {
|
|
51
|
-
return [...new Set(values.map((value) => normalizeText(value)).filter((value) => value.length > 0))];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
19
|
export function makeNotNeededAcknowledgement(detail: string): OnboardingWizardAcknowledgementFieldDefinition {
|
|
55
20
|
return {
|
|
56
21
|
kind: 'acknowledgement',
|
|
@@ -65,7 +30,7 @@ export function makeNotNeededAcknowledgement(detail: string): OnboardingWizardAc
|
|
|
65
30
|
|
|
66
31
|
export function buildDefaultDerivedState(): OnboardingStepDerivationState {
|
|
67
32
|
return {
|
|
68
|
-
step1Capabilities:
|
|
33
|
+
step1Capabilities: [],
|
|
69
34
|
step1_5NetworkMode: 'local-network-default',
|
|
70
35
|
reopenEditAcknowledgements: {
|
|
71
36
|
providers: {
|
|
@@ -125,21 +90,6 @@ export function modelSelectionLabel(selection: OnboardingWizardModelSelection |
|
|
|
125
90
|
return `${provider}/${model}`;
|
|
126
91
|
}
|
|
127
92
|
|
|
128
|
-
export function getExternalSurfaceSetupFieldSpec(fieldId: string): ExternalSurfaceSetupFieldSpec | null {
|
|
129
|
-
for (const surface of EXTERNAL_SURFACE_SPECS) {
|
|
130
|
-
const field = surface.fields.find((entry) => entry.id === fieldId);
|
|
131
|
-
if (field) return field;
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function getExternalSurfaceSpecByFieldId(fieldId: string): ExternalSurfaceSpec | null {
|
|
137
|
-
for (const surface of EXTERNAL_SURFACE_SPECS) {
|
|
138
|
-
if (surface.fields.some((entry) => entry.id === fieldId)) return surface;
|
|
139
|
-
}
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
93
|
export function getRuntimeDerivedState(hydration: OnboardingWizardRuntimeHydration): OnboardingStepDerivationState {
|
|
144
94
|
if (hydration.derived) {
|
|
145
95
|
const fallback = buildDefaultDerivedState();
|
|
@@ -154,7 +104,6 @@ export function getRuntimeDerivedState(hydration: OnboardingWizardRuntimeHydrati
|
|
|
154
104
|
};
|
|
155
105
|
}
|
|
156
106
|
|
|
157
|
-
if (hydration.snapshot) return deriveOnboardingStepState(hydration.snapshot);
|
|
158
107
|
return buildDefaultDerivedState();
|
|
159
108
|
}
|
|
160
109
|
|