Merge 6d0cb1c935 into 4583f88626
This commit is contained in:
commit
f6aa6869f4
@ -41,6 +41,10 @@ export function registerOnboardCommand(program: Command) {
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/onboard", "docs.molt.bot/cli/onboard")}\n`,
|
||||
)
|
||||
.option("--workspace <dir>", "Agent workspace directory (default: ~/clawd)")
|
||||
.option(
|
||||
"--user-timezone <tz>",
|
||||
"User timezone for cron schedules (e.g., America/New_York, Africa/Lagos)",
|
||||
)
|
||||
.option("--reset", "Reset config + credentials + sessions + workspace before running wizard")
|
||||
.option("--non-interactive", "Run without prompts", false)
|
||||
.option(
|
||||
@ -106,6 +110,7 @@ export function registerOnboardCommand(program: Command) {
|
||||
await onboardCommand(
|
||||
{
|
||||
workspace: opts.workspace as string | undefined,
|
||||
userTimezone: opts.userTimezone as string | undefined,
|
||||
nonInteractive: Boolean(opts.nonInteractive),
|
||||
acceptRisk: Boolean(opts.acceptRisk),
|
||||
flow: opts.flow as "quickstart" | "advanced" | "manual" | undefined,
|
||||
|
||||
@ -20,6 +20,10 @@ export function registerSetupCommand(program: Command) {
|
||||
"--workspace <dir>",
|
||||
"Agent workspace directory (default: ~/clawd; stored as agents.defaults.workspace)",
|
||||
)
|
||||
.option(
|
||||
"--user-timezone <tz>",
|
||||
"User timezone for cron schedules (e.g., America/New_York, Africa/Lagos)",
|
||||
)
|
||||
.option("--wizard", "Run the interactive onboarding wizard", false)
|
||||
.option("--non-interactive", "Run the wizard without prompts", false)
|
||||
.option("--mode <mode>", "Wizard mode: local|remote")
|
||||
@ -38,6 +42,7 @@ export function registerSetupCommand(program: Command) {
|
||||
await onboardCommand(
|
||||
{
|
||||
workspace: opts.workspace as string | undefined,
|
||||
userTimezone: opts.userTimezone as string | undefined,
|
||||
nonInteractive: Boolean(opts.nonInteractive),
|
||||
mode: opts.mode as "local" | "remote" | undefined,
|
||||
remoteUrl: opts.remoteUrl as string | undefined,
|
||||
|
||||
@ -425,3 +425,44 @@ function isValidIPv4(host: string): boolean {
|
||||
return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an IANA timezone string.
|
||||
* Uses Intl.supportedValuesOf('timeZone') when available (Node 18.6+),
|
||||
* falls back to testing via DateTimeFormat.
|
||||
*/
|
||||
export function isValidTimezone(tz: string): boolean {
|
||||
if (!tz || typeof tz !== "string") return false;
|
||||
const trimmed = tz.trim();
|
||||
if (!trimmed) return false;
|
||||
|
||||
// Try Intl.supportedValuesOf if available (Node 18.6+)
|
||||
if (typeof Intl.supportedValuesOf === "function") {
|
||||
try {
|
||||
const supported = Intl.supportedValuesOf("timeZone");
|
||||
return supported.includes(trimmed);
|
||||
} catch {
|
||||
// Fall through to DateTimeFormat check
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to create a DateTimeFormat with the timezone
|
||||
try {
|
||||
new Intl.DateTimeFormat("en-US", { timeZone: trimmed });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the system's local timezone.
|
||||
* Returns the IANA timezone string (e.g., "America/New_York").
|
||||
*/
|
||||
export function detectSystemTimezone(): string {
|
||||
try {
|
||||
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
} catch {
|
||||
return "UTC";
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,9 @@ import { healthCommand } from "../health.js";
|
||||
import {
|
||||
applyWizardMetadata,
|
||||
DEFAULT_WORKSPACE,
|
||||
detectSystemTimezone,
|
||||
ensureWorkspaceAndSessions,
|
||||
isValidTimezone,
|
||||
resolveControlUiLinks,
|
||||
waitForGatewayReachable,
|
||||
} from "../onboard-helpers.js";
|
||||
@ -35,6 +37,23 @@ export async function runNonInteractiveOnboardingLocal(params: {
|
||||
defaultWorkspaceDir: DEFAULT_WORKSPACE,
|
||||
});
|
||||
|
||||
// Resolve user timezone
|
||||
const systemTimezone = detectSystemTimezone();
|
||||
const existingTimezone = baseConfig.agents?.defaults?.userTimezone;
|
||||
let userTimezone: string | undefined;
|
||||
if (opts.userTimezone !== undefined) {
|
||||
const trimmed = opts.userTimezone.trim();
|
||||
if (trimmed && isValidTimezone(trimmed)) {
|
||||
userTimezone = trimmed;
|
||||
} else if (trimmed) {
|
||||
runtime.log(`Invalid timezone "${trimmed}". Using system default: ${systemTimezone}`);
|
||||
userTimezone = systemTimezone;
|
||||
}
|
||||
} else {
|
||||
// Use existing or system timezone
|
||||
userTimezone = existingTimezone ?? systemTimezone;
|
||||
}
|
||||
|
||||
let nextConfig: MoltbotConfig = {
|
||||
...baseConfig,
|
||||
agents: {
|
||||
@ -42,6 +61,7 @@ export async function runNonInteractiveOnboardingLocal(params: {
|
||||
defaults: {
|
||||
...baseConfig.agents?.defaults,
|
||||
workspace: workspaceDir,
|
||||
...(userTimezone ? { userTimezone } : {}),
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
|
||||
@ -47,6 +47,8 @@ export type OnboardOptions = {
|
||||
/** "manual" is an alias for "advanced". */
|
||||
flow?: "quickstart" | "advanced" | "manual";
|
||||
workspace?: string;
|
||||
/** User timezone for cron schedules (e.g., "America/New_York", "Europe/London"). */
|
||||
userTimezone?: string;
|
||||
nonInteractive?: boolean;
|
||||
/** Required for non-interactive onboarding; skips the interactive risk prompt when true. */
|
||||
acceptRisk?: boolean;
|
||||
|
||||
@ -11,8 +11,10 @@ import { setupChannels } from "../commands/onboard-channels.js";
|
||||
import {
|
||||
applyWizardMetadata,
|
||||
DEFAULT_WORKSPACE,
|
||||
detectSystemTimezone,
|
||||
ensureWorkspaceAndSessions,
|
||||
handleReset,
|
||||
isValidTimezone,
|
||||
printWizardHeader,
|
||||
probeGatewayReachable,
|
||||
summarizeExistingConfig,
|
||||
@ -335,6 +337,42 @@ export async function runOnboardingWizard(
|
||||
|
||||
const workspaceDir = resolveUserPath(workspaceInput.trim() || DEFAULT_WORKSPACE);
|
||||
|
||||
// Timezone prompt
|
||||
const systemTimezone = detectSystemTimezone();
|
||||
const existingTimezone = baseConfig.agents?.defaults?.userTimezone;
|
||||
const timezoneDefault = existingTimezone ?? systemTimezone;
|
||||
|
||||
let userTimezone: string | undefined;
|
||||
if (opts.userTimezone !== undefined) {
|
||||
// CLI option provided
|
||||
userTimezone = opts.userTimezone.trim() || undefined;
|
||||
if (userTimezone && !isValidTimezone(userTimezone)) {
|
||||
await prompter.note(
|
||||
`Invalid timezone "${userTimezone}". Using system default: ${systemTimezone}`,
|
||||
"Timezone",
|
||||
);
|
||||
userTimezone = systemTimezone;
|
||||
}
|
||||
} else if (flow === "quickstart") {
|
||||
// QuickStart: use existing or system timezone silently
|
||||
userTimezone = existingTimezone ?? systemTimezone;
|
||||
} else {
|
||||
// Manual flow: prompt for timezone
|
||||
const timezoneInput = await prompter.text({
|
||||
message: "Timezone (for cron schedules)",
|
||||
initialValue: timezoneDefault,
|
||||
placeholder: "e.g., America/New_York, Europe/London, Africa/Lagos",
|
||||
validate: (value) => {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return; // Allow empty (will use system default)
|
||||
if (!isValidTimezone(trimmed)) {
|
||||
return `Invalid timezone. Examples: America/New_York, Europe/London, Africa/Lagos`;
|
||||
}
|
||||
},
|
||||
});
|
||||
userTimezone = timezoneInput.trim() || systemTimezone;
|
||||
}
|
||||
|
||||
let nextConfig: MoltbotConfig = {
|
||||
...baseConfig,
|
||||
agents: {
|
||||
@ -342,6 +380,7 @@ export async function runOnboardingWizard(
|
||||
defaults: {
|
||||
...baseConfig.agents?.defaults,
|
||||
workspace: workspaceDir,
|
||||
...(userTimezone ? { userTimezone } : {}),
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user