import { z } from "zod";
// default heartbeat (wake‑up) interval used when no value is supplied
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
// reduced-frequency heartbeat for overnight hours (midnight–6 AM)
export const NIGHT_HEARTBEAT_INTERVAL_MS = 2 * 60 * 60 * 1000; // 2 hours
// previous default; used by config migration to detect stale values
export const LEGACY_HEARTBEAT_INTERVAL_MS = 3 * 60 * 1000; // 3 minutes
export const GoogleTokensSchema = z.object({
    access_token: z.string(),
    refresh_token: z.string(),
    token_type: z.string().default("Bearer"),
    expiry_date: z.number(),
    scope: z.string(),
});
export const ChannelConfigSchema = z.object({
    telegram: z
        .object({
        enabled: z.boolean().default(false),
        botToken: z.string().optional(),
    })
        .optional(),
    discord: z
        .object({
        enabled: z.boolean().default(false),
        botToken: z.string().optional(),
    })
        .optional(),
    weixin: z
        .object({
        enabled: z.boolean().default(false),
        accountId: z.string().optional(),
        baseUrl: z.string().default("https://ilinkai.weixin.qq.com"),
        cdnBaseUrl: z.string().default("https://novac2c.cdn.weixin.qq.com/c2c"),
    })
        .optional(),
});
export const OBS_TUNNEL_DOMAIN = "visionclawobs.com";
/**
 * Cloudflare zone ID for {@link OBS_TUNNEL_DOMAIN}. Used by setup tooling to
 * verify that a given `cert.pem` was issued for our zone (vs. a developer's
 * personal Cloudflare account) before reusing it for the dev profile.
 *
 * Can be overridden at runtime via the `VISIONCLAW_TUNNEL_ZONE_ID` env var
 * (useful if the domain ever moves to a different zone). In production the
 * default is authoritative — this constant is not user-configurable.
 */
export const OBS_TUNNEL_ZONE_ID_DEFAULT = "cbf534f34cd6d335f9df92ec724b0cc4";
export function getObsTunnelZoneId() {
    const override = process.env.VISIONCLAW_TUNNEL_ZONE_ID?.trim();
    return override && override.length > 0 ? override : OBS_TUNNEL_ZONE_ID_DEFAULT;
}
export const ObsConfigSchema = z
    .object({
    enabled: z.boolean().default(true),
    host: z.string().default("0.0.0.0"),
    port: z.number().int().default(3101),
    bufferSize: z.number().int().default(1000),
    tunnel: z.enum(["cloudflared", "ngrok", "off"]).default("cloudflared"),
    tunnelName: z.string().optional(),
    tunnelHostname: z.string().optional(),
    // Optional dev-profile-only fields. When set, `cloudflared` invocations will
    // use this cert (via TUNNEL_ORIGIN_CERT) and this config file (via --config)
    // instead of the default `~/.cloudflared/*`, isolating the dev environment
    // from the user's personal Cloudflare account and the production default
    // profile. Production `config.json` must NOT set these so that existing
    // installs keep using `~/.cloudflared/*` unchanged.
    tunnelOriginCert: z.string().optional(),
    tunnelConfigPath: z.string().optional(),
})
    .superRefine((data, ctx) => {
    const hasCert = !!data.tunnelOriginCert;
    const hasCfg = !!data.tunnelConfigPath;
    // Either both together (isolated path, dev profile) or neither (default
    // ~/.cloudflared/*, prod default profile). Deliberately does NOT require
    // tunnelName because prod config.json has tunnelName+tunnelHostname but no
    // tunnelOriginCert/tunnelConfigPath — requiring a triple would break every
    // existing prod install.
    if (hasCert !== hasCfg) {
        ctx.addIssue({
            code: "custom",
            message: "obs.tunnelOriginCert and obs.tunnelConfigPath must be set together (or both omitted)",
        });
    }
});
import { CLAUDE_SONNET_MODEL, CLAUDE_OPUS_4_6_MODEL, CLAUDE_OPUS_MODEL, GPT_MODEL, } from "./models.js";
export const ModelSchema = z.enum([
    CLAUDE_OPUS_MODEL,
    CLAUDE_OPUS_4_6_MODEL,
    CLAUDE_SONNET_MODEL,
    GPT_MODEL,
]).default(CLAUDE_SONNET_MODEL);
export const ProviderSchema = z.enum(["anthropic", "bedrock", "vertex", "9router", "openai", "azure-openai"]).default("anthropic");
export const MODEL_PROVIDER_COMPATIBILITY = {
    [CLAUDE_SONNET_MODEL]: ["anthropic", "bedrock", "vertex", "9router"],
    [CLAUDE_OPUS_MODEL]: ["anthropic", "bedrock", "vertex", "9router"],
    [CLAUDE_OPUS_4_6_MODEL]: ["anthropic", "bedrock", "vertex", "9router"],
    [GPT_MODEL]: ["openai", "azure-openai", "9router"],
};
export function inferModelFromProvider(provider) {
    return provider === "openai" || provider === "azure-openai"
        ? GPT_MODEL
        : CLAUDE_SONNET_MODEL;
}
export function getSupportedProvidersForModel(model) {
    return MODEL_PROVIDER_COMPATIBILITY[model];
}
export function isProviderSupportedForModel(model, provider) {
    return getSupportedProvidersForModel(model).includes(provider);
}
export function assertSupportedModelProvider(model, provider) {
    if (!isProviderSupportedForModel(model, provider)) {
        throw new Error(`Provider "${provider}" does not support model "${model}"`);
    }
}
/**
 * Pre-ship manufacturer config.
 * Contains runtime dependencies and infrastructure settings.
 */
export const BrowserBackendSchema = z.enum(["playwright", "playwriter"]).default("playwright");
const VisionClawConfigBaseSchema = z.object({
    agentName: z.string().default("VisionClaw"),
    // Optional human-friendly label, separate from the canonical `agentName`
    // slug used as the server-side identity. Populated automatically when a
    // heartbeat response reports an `agentName` that differs from our local
    // value: the old local agentName is moved here (only if displayName is
    // currently unset) and `agentName` is overwritten with the server's
    // canonical form. Manual configuration is also supported.
    displayName: z.string().optional(),
    gmail: z.email(),
    gmailPassword: z.string().optional(),
    phoneNumber: z.string().optional(),
    systemPassword: z.string().optional(),
    googleClientId: z.string().optional(),
    googleClientSecret: z.string().optional(),
    model: ModelSchema,
    provider: ProviderSchema,
    anthropicApiKey: z.string().optional(),
    anthropicBaseUrl: z.string().optional(),
    openaiApiKey: z.string().optional(),
    azureOpenAIEndpoint: z.string().optional(),
    azureOpenAIApiKey: z.string().optional(),
    azureOpenAIApiVersion: z.string().optional(),
    azureOpenAIDeployment: z.string().optional(),
    ninerouterBaseUrl: z.string().default("https://9r.gru.ai"),
    ninerouterApiKey: z.string().optional(),
    awsRegion: z.string().optional(),
    awsBearerToken: z.string().optional(),
    awsAccessKeyId: z.string().optional(),
    awsSecretAccessKey: z.string().optional(),
    awsSessionToken: z.string().optional(),
    vertexProjectId: z.string().optional(),
    vertexRegion: z.string().default("global"),
    googleApplicationCredentials: z.string().optional(),
    channels: ChannelConfigSchema.default({}),
    obs: ObsConfigSchema.default({
        enabled: true,
        host: "0.0.0.0",
        port: 3101,
        bufferSize: 1000,
        tunnel: "cloudflared",
    }),
    launchAtLoginEnabled: z.boolean().default(true),
    // Controls whether the agent applies its branded desktop wallpaper on
    // startup (macOS only). Defaults to true to preserve pre-existing
    // behaviour for the production `default` profile; `make setup` sets this
    // to false for the `dev` profile so local agent runs don't overwrite the
    // developer's personal wallpaper.
    wallpaperEnabled: z.boolean().default(true),
    onboardingServiceUrl: z.string().optional(),
    visionclawServiceApiKey: z.string().optional(),
    skillsDir: z.string().optional(),
    serpApiKey: z.string().optional(),
    phonecallApiKey: z.string().optional(),
    financialToolApiKey: z.string().optional(),
    smsApiKey: z.string().optional(),
    smsPhoneNumber: z.string().optional(),
    geminiApiKey: z.string().optional(),
    tosAccessKeyId: z.string().optional(),
    tosAccessKeySecret: z.string().optional(),
    backupProvider: z.enum(["gdrive", "tos"]).default("gdrive"),
    backupTosBucket: z.string().default("visionclaw-backup"),
    backupTosRegion: z.string().default("cn-beijing"),
    backupTosEndpoint: z.string().default("tos-accelerate.volces.com"),
    backupTosPublicEndpoint: z.string().default("tos-accelerate.volces.com"),
    backupTosSignedUrlExpiresSeconds: z.number().int().positive().default(7 * 24 * 60 * 60),
    backupDesktopEnabled: z.boolean().default(true),
    backupDesktopMaxBytes: z.number().int().positive().default(1024 * 1024 * 1024),
    browserBackend: BrowserBackendSchema,
    e2e: z
        .object({
        google: z
            .object({
            meetGuestEmail: z.email().optional(),
            meetGuestBrowserProfileDir: z.string().optional(),
        })
            .optional(),
    })
        .optional(),
    playwriterRelayPort: z.number().int().optional(),
    playwriterRelayHost: z.string().optional(),
    imageArchive: z
        .object({
        provider: z.enum(["tos", "s3"]),
        bucket: z.string(),
        tosAccessKeyId: z.string().optional(),
        tosAccessKeySecret: z.string().optional(),
        tosRegion: z.string().default("cn-beijing"),
        tosEndpoint: z.string().default("tos-accelerate.volces.com"),
        s3AccessKeyId: z.string().optional(),
        s3SecretAccessKey: z.string().optional(),
        s3Region: z.string().default("us-east-1"),
    })
        .optional(),
    machineId: z.string().optional(),
    machineSerial: z.string().optional(),
});
const VisionClawConfigPreprocessedSchema = z.preprocess((raw) => {
    if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
        const obj = raw;
        if ("macUserPassword" in obj && !("systemPassword" in obj)) {
            obj.systemPassword = obj.macUserPassword;
        }
        delete obj.macUserPassword;
    }
    return raw;
}, VisionClawConfigBaseSchema);
export const VisionClawConfigSchema = VisionClawConfigPreprocessedSchema.superRefine((config, ctx) => {
    if (!isProviderSupportedForModel(config.model, config.provider)) {
        ctx.addIssue({
            code: "custom",
            path: ["provider"],
            message: `Provider "${config.provider}" does not support model "${config.model}"`,
        });
    }
});
/**
 * Post-ship owner config.
 * Populated during Telegram onboarding after the agent is delivered.
 */
export const OwnerConfigSchema = z.object({
    ownerName: z.string(),
    ownerEmail: z.string().min(1).refine((v) => v.includes("@"), { message: "Invalid email" }),
    phoneNumber: z.string().optional(),
    telegramChatId: z.number().optional(),
    allowedDiscordChannelIds: z.array(z.string()).default([]),
    heartbeatIntervalMs: z.number().default(DEFAULT_HEARTBEAT_INTERVAL_MS),
    dualSessionEnabled: z.boolean().default(false),
    androidUseEnabled: z.boolean().default(false),
    chatMonitorEnabled: z.boolean().default(true),
    onboardingComplete: z.boolean().default(false),
    onboardingCode: z.string().optional(),
    onboardingCodeVerified: z.boolean().default(false),
    onboardedAt: z.string().optional(),
    silentCompaction: z.boolean().default(true),
    language: z.string().default("en"),
    timezone: z.string().optional(),
});
export const TelegramAccessStatusSchema = z.enum(["pending", "approved", "blocked"]);
export const TelegramChatKindSchema = z.enum(["dm", "group", "supergroup"]);
export const TelegramChatAccessEntrySchema = z.object({
    id: z.string(),
    chatId: z.number(),
    userId: z.number().optional(),
    kind: TelegramChatKindSchema,
    title: z.string(),
    username: z.string().optional(),
    status: TelegramAccessStatusSchema,
    firstSeenAt: z.string(),
    lastSeenAt: z.string(),
    pendingMessageCount: z.number().int().nonnegative().default(0),
    lastMessagePreview: z.string().optional(),
    lastOwnerNotifiedAt: z.string().optional(),
    autoReplySentAt: z.string().optional(),
});
const StoredTelegramUserSchema = z.object({
    id: z.number(),
    first_name: z.string().optional(),
    last_name: z.string().optional(),
    username: z.string().optional(),
});
const StoredTelegramChatSchema = z.object({
    id: z.number(),
    type: z.enum(["private", "group", "supergroup"]),
    title: z.string().optional(),
    first_name: z.string().optional(),
    last_name: z.string().optional(),
    username: z.string().optional(),
});
const StoredTelegramPhotoSchema = z.object({
    file_id: z.string(),
});
const StoredTelegramDocumentSchema = z.object({
    file_id: z.string(),
    file_name: z.string().optional(),
    mime_type: z.string().optional(),
});
const StoredTelegramVoiceSchema = z.object({
    file_id: z.string(),
    mime_type: z.string().optional(),
});
const StoredTelegramVideoSchema = z.object({
    file_id: z.string(),
    mime_type: z.string().optional(),
});
const StoredTelegramStickerSchema = z.object({
    file_id: z.string(),
});
export const StoredTelegramMessageSchema = z.object({
    entryId: z.string(),
    message_id: z.number().int(),
    date: z.number().int(),
    text: z.string().optional(),
    caption: z.string().optional(),
    chat: StoredTelegramChatSchema,
    from: StoredTelegramUserSchema.optional(),
    photo: z.array(StoredTelegramPhotoSchema).optional(),
    document: StoredTelegramDocumentSchema.optional(),
    voice: StoredTelegramVoiceSchema.optional(),
    video: StoredTelegramVideoSchema.optional(),
    sticker: StoredTelegramStickerSchema.optional(),
});
export const TelegramAccessConfigSchema = z.object({
    unknownDmPolicy: z.enum(["require_approval"]).default("require_approval"),
    unknownGroupPolicy: z.enum(["require_approval"]).default("require_approval"),
    entries: z.array(TelegramChatAccessEntrySchema).default([]),
    heldMessages: z.array(StoredTelegramMessageSchema).default([]),
});
//# sourceMappingURL=types.js.map