/** * Pure/sync helpers for editing the `.hublaunch/hublaunch.config.js` source * file as text. Kept side-effect free (no filesystem access) so the string * transformations are unit-testable in isolation — same rationale as * `appendGitignoreEntry` in `src/commands/init.ts`. Callers own reading and * writing the file. */ /** * Insert or replace the `provider` block in a config file's source text. * * Mirrors the comment-out-then-insert approach used by `saveApiKeyToConfig` in * `src/commands/login.ts` for `hulaApiKey`: * - Any existing `provider:` line — whether active or commented out (including * the placeholder init writes) — is commented out. * - The new active line is inserted immediately after the config opening * (`export const config = {` or `export default {`). * * The block is written on a SINGLE line (`provider: { type: '…', apiKey: '…' },`) * so the comment-out regex is robust — a multi-line object would leave dangling * inner lines. `init` emits the same single-line shape. * * If no config opening is found the content is returned unchanged (the caller * degrades gracefully rather than corrupting the file). Never echoes the key * anywhere but the returned string. */ export declare function upsertProviderApiKey(content: string, providerType: string, key: string): string; /** * Commented-out example lines for documented settings. Each is the EXACT text * the fresh-config template emits for the unset case (minus the leading * newline), so the template path (which prepends `\n`) and the re-init * "Available settings" examples block stay byte-identical. `key` is the config * key used for presence detection; `line` is the full rendered line including * indentation and trailing inline comment. * * `hooks` is the one exception with no template equivalent: re-init renders it * as a single commented example only — never the active `hooks: { … }` block * `buildHooksConfigSection` emits — so re-init can never silently switch a hook * on. */ export declare const PROVIDER_EXAMPLE_LINE = " // provider: { type: \"claude\", apiKey: \"\" }, // LLM provider: claude | openai | openrouter. claude=sk-ant-oat01-\u2026 (claude.ai/settings) or sk-ant-api03-\u2026 (platform.claude.com); openai=platform.openai.com/api-keys; openrouter=sk-or-\u2026 (openrouter.ai/settings/keys)"; export declare const HULA_API_KEY_EXAMPLE_LINE = " // hulaApiKey: \"your-api-key-here\","; export declare const UPDATE_NOTIFICATION_URL_EXAMPLE_LINE = " // updateNotificationUrl: \"https://hooks.slack.com/services/T0/B0/secret\", // POSTed on every terminal task transition (Slack hosts get { text } body, others get generic JSON). See hula-server API.md."; export declare const UPDATE_NOTIFICATION_NAME_TAG_EXAMPLE_LINE = " // updateNotificationNameTag: \"<@U12345>\", // Verbatim \"initiated by\" label in update notifications. Slack mention markup (<@U12345> member, group) actually pings; bare text does not. Pairs with updateNotificationUrl. See hula-server API.md."; export declare const ENV_VARS_EXAMPLE_LINE = " // envVars: [\"TEST_USER_EMAIL\", \"API_KEY\"], // Environment variables to forward to the container for both hula launch and hula schedule (read from .env). Or use envVars: \"all\" to forward every non-reserved variable."; export declare const STEPS_EXAMPLE_LINE = " // steps: { implementation: { model: \"opus\" }, regression: { skip: true } }, // Per-pipeline-step model/iteration overrides. Keys/fields/ranges: https://github.com/NoStackApp/hub-launch#configuring-per-step-model--iteration-overrides"; export declare const MERGE_POLICY_EXAMPLE_LINE = " // mergePolicy: { minConfidence: 90, autoMerge: false }, // Merge gate for hula approve / server auto-merge. Docs: https://github.com/NoStackApp/hub-launch/blob/main/docs/advanced.md#merge-policy"; export declare const HOOKS_EXAMPLE_LINE = " // hooks: { beforeLaunch: \".hublaunch/hooks/beforeLaunch.ts\" }, // Lifecycle hooks (beforeLaunch, afterMerge, afterRevert). Add script paths under .hublaunch/hooks/; omit to run none."; /** * Documented settings appended once as commented-out examples when the existing * config mentions them nowhere (neither active nor already commented). Order is * the order they appear in the appended block. */ export declare const COMMENTED_EXAMPLE_LINES: ReadonlyArray<{ key: string; line: string; }>; /** * True when `key` appears anywhere in the config source as an active (`key:`) * or commented (`// key:`) line. Mirrors the active/commented regex pair in * `updateSaveCredentialsInConfig`; this is what makes the commented-examples * append idempotent (a key written as an example on run 1 is detected on run * 2 and not re-added). */ export declare function hasKeyMention(content: string, key: string): boolean; /** * Replace the value on an existing `version:` line (active or commented, * preserving any trailing inline comment), else insert a documented * `version: ""` line immediately after the config opening. Returns the * content unchanged when there is no config opening (degrade, don't corrupt). * A version value that already equals `version` round-trips to identical text, * so the transform is a true no-op in that case. */ export declare function upsertVersionLine(content: string, version: string): string; /** * Insert a single rendered line immediately before the final closing `};` — * but ONLY when `key` is not already mentioned (active OR commented). No-op * otherwise, and unchanged when there is no closing brace. `line` is the full * line text including indentation and any trailing comment; the newline is * added by this helper. Used for `sandboxProfile` and `saveCredentials`. */ export declare function insertKeyLineIfAbsent(content: string, key: string, line: string): string; /** * Delete every ACTIVE line for `key` (whole line incl. its trailing newline). * Only active declarations match — a `// key:` comment or an `example` line is * left alone, which also keeps the transform idempotent (a second run finds * nothing to remove). The anchoring mirrors `upsertProviderApiKey`'s line * regex. Deleting rather than commenting out avoids leaving a (possibly * secret) obsolete value behind as a comment. Returns `{ content, removed }` * so the caller can build the "Removed obsolete settings" notice. Used for the * dead Daytona keys, which are only ever written as single-line values. */ export declare function removeKeyLine(content: string, key: string): { content: string; removed: boolean; }; /** * Append, once, an `// Available settings (examples)` block of commented-out * example lines for every `examples` entry the config does not mention at all. * Inserted before the final closing `};`. Idempotent via {@link hasKeyMention} * (a second application adds nothing). Returns the content unchanged when no * example is missing or there is no closing brace. */ export declare function appendCommentedExamples(content: string, examples: ReadonlyArray<{ key: string; line: string; }>): string; export interface ReinitSurgicalEdits { /** Current CLI version (`getCliVersion()`); `null` leaves `version` untouched. */ cliVersion: string | null; /** Resolved provider from prompts, or undefined when none is active. */ provider: { type: string; apiKey?: string; } | undefined; /** Provider already recorded in the existing config, for the change check. */ existingProvider: { type: string; apiKey?: string; } | undefined; /** Full `sandboxProfile:` line to insert when the key is absent, else undefined. */ sandboxProfileLine?: string; /** Full `saveCredentials:` line to insert when the key is absent, else undefined. */ saveCredentialsLine?: string; /** Config keys to scrub (commented out) if present, e.g. the dead Daytona keys. */ obsoleteKeys: ReadonlyArray; /** Documented settings to append once as commented examples when unmentioned. */ exampleLines: ReadonlyArray<{ key: string; line: string; }>; } /** * Apply the full surgical re-init transform to a config file's source text, * preserving every pre-existing line except the small enumerated set of edits. * Pure (no filesystem) so the whole chain is unit-testable in isolation. The * order matches the plan: scrub obsolete keys → provider (only when changed) → * insert sandboxProfile / saveCredentials when absent → version → commented * examples. Returns the new text plus the key names that changed (for the * summary) and which obsolete keys were removed (for the notice). Never echoes * any secret value — only key names are surfaced to callers. */ export declare function applyReinitSurgicalEdits(content: string, edits: ReinitSurgicalEdits): { content: string; changedKeys: string[]; removedObsoleteKeys: string[]; }; //# sourceMappingURL=config-file-edit.d.ts.map