import { a as __commonJSMin, b as __require } from './chunk-DDP5xfzh.js';
import 'node:module';
import 'node:fs';
import 'node:path';
import { execFile } from 'node:child_process';
import 'node:crypto';
import { totalmem, freemem } from 'node:os';
import { a3 as SYSTEM_ROLE_MAP, b as getLogger, s as object, u as string, z as array, v as number, _ as _enum, w as boolean, W as WHOAMI_ROLE_IDS } from './paths-2M_UxD_H.js';
import './src2-DK8DKufK.js';
import './branch-lock-qP1EkZ7p.js';
import { s as setConfigValue } from './config-B0nEjgSh.js';
import './index-IVoIz4D5.js';
import { readFile } from 'node:fs/promises';
import './index-Ca4C2M45.js';
import { g as getProviderProfile, c as addCredential } from './credential-pool-B04qO8at.js';
import { c as catalogKeyForProvider, v as validateModelForProvider, r as resolveProviderDefaultModel } from './catalog-model-resolver2-Cexk2TN0.js';
import { r as resolveLLMForRole, I as IMPLICIT_FALLBACK_PROVIDER, b as probeOllamaAlive } from './role-resolver-DF9BQ_U6.js';
import './nexus-schema2-BEd-ol6e.js';
import './dual-scope-db-ATrwq0LH.js';
import './identity2-DeAB5RXE.js';
import 'node:url';
import { promisify } from 'node:util';

//#region ../core/dist/llm/complexity-classifier.js
/**
* L1 complexity classifier — a prompt-complexity **tier proposer** for the E9
* LLM chokepoint.
*
* ## What this is
*
* A faithful TypeScript port of the deleted Rust `cant-router` Layer-1
* classifier (`crates/cant-router/src/features.rs` + `classifier.rs`, retired
* in T11807 / D11137). It converts a raw prompt string into five auditable
* heuristic features, scores them with a fixed linear model, and maps the
* scalar score to a three-level complexity tier (`low` / `mid` / `high`).
*
* The classifier is a **proposer** that COMPLEMENTS — never replaces — the E9
* resolver {@link resolveLLMForSystem}. The flow is:
*
* ```text
*   classifyComplexity(prompt)  ->  ComplexityTier ('low'|'mid'|'high')
*   complexityTierToRole(tier)  ->  RoleName       (cheap → capable ladder)
*   resolveLLMForSystem({ kind:'role', id })  ->  model + credential
* ```
*
* So when a caller has NO explicit tier/role, it can derive one from the
* prompt's complexity and feed it to the chokepoint.
*
* ## What this is NOT (Gate-13 LLM Chokepoint Guard — T11783)
*
* - It NEVER constructs a transport or SDK client.
* - It NEVER reads `*_API_KEY` (no credential I/O at all).
* - It NEVER hardcodes a model-id literal. The tier→model mapping is the E9
*   resolver's job; this module stops at proposing a {@link RoleName}.
*
* It returns a TIER (and, via the helper, a role). The provider, model, and
* credential are resolved exclusively by {@link resolveLLMForSystem}.
*
* The classifier is a pure, deterministic function with no I/O and no module
* side effects — safe to import anywhere in the chokepoint.
*
* @module llm/complexity-classifier
* @task T11906
* @epic T11745
* @see {@link resolveLLMForSystem} — the E9 chokepoint this proposer feeds.
*/
/** Weight applied to the normalized `tokenCount` feature. */
var WEIGHT_TOKEN_COUNT = .15;
/** Weight applied to the normalized `syntacticComplexity` feature. */
var WEIGHT_SYNTACTIC_COMPLEXITY = .25;
/** Weight applied to the normalized `reasoningDepth` feature. */
var WEIGHT_REASONING_DEPTH = .3;
/** Weight applied to the normalized `domainSpecificity` feature. */
var WEIGHT_DOMAIN_SPECIFICITY = .2;
/** Weight applied to the normalized `touchesFilesCount` feature. */
var WEIGHT_TOUCHES_FILES_COUNT = .1;
/**
* Estimate syntactic complexity by counting nested bracket depth.
*
* Walks the string once tracking the running depth of `(`, `{`, `[` (and their
* matching closers). The maximum depth observed is divided by 5 and clamped to
* `[0.0, 1.0]`, so a prompt with no brackets scores 0 and one with 5+ nested
* brackets scores 1.
*
* @param s - The raw prompt string.
* @returns A syntactic-complexity score in `[0.0, 1.0]`.
*/
function estimateSyntacticComplexity(s) {
	let depth = 0;
	let maxDepth = 0;
	for (const c of s) if (c === "(" || c === "{" || c === "[") {
		depth += 1;
		if (depth > maxDepth) maxDepth = depth;
	} else if (c === ")" || c === "}" || c === "]") depth = Math.max(depth - 1, 0);
	return Math.min(maxDepth / 5, 1);
}
/**
* Reasoning-signal keywords — terms that correlate with multi-step reasoning.
*
* Intentionally short and biased; ported verbatim from the Rust `KEYWORDS`
* slice in `count_reasoning_keywords`.
*/
var REASONING_KEYWORDS = [
	"why",
	"should",
	"compare",
	"decide",
	"explain",
	"analyze",
	"evaluate",
	"consider",
	"trade-off",
	"tradeoff"
];
/**
* Count reasoning-signal keyword occurrences in the prompt (case-insensitive,
* counting every (possibly overlapping-free) occurrence, matching the Rust
* `str::matches` semantics).
*
* @param s - The raw prompt string.
* @returns The total number of reasoning-keyword occurrences.
*/
function countReasoningKeywords(s) {
	const lower = s.toLowerCase();
	let total = 0;
	for (const keyword of REASONING_KEYWORDS) {
		let from = 0;
		for (;;) {
			const idx = lower.indexOf(keyword, from);
			if (idx === -1) break;
			total += 1;
			from = idx + keyword.length;
		}
	}
	return total;
}
/**
* Count the uppercase characters in a token (used by the CamelCase proxy).
*
* @param w - A single whitespace-delimited token.
* @returns The number of uppercase characters in `w`.
*/
function uppercaseCount(w) {
	let n = 0;
	for (const c of w) if (c !== c.toLowerCase() && c === c.toUpperCase()) n += 1;
	return n;
}
/**
* Estimate domain specificity via CamelCase identifier density.
*
* Counts whitespace-delimited tokens containing at least two uppercase letters
* (a rough proxy for domain-specific identifiers like `ModelSelection` or
* `XDGBaseDir`). The count is divided by 10 and clamped to `[0.0, 1.0]`.
*
* @param s - The raw prompt string.
* @returns A domain-specificity score in `[0.0, 1.0]`.
*/
function estimateDomainSpecificity(s) {
	const camelCaseCount = splitWhitespace(s).filter((w) => uppercaseCount(w) >= 2).length;
	return Math.min(camelCaseCount / 10, 1);
}
/**
* Count file references in the prompt.
*
* A token is a file reference if it contains a `/` or ends in a known
* source-file extension (`.rs`, `.ts`, `.md`, `.json`). Punctuation on the
* token is left intact for v1 — callers aware of this limitation can pre-clean
* their prompts.
*
* @param s - The raw prompt string.
* @returns The number of file-reference tokens.
*/
function countFileReferences(s) {
	return splitWhitespace(s).filter((w) => w.includes("/") || w.endsWith(".rs") || w.endsWith(".ts") || w.endsWith(".md") || w.endsWith(".json")).length;
}
/**
* Split a string on Unicode whitespace, dropping empty segments — matching
* Rust's `str::split_whitespace` (which never yields empty tokens).
*
* @param s - The string to split.
* @returns The non-empty whitespace-delimited tokens of `s`.
*/
function splitWhitespace(s) {
	return s.split(/\s+/).filter((w) => w.length > 0);
}
/**
* Extract a {@link PromptFeatures} vector from a raw prompt string.
*
* Pure heuristics, no ML runtime — the Layer-1 input stage. Each field is a
* small, auditable signal that feeds {@link classify}.
*
* @param prompt - The raw prompt string.
* @returns The five-feature vector for `prompt`.
*
* @example
* ```ts
* const f = extractFeatures('Refactor auth.ts to use JWT tokens.');
* f.tokenCount; // > 0
* ```
*/
function extractFeatures(prompt) {
	return {
		tokenCount: splitWhitespace(prompt).length,
		syntacticComplexity: estimateSyntacticComplexity(prompt),
		reasoningDepth: countReasoningKeywords(prompt),
		domainSpecificity: estimateDomainSpecificity(prompt),
		touchesFilesCount: countFileReferences(prompt)
	};
}
/**
* Clamp a value to `[lo, hi]` — equivalent to Rust's `f64::clamp`. Returns `lo`
* for `NaN`, matching the saturating intent of the original normalizers.
*/
function clamp(value, lo, hi) {
	if (Number.isNaN(value)) return lo;
	return Math.min(Math.max(value, lo), hi);
}
/**
* Normalize each feature to `[0.0, 1.0]` for the weighted sum.
*
* These normalization constants are the heuristic v1 defaults tuned against
* the ULTRAPLAN §11.1 example ranges (not a labeled corpus). Ported verbatim
* from the Rust `normalize_features`.
*/
function normalizeFeatures(f) {
	return {
		tokenCount: Math.min(f.tokenCount / 1e3, 1),
		syntacticComplexity: clamp(f.syntacticComplexity, 0, 1),
		reasoningDepth: Math.min(f.reasoningDepth / 10, 1),
		domainSpecificity: clamp(f.domainSpecificity, 0, 1),
		touchesFilesCount: Math.min(f.touchesFilesCount / 20, 1)
	};
}
/**
* Classify a feature vector into a {@link Classification} result.
*
* Applies the linear weighted sum from ULTRAPLAN §11.1 and maps the resulting
* score to a tier via {@link THRESHOLD_HIGH} and {@link THRESHOLD_MID}. The
* input {@link PromptFeatures} is preserved so downstream consumers can inspect
* the raw signals that produced the decision.
*
* @param features - The five-feature vector to score.
* @returns The score, tier, and preserved feature vector.
*/
function classify(features) {
	const normalized = normalizeFeatures(features);
	const score = WEIGHT_TOKEN_COUNT * normalized.tokenCount + WEIGHT_SYNTACTIC_COMPLEXITY * normalized.syntacticComplexity + WEIGHT_REASONING_DEPTH * normalized.reasoningDepth + WEIGHT_DOMAIN_SPECIFICITY * normalized.domainSpecificity + WEIGHT_TOUCHES_FILES_COUNT * normalized.touchesFilesCount;
	return {
		score,
		tier: score >= .75 ? "high" : score >= .35 ? "mid" : "low",
		features
	};
}
/**
* Classify a raw prompt into a complexity {@link ComplexityTier}.
*
* The one-call entry point: `extractFeatures` → `classify` → tier. This is the
* **proposer** half of the L1 router — it stops at a tier and never touches a
* model, transport, or credential (Gate-13 compliant).
*
* @param prompt - The raw prompt string to classify.
* @returns The proposed complexity tier (`low` / `mid` / `high`).
*
* @example
* ```ts
* classifyComplexity('list files');                       // 'low'
* classifyComplexity('Why should we compare these and decide the trade-off?'); // 'mid'+
* ```
*/
function classifyComplexity(prompt) {
	return classify(extractFeatures(prompt)).tier;
}
/**
* Map a complexity {@link ComplexityTier} to the {@link RoleName} that
* {@link resolveLLMForSystem} should resolve when no explicit tier/role was
* given.
*
* This is the wiring contract (AC2) that lets a tier flow into the E9
* chokepoint: the classifier proposes a tier, this maps it to a role, and
* `resolveLLMForSystem({ kind: 'role', id })` performs the actual model +
* credential resolution. The mapping is a cheap → capable ladder over the
* canonical {@link RoleName} set — it pins NO model literal of its own.
*
* - `low`  → `hygiene`      — cheapest single-turn cleanup-class role.
* - `mid`  → `consolidation` — the default workhorse role.
* - `high` → `judgement`    — the most-capable reasoning role.
*
* @param tier - The proposed complexity tier.
* @returns The {@link RoleName} to feed into the E9 resolver.
*/
function complexityTierToRole(tier) {
	switch (tier) {
		case "low": return "hygiene";
		case "mid": return "consolidation";
		case "high": return "judgement";
	}
}
/**
* Propose the {@link RoleName} for a raw prompt by classifying its complexity
* and mapping the resulting tier to a role.
*
* Convenience composition of {@link classifyComplexity} and
* {@link complexityTierToRole} — the single call a caller uses to derive a role
* for {@link resolveLLMForSystem} when it has no explicit tier/role.
*
* The returned role is intended to be passed as the structured descriptor:
* `resolveLLMForSystem({ kind: 'role', id: proposeRoleForPrompt(prompt) })`.
*
* @param prompt - The raw prompt string.
* @returns The proposed {@link RoleName} for the E9 chokepoint.
*
* @example
* ```ts
* import { proposeRoleForPrompt } from './complexity-classifier.js';
* import { resolveLLMForSystem } from './system-resolver.js';
*
* const role = proposeRoleForPrompt(userPrompt);
* const llm = await resolveLLMForSystem({ kind: 'role', id: role }, { projectRoot });
* ```
*/
function proposeRoleForPrompt(prompt) {
	return complexityTierToRole(classifyComplexity(prompt));
}
//#endregion
//#region ../core/dist/llm/system-resolver.js
/**
* `resolveLLMForSystem` — the single DRY chokepoint for all LLM resolution (E9).
*
* ## Purpose
*
* CLEO previously had a 4-resolver / 3-picker sprawl: `resolveLLMForRole`,
* `resolveAnthropicForRole`, `resolveCredentials`, and several inline `pickX`
* helpers each duplicated the implicit fallback model literal or called different
* tiers of the resolution chain independently.
*
* `resolveLLMForSystem` is the ONE chokepoint that:
*
*   1. Accepts a semantic "system of use" label (e.g. `'sentient'`, `'memory'`)
*      instead of raw role names — insulating call-sites from config vocabulary.
*   2. Maps the system label to the canonical {@link RoleName} via
*      {@link SYSTEM_ROLE_MAP} (overridable by `opts.roleOverride`).
*   3. Delegates to `resolveLLMForRole` for the full 5-tier config +
*      CredentialPool resolution chain — no duplication.
*   4. When resolution lands on `implicit-fallback`, replaces the hardcoded
*      haiku literal with the SSoT default model from the provider registry
*      (`getProviderProfile(provider).defaultModel`) — satisfying the
*      "not hardcoded" acceptance criterion.
*
* ## What this is NOT
*
* - A replacement for `resolveLLMForRole`. Existing callers of
*   `resolveLLMForRole` continue to work unchanged; T11757 will migrate them
*   to this chokepoint incrementally.
* - A new credential store or provider registry. All credential I/O goes
*   through the existing CredentialPool (E3 pool) inside `resolveLLMForRole`.
*
* @module llm/system-resolver
* @task T11749
* @epic T11745
*/
var logger$2 = getLogger("llm-system-resolver");
/**
* Type guard: is `input` the structured {@link RoleSystem} descriptor form
* (`{ kind: 'role', id: RoleName }`) rather than a flat {@link SystemOfUseLabel}?
*
* A flat label is always a `string`; the descriptor is the only object form the
* chokepoint accepts — so a single `typeof === 'object'` test is sufficient and
* narrows `input` to {@link RoleSystem}.
*
* @task T11750
*/
function isRoleSystem(input) {
	return typeof input === "object" && input !== null && input.kind === "role";
}
/**
* Map a {@link SystemResolverInput} to the {@link RoleName} that
* `resolveLLMForRole` will use for config lookup.
*
* Priority:
*   1. `opts.roleOverride` — explicit caller override (both input forms).
*   2. Structured {@link RoleSystem} descriptor → its `id` IS the role (T11750).
*   3. {@link SYSTEM_ROLE_MAP} — static default for the flat system label.
*   4. L1 complexity proposer (T11906) — when the label maps to NO role AND a
*      `complexityPrompt` was supplied, classify it and propose a role from the
*      resulting tier. This is the "derive a tier from prompt complexity when no
*      explicit tier/role is given" wiring (AC2): it COMPLEMENTS the resolver,
*      only filling in a role the input left blank. The classifier returns a
*      tier and constructs no LLM client.
*
* Returns `null` when a flat label maps to the global default (no role entry)
* and no `complexityPrompt` was supplied. The descriptor form always carries a
* concrete {@link RoleName}, so it never returns `null`.
*/
function deriveRole(input, opts) {
	if (opts?.roleOverride) return opts.roleOverride;
	if (isRoleSystem(input)) return input.id;
	const mapped = SYSTEM_ROLE_MAP[input] ?? null;
	if (mapped !== null) return mapped;
	if (opts?.complexityPrompt !== void 0) {
		const proposed = proposeRoleForPrompt(opts.complexityPrompt);
		logger$2.debug({
			system: input,
			proposedRole: proposed
		}, "system-resolver: derived role from L1 complexity classifier (no explicit tier/role)");
		return proposed;
	}
	return null;
}
/**
* When `resolveLLMForRole` returns `source === 'implicit-fallback'`, the model is
* the hardcoded `IMPLICIT_FALLBACK_MODEL` literal. This function derives the
* default from the **`models_catalog` SSoT** (latest `release_date` for the
* provider) so the resolved model tracks the catalog rather than a frozen
* constant — the "catalog-driven models, NO hardcoded" North Star (E8 · T11944).
*
* ## Resolution order (offline-first degrade — T11944 AC1/AC2)
*
*   1. **`models_catalog` table** (via the {@link resolveCatalogEntry} table-first
*      chokepoint — T11737): newest `release_date` row for the provider. When the
*      table is seeded this WINS and the static builtin literal is never reached.
*   2. **provider-registry `defaultModel`** (the static builtin
*      `ProviderDef.defaultModel`) — reached ONLY when the catalog (table + disk
*      cache + shipped seed) yields nothing for the provider.
*   3. **the existing `IMPLICIT_FALLBACK_MODEL` literal** already on `resolved` —
*      the absolute offline floor, preserved for a fresh/offline install with no
*      catalog AND no provider profile (degrade does NOT break — AC2).
*
* NO network is issued at resolve time (table-or-cache only — AC4). On any lookup
* error the original `resolved` envelope is returned unchanged so the caller is
* never blocked.
*
* @param resolved - The envelope returned by `resolveLLMForRole`.
* @returns The same envelope, possibly with `model` replaced from the catalog SSoT.
*/
async function upgradeCatalogDefault(resolved) {
	if (resolved.source !== "implicit-fallback") return resolved;
	try {
		const { resolveCatalogEntry } = await import('./catalog-resolver-BRX_L7VE.js');
		const { catalogKeyForProvider } = await import('./catalog-model-resolver-Bas9qnoP.js');
		const entry = await resolveCatalogEntry(catalogKeyForProvider(resolved.provider));
		if (entry?.id && entry.id !== resolved.model) {
			logger$2.debug({
				provider: resolved.provider,
				from: resolved.model,
				to: entry.id,
				source: entry.source
			}, "system-resolver: upgrading implicit-fallback model to models_catalog default");
			return {
				...resolved,
				model: entry.id
			};
		}
		if (entry?.id) return resolved;
		const { getProviderProfile } = await import('./provider-registry2-B0lKDoCm.js');
		const profile = await getProviderProfile(resolved.provider);
		if (profile?.defaultModel && profile.defaultModel !== resolved.model) {
			logger$2.debug({
				provider: resolved.provider,
				from: resolved.model,
				to: profile.defaultModel
			}, "system-resolver: catalog empty — degrading to static builtin defaultModel floor");
			return {
				...resolved,
				model: profile.defaultModel
			};
		}
		return resolved;
	} catch (err) {
		logger$2.warn({ err: err instanceof Error ? err.message : String(err) }, "system-resolver: catalog default lookup failed; keeping existing fallback model");
		return resolved;
	}
}
/**
* Resolve the LLM client + credential for a semantic system-of-use label OR a
* structured {@link RoleSystem} descriptor.
*
* This is the single DRY chokepoint for all LLM resolution in CLEO (E9 ·
* T11745). It wraps `resolveLLMForRole` with:
*
*   - System label → role mapping (via {@link SYSTEM_ROLE_MAP}); OR direct
*     `{ kind: 'role', id }` descriptor resolution (T11750 · AC1).
*   - SSoT default model from the provider registry when `implicit-fallback`
*     is reached (the hardcoded haiku literal is NOT used as the final model).
*   - Full CredentialPool (E3) binding via `resolveLLMForRole` delegation.
*
* ## Role equivalence (T11750 · AC1)
*
* `resolveLLMForSystem({ kind: 'role', id })` is the chokepoint expression of a
* direct role resolution — identical to `resolveLLMForRole(id)` for every
* resolution-relevant field (`provider` / `model` / `source` / `credential` /
* `sealedCredential` / `apiMode` / …), differing ONLY by the additive `system`
* + `resolvedRole` envelope fields. There is exactly ONE resolution
* implementation (`resolveLLMForRole`); both input forms funnel through it with
* ZERO duplicated logic. The descriptor form does NOT thread a `systemKey`
* (there is no flat label to key the `llm.systems[key]` override tier on), so it
* walks the same tier chain a bare `resolveLLMForRole(id)` call walks.
*
* Like `resolveLLMForRole`, this function **never throws**: when no credential
* is reachable the caller receives `{ credential: null, client: null, … }` and
* is responsible for its own graceful-degradation path.
*
* @example
* ```ts
* // Flat label (background subsystem):
* const a = await resolveLLMForSystem('sentient', { projectRoot });
* // Structured role descriptor (T11750 · AC1) — equivalent to
* // resolveLLMForRole('consolidation'):
* const b = await resolveLLMForSystem({ kind: 'role', id: 'consolidation' }, { projectRoot });
* if (!a.sealedCredential || !a.client) {
*   return null; // graceful no-op — no credential available
* }
* // Use resolved.model, resolved.client, resolved.credential (metadata only);
* // materialize the secret at the wire via resolved.sealedCredential.fetch().
* ```
*
* @param system - Flat {@link SystemOfUseLabel} or {@link RoleSystem} descriptor.
* @param opts   - Optional overrides (project root, role override, skipCatalogDefault).
* @returns A {@link ResolvedLLMForSystem} envelope; never throws.
*
* @task T11749
* @task T11750
* @epic T11745
*/
async function resolveLLMForSystem(system, opts) {
	const resolvedRole = deriveRole(system, opts);
	const roleForResolution = resolvedRole ?? "consolidation";
	const systemKey = opts?.systemKey ?? (isRoleSystem(system) ? void 0 : system);
	let base;
	try {
		base = await resolveLLMForRole(roleForResolution, {
			projectRoot: opts?.projectRoot,
			systemKey,
			...opts?.profile !== void 0 ? { profileOverride: opts.profile } : {}
		});
	} catch (err) {
		logger$2.warn({
			err: err instanceof Error ? err.message : String(err),
			system,
			role: roleForResolution
		}, "system-resolver: resolveLLMForRole threw unexpectedly; returning null-credential envelope");
		base = {
			provider: IMPLICIT_FALLBACK_PROVIDER,
			model: "unknown",
			client: null,
			credential: null,
			sealedCredential: null,
			source: "implicit-fallback",
			apiMode: "anthropic_messages",
			baseUrl: null,
			authType: null
		};
	}
	return {
		...opts?.skipCatalogDefault ? base : await upgradeCatalogDefault(base),
		system,
		resolvedRole
	};
}

var DreamExtractedMemorySchema = object({
	type: _enum([
		"decision",
		"pattern",
		"learning",
		"constraint"
	]),
	content: string().min(1).max(500),
	importance: number().min(0).max(1),
	entities: array(string()).max(20),
	justification: string().min(1).max(200)
});
object({ memories: array(DreamExtractedMemorySchema).max(15) });

/**
* Zod schema for the structured output returned by the LLM escalation call.
*/
object({
	is_real_defect: boolean(),
	confidence: number().min(0).max(1),
	recommended_action: _enum([
		"auto-fix",
		"propose",
		"ignore"
	]),
	reasoning: string()
});

//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/create-id.js
var require_create_id = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.createID = createID;
	var node_crypto_1 = __importDefault(__require("node:crypto"));
	function createID(prefix = "", length = 16) {
		const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		const values = node_crypto_1.default.randomBytes(length);
		const id = Array.from(values, (v) => charset[v % 62]).join("");
		return prefix ? `${prefix}-${id}` : id;
	}
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/logger.js
var require_logger = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	var levelColors = {
		INFO: "\x1B[36m",
		WARN: "\x1B[33m",
		ERROR: "\x1B[31m",
		DEBUG: "\x1B[35m"
	};
	var GREEN = "\x1B[32m";
	var RESET = "\x1B[0m";
	function log(level, message, extra) {
		const timestamp = (/* @__PURE__ */ new Date()).toISOString();
		const color = levelColors[level] ?? "";
		const output = `${`[${timestamp}] [PID: ${process.pid}] ${GREEN}[NODE-CRON]${GREEN} ${color}[${level}]${RESET}`} ${message}`;
		switch (level) {
			case "ERROR":
				console.error(output, extra ?? "");
				break;
			case "DEBUG":
				console.debug(output, extra ?? "");
				break;
			case "WARN":
				console.warn(output);
				break;
			default:
				console.info(output);
				break;
		}
	}
	exports$1.default = {
		info(message) {
			log("INFO", message);
		},
		warn(message) {
			log("WARN", message);
		},
		error(message, err) {
			if (message instanceof Error) log("ERROR", message.message, message);
			else log("ERROR", message, err);
		},
		debug(message, err) {
			if (message instanceof Error) log("DEBUG", message.message, message);
			else log("DEBUG", message, err);
		}
	};
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/promise/tracked-promise.js
var require_tracked_promise = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.TrackedPromise = void 0;
	var TrackedPromise = class {
		promise;
		error;
		state;
		value;
		constructor(executor) {
			this.state = "pending";
			this.promise = new Promise((resolve, reject) => {
				executor((value) => {
					this.state = "fulfilled";
					this.value = value;
					resolve(value);
				}, (error) => {
					this.state = "rejected";
					this.error = error;
					reject(error);
				});
			});
		}
		getPromise() {
			return this.promise;
		}
		getState() {
			return this.state;
		}
		isPending() {
			return this.state === "pending";
		}
		isFulfilled() {
			return this.state === "fulfilled";
		}
		isRejected() {
			return this.state === "rejected";
		}
		getValue() {
			return this.value;
		}
		getError() {
			return this.error;
		}
		then(onfulfilled, onrejected) {
			return this.promise.then(onfulfilled, onrejected);
		}
		catch(onrejected) {
			return this.promise.catch(onrejected);
		}
		finally(onfinally) {
			return this.promise.finally(onfinally);
		}
	};
	exports$1.TrackedPromise = TrackedPromise;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/scheduler/runner.js
var require_runner = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.Runner = void 0;
	var create_id_1 = require_create_id();
	var logger_1 = __importDefault(require_logger());
	var tracked_promise_1 = require_tracked_promise();
	function emptyOnFn() {}
	function emptyHookFn() {
		return true;
	}
	function defaultOnError(date, error) {
		logger_1.default.error("Task failed with error!", error);
	}
	var Runner = class {
		timeMatcher;
		onMatch;
		noOverlap;
		maxExecutions;
		maxRandomDelay;
		runCount;
		running;
		heartBeatTimeout;
		onMissedExecution;
		onOverlap;
		onError;
		beforeRun;
		onFinished;
		onMaxExecutions;
		constructor(timeMatcher, onMatch, options) {
			this.timeMatcher = timeMatcher;
			this.onMatch = onMatch;
			this.noOverlap = options == void 0 || options.noOverlap === void 0 ? false : options.noOverlap;
			this.maxExecutions = options?.maxExecutions;
			this.maxRandomDelay = options?.maxRandomDelay || 0;
			this.onMissedExecution = options?.onMissedExecution || emptyOnFn;
			this.onOverlap = options?.onOverlap || emptyOnFn;
			this.onError = options?.onError || defaultOnError;
			this.onFinished = options?.onFinished || emptyHookFn;
			this.beforeRun = options?.beforeRun || emptyHookFn;
			this.onMaxExecutions = options?.onMaxExecutions || emptyOnFn;
			this.runCount = 0;
			this.running = false;
		}
		start() {
			this.running = true;
			let lastExecution;
			let expectedNextExecution;
			const scheduleNextHeartBeat = (currentDate) => {
				if (this.running) {
					clearTimeout(this.heartBeatTimeout);
					this.heartBeatTimeout = setTimeout(heartBeat, getDelay(this.timeMatcher, currentDate));
				}
			};
			const runTask = (date) => {
				return new Promise(async (resolve) => {
					const execution = {
						id: (0, create_id_1.createID)("exec"),
						reason: "scheduled"
					};
					const shouldExecute = await this.beforeRun(date, execution);
					const randomDelay = Math.floor(Math.random() * this.maxRandomDelay);
					if (shouldExecute) setTimeout(async () => {
						try {
							this.runCount++;
							execution.startedAt = /* @__PURE__ */ new Date();
							const result = await this.onMatch(date, execution);
							execution.finishedAt = /* @__PURE__ */ new Date();
							execution.result = result;
							this.onFinished(date, execution);
							if (this.maxExecutions && this.runCount >= this.maxExecutions) {
								this.onMaxExecutions(date);
								this.stop();
							}
						} catch (error) {
							execution.finishedAt = /* @__PURE__ */ new Date();
							execution.error = error;
							this.onError(date, error, execution);
						}
						resolve(true);
					}, randomDelay);
				});
			};
			const checkAndRun = (date) => {
				return new tracked_promise_1.TrackedPromise(async (resolve, reject) => {
					try {
						if (this.timeMatcher.match(date)) await runTask(date);
						resolve(true);
					} catch (err) {
						reject(err);
					}
				});
			};
			const heartBeat = async () => {
				const currentDate = nowWithoutMs();
				if (expectedNextExecution && expectedNextExecution.getTime() < currentDate.getTime()) while (expectedNextExecution.getTime() < currentDate.getTime()) {
					logger_1.default.warn(`missed execution at ${expectedNextExecution}! Possible blocking IO or high CPU user at the same process used by node-cron.`);
					expectedNextExecution = this.timeMatcher.getNextMatch(expectedNextExecution);
					runAsync(this.onMissedExecution, expectedNextExecution, defaultOnError);
				}
				if (lastExecution && lastExecution.getState() === "pending") {
					runAsync(this.onOverlap, currentDate, defaultOnError);
					if (this.noOverlap) {
						logger_1.default.warn("task still running, new execution blocked by overlap prevention!");
						expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
						scheduleNextHeartBeat(currentDate);
						return;
					}
				}
				lastExecution = checkAndRun(currentDate);
				expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
				scheduleNextHeartBeat(currentDate);
			};
			this.heartBeatTimeout = setTimeout(() => {
				heartBeat();
			}, getDelay(this.timeMatcher, nowWithoutMs()));
		}
		nextRun() {
			return this.timeMatcher.getNextMatch(/* @__PURE__ */ new Date());
		}
		stop() {
			this.running = false;
			if (this.heartBeatTimeout) {
				clearTimeout(this.heartBeatTimeout);
				this.heartBeatTimeout = void 0;
			}
		}
		isStarted() {
			return !!this.heartBeatTimeout && this.running;
		}
		isStopped() {
			return !this.isStarted();
		}
		async execute() {
			const date = /* @__PURE__ */ new Date();
			const execution = {
				id: (0, create_id_1.createID)("exec"),
				reason: "invoked"
			};
			try {
				if (await this.beforeRun(date, execution)) {
					this.runCount++;
					execution.startedAt = /* @__PURE__ */ new Date();
					const result = await this.onMatch(date, execution);
					execution.finishedAt = /* @__PURE__ */ new Date();
					execution.result = result;
					this.onFinished(date, execution);
				}
			} catch (error) {
				execution.finishedAt = /* @__PURE__ */ new Date();
				execution.error = error;
				this.onError(date, error, execution);
			}
		}
	};
	exports$1.Runner = Runner;
	async function runAsync(fn, date, onError) {
		try {
			await fn(date);
		} catch (error) {
			onError(date, error);
		}
	}
	function getDelay(timeMatcher, currentDate) {
		const maxDelay = 864e5;
		const nextRun = timeMatcher.getNextMatch(currentDate);
		const now = /* @__PURE__ */ new Date();
		const delay = nextRun.getTime() - now.getTime();
		if (delay > maxDelay) return maxDelay;
		return Math.max(0, delay);
	}
	function nowWithoutMs() {
		const date = /* @__PURE__ */ new Date();
		date.setMilliseconds(0);
		return date;
	}
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.js
var require_month_names_conversion = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.default = (() => {
		const months = [
			"january",
			"february",
			"march",
			"april",
			"may",
			"june",
			"july",
			"august",
			"september",
			"october",
			"november",
			"december"
		];
		const shortMonths = [
			"jan",
			"feb",
			"mar",
			"apr",
			"may",
			"jun",
			"jul",
			"aug",
			"sep",
			"oct",
			"nov",
			"dec"
		];
		function convertMonthName(expression, items) {
			for (let i = 0; i < items.length; i++) expression = expression.replace(new RegExp(items[i], "gi"), i + 1);
			return expression;
		}
		function interprete(monthExpression) {
			monthExpression = convertMonthName(monthExpression, months);
			monthExpression = convertMonthName(monthExpression, shortMonths);
			return monthExpression;
		}
		return interprete;
	})();
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.js
var require_week_day_names_conversion = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.default = (() => {
		const weekDays = [
			"sunday",
			"monday",
			"tuesday",
			"wednesday",
			"thursday",
			"friday",
			"saturday"
		];
		const shortWeekDays = [
			"sun",
			"mon",
			"tue",
			"wed",
			"thu",
			"fri",
			"sat"
		];
		function convertWeekDayName(expression, items) {
			for (let i = 0; i < items.length; i++) expression = expression.replace(new RegExp(items[i], "gi"), i);
			return expression;
		}
		function convertWeekDays(expression) {
			expression = expression.replace("7", "0");
			expression = convertWeekDayName(expression, weekDays);
			return convertWeekDayName(expression, shortWeekDays);
		}
		return convertWeekDays;
	})();
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.js
var require_asterisk_to_range_conversion = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.default = (() => {
		function convertAsterisk(expression, replecement) {
			if (expression.indexOf("*") !== -1) return expression.replace("*", replecement);
			return expression;
		}
		function convertAsterisksToRanges(expressions) {
			expressions[0] = convertAsterisk(expressions[0], "0-59");
			expressions[1] = convertAsterisk(expressions[1], "0-59");
			expressions[2] = convertAsterisk(expressions[2], "0-23");
			expressions[3] = convertAsterisk(expressions[3], "1-31");
			expressions[4] = convertAsterisk(expressions[4], "1-12");
			expressions[5] = convertAsterisk(expressions[5], "0-6");
			return expressions;
		}
		return convertAsterisksToRanges;
	})();
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.js
var require_range_conversion = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.default = (() => {
		function replaceWithRange(expression, text, init, end, stepTxt) {
			const step = parseInt(stepTxt);
			const numbers = [];
			let last = parseInt(end);
			let first = parseInt(init);
			if (first > last) {
				last = parseInt(init);
				first = parseInt(end);
			}
			for (let i = first; i <= last; i += step) numbers.push(i);
			return expression.replace(new RegExp(text, "i"), numbers.join());
		}
		function convertRange(expression) {
			const rangeRegEx = /(\d+)-(\d+)(\/(\d+)|)/;
			let match = rangeRegEx.exec(expression);
			while (match !== null && match.length > 0) {
				expression = replaceWithRange(expression, match[0], match[1], match[2], match[4] || "1");
				match = rangeRegEx.exec(expression);
			}
			return expression;
		}
		function convertAllRanges(expressions) {
			for (let i = 0; i < expressions.length; i++) expressions[i] = convertRange(expressions[i]);
			return expressions;
		}
		return convertAllRanges;
	})();
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/index.js
var require_convertion = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	var month_names_conversion_1 = __importDefault(require_month_names_conversion());
	var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
	var asterisk_to_range_conversion_1 = __importDefault(require_asterisk_to_range_conversion());
	var range_conversion_1 = __importDefault(require_range_conversion());
	exports$1.default = (() => {
		function appendSeccondExpression(expressions) {
			if (expressions.length === 5) return ["0"].concat(expressions);
			return expressions;
		}
		function removeSpaces(str) {
			return str.replace(/\s{2,}/g, " ").trim();
		}
		function normalizeIntegers(expressions) {
			for (let i = 0; i < expressions.length; i++) {
				const numbers = expressions[i].split(",");
				for (let j = 0; j < numbers.length; j++) numbers[j] = parseInt(numbers[j]);
				expressions[i] = numbers;
			}
			return expressions;
		}
		function interprete(expression) {
			let expressions = removeSpaces(`${expression}`).split(" ");
			expressions = appendSeccondExpression(expressions);
			expressions[4] = (0, month_names_conversion_1.default)(expressions[4]);
			expressions[5] = (0, week_day_names_conversion_1.default)(expressions[5]);
			expressions = (0, asterisk_to_range_conversion_1.default)(expressions);
			expressions = (0, range_conversion_1.default)(expressions);
			expressions = normalizeIntegers(expressions);
			return expressions;
		}
		return interprete;
	})();
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/localized-time.js
var require_localized_time = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.LocalizedTime = void 0;
	var LocalizedTime = class {
		timestamp;
		parts;
		timezone;
		constructor(date, timezone) {
			this.timestamp = date.getTime();
			this.timezone = timezone;
			this.parts = buildDateParts(date, timezone);
		}
		toDate() {
			return new Date(this.timestamp);
		}
		toISO() {
			const gmt = this.parts.gmt.replace(/^GMT/, "");
			const offset = gmt ? gmt : "Z";
			const pad = (n) => String(n).padStart(2, "0");
			return `${this.parts.year}-${pad(this.parts.month)}-${pad(this.parts.day)}T${pad(this.parts.hour)}:${pad(this.parts.minute)}:${pad(this.parts.second)}.${String(this.parts.milisecond).padStart(3, "0")}` + offset;
		}
		getParts() {
			return this.parts;
		}
		set(field, value) {
			this.parts[field] = value;
			const newDate = new Date(this.toISO());
			this.timestamp = newDate.getTime();
			this.parts = buildDateParts(newDate, this.timezone);
		}
	};
	exports$1.LocalizedTime = LocalizedTime;
	function buildDateParts(date, timezone) {
		const dftOptions = {
			year: "numeric",
			month: "2-digit",
			day: "2-digit",
			hour: "2-digit",
			minute: "2-digit",
			second: "2-digit",
			weekday: "short",
			hour12: false
		};
		if (timezone) dftOptions.timeZone = timezone;
		const parts = new Intl.DateTimeFormat("en-US", dftOptions).formatToParts(date).filter((part) => {
			return part.type !== "literal";
		}).reduce((acc, part) => {
			acc[part.type] = part.value;
			return acc;
		}, {});
		return {
			day: parseInt(parts.day),
			month: parseInt(parts.month),
			year: parseInt(parts.year),
			hour: parts.hour === "24" ? 0 : parseInt(parts.hour),
			minute: parseInt(parts.minute),
			second: parseInt(parts.second),
			milisecond: date.getMilliseconds(),
			weekday: parts.weekday,
			gmt: getTimezoneGMT(date, timezone)
		};
	}
	function getTimezoneGMT(date, timezone) {
		const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
		const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
		let offsetInMinutes = (utcDate.getTime() - tzDate.getTime()) / 6e4;
		const sign = offsetInMinutes <= 0 ? "+" : "-";
		offsetInMinutes = Math.abs(offsetInMinutes);
		if (offsetInMinutes === 0) return "Z";
		return `GMT${sign}${Math.floor(offsetInMinutes / 60).toString().padStart(2, "0")}:${Math.floor(offsetInMinutes % 60).toString().padStart(2, "0")}`;
	}
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/matcher-walker.js
var require_matcher_walker = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.MatcherWalker = void 0;
	var convertion_1 = __importDefault(require_convertion());
	var localized_time_1 = require_localized_time();
	var time_matcher_1 = require_time_matcher();
	var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
	var MatcherWalker = class {
		cronExpression;
		baseDate;
		pattern;
		expressions;
		timeMatcher;
		timezone;
		constructor(cronExpression, baseDate, timezone) {
			this.cronExpression = cronExpression;
			this.baseDate = baseDate;
			this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, timezone);
			this.timezone = timezone;
			this.expressions = (0, convertion_1.default)(cronExpression);
		}
		isMatching() {
			return this.timeMatcher.match(this.baseDate);
		}
		matchNext() {
			const findNextDateIgnoringWeekday = () => {
				const baseDate = new Date(this.baseDate.getTime());
				baseDate.setMilliseconds(0);
				const localTime = new localized_time_1.LocalizedTime(baseDate, this.timezone);
				const dateParts = localTime.getParts();
				const date = new localized_time_1.LocalizedTime(localTime.toDate(), this.timezone);
				const seconds = this.expressions[0];
				const nextSecond = availableValue(seconds, dateParts.second);
				if (nextSecond) {
					date.set("second", nextSecond);
					if (this.timeMatcher.match(date.toDate())) return date;
				}
				date.set("second", seconds[0]);
				const minutes = this.expressions[1];
				const nextMinute = availableValue(minutes, dateParts.minute);
				if (nextMinute) {
					date.set("minute", nextMinute);
					if (this.timeMatcher.match(date.toDate())) return date;
				}
				date.set("minute", minutes[0]);
				const hours = this.expressions[2];
				const nextHour = availableValue(hours, dateParts.hour);
				if (nextHour) {
					date.set("hour", nextHour);
					if (this.timeMatcher.match(date.toDate())) return date;
				}
				date.set("hour", hours[0]);
				const days = this.expressions[3];
				const nextDay = availableValue(days, dateParts.day);
				if (nextDay) {
					date.set("day", nextDay);
					if (this.timeMatcher.match(date.toDate())) return date;
				}
				date.set("day", days[0]);
				const months = this.expressions[4];
				const nextMonth = availableValue(months, dateParts.month);
				if (nextMonth) {
					date.set("month", nextMonth);
					if (this.timeMatcher.match(date.toDate())) return date;
				}
				date.set("year", date.getParts().year + 1);
				date.set("month", months[0]);
				return date;
			};
			const date = findNextDateIgnoringWeekday();
			const weekdays = this.expressions[5];
			let currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date.getParts().weekday));
			while (!(weekdays.indexOf(currentWeekday) > -1)) {
				date.set("year", date.getParts().year + 1);
				currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date.getParts().weekday));
			}
			return date;
		}
	};
	exports$1.MatcherWalker = MatcherWalker;
	function availableValue(values, currentValue) {
		const availableValues = values.sort((a, b) => a - b).filter((s) => s > currentValue);
		if (availableValues.length > 0) return availableValues[0];
		return false;
	}
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/time-matcher.js
var require_time_matcher = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.TimeMatcher = void 0;
	var index_1 = __importDefault(require_convertion());
	var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
	var localized_time_1 = require_localized_time();
	var matcher_walker_1 = require_matcher_walker();
	function matchValue(allowedValues, value) {
		return allowedValues.indexOf(value) !== -1;
	}
	var TimeMatcher = class {
		timezone;
		pattern;
		expressions;
		constructor(pattern, timezone) {
			this.timezone = timezone;
			this.pattern = pattern;
			this.expressions = (0, index_1.default)(pattern);
		}
		match(date) {
			const parts = new localized_time_1.LocalizedTime(date, this.timezone).getParts();
			const runOnSecond = matchValue(this.expressions[0], parts.second);
			const runOnMinute = matchValue(this.expressions[1], parts.minute);
			const runOnHour = matchValue(this.expressions[2], parts.hour);
			const runOnDay = matchValue(this.expressions[3], parts.day);
			const runOnMonth = matchValue(this.expressions[4], parts.month);
			const runOnWeekDay = matchValue(this.expressions[5], parseInt((0, week_day_names_conversion_1.default)(parts.weekday)));
			return runOnSecond && runOnMinute && runOnHour && runOnDay && runOnMonth && runOnWeekDay;
		}
		getNextMatch(date) {
			return new matcher_walker_1.MatcherWalker(this.pattern, date, this.timezone).matchNext().toDate();
		}
	};
	exports$1.TimeMatcher = TimeMatcher;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/state-machine.js
var require_state_machine = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.StateMachine = void 0;
	var allowedTransitions = {
		"stopped": [
			"stopped",
			"idle",
			"destroyed"
		],
		"idle": [
			"idle",
			"running",
			"stopped",
			"destroyed"
		],
		"running": [
			"running",
			"idle",
			"stopped",
			"destroyed"
		],
		"destroyed": ["destroyed"]
	};
	var StateMachine = class {
		state;
		constructor(initial = "stopped") {
			this.state = initial;
		}
		changeState(state) {
			if (allowedTransitions[this.state].includes(state)) this.state = state;
			else throw new Error(`invalid transition from ${this.state} to ${state}`);
		}
	};
	exports$1.StateMachine = StateMachine;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.js
var require_inline_scheduled_task = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.InlineScheduledTask = void 0;
	var events_1 = __importDefault(__require("events"));
	var runner_1 = require_runner();
	var time_matcher_1 = require_time_matcher();
	var create_id_1 = require_create_id();
	var state_machine_1 = require_state_machine();
	var logger_1 = __importDefault(require_logger());
	var localized_time_1 = require_localized_time();
	var TaskEmitter = class extends events_1.default {};
	var InlineScheduledTask = class {
		emitter;
		cronExpression;
		timeMatcher;
		runner;
		id;
		name;
		stateMachine;
		timezone;
		constructor(cronExpression, taskFn, options) {
			this.emitter = new TaskEmitter();
			this.cronExpression = cronExpression;
			this.id = (0, create_id_1.createID)("task", 12);
			this.name = options?.name || this.id;
			this.timezone = options?.timezone;
			this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, options?.timezone);
			this.stateMachine = new state_machine_1.StateMachine();
			const runnerOptions = {
				timezone: options?.timezone,
				noOverlap: options?.noOverlap,
				maxExecutions: options?.maxExecutions,
				maxRandomDelay: options?.maxRandomDelay,
				beforeRun: (date, execution) => {
					if (execution.reason === "scheduled") this.changeState("running");
					this.emitter.emit("execution:started", this.createContext(date, execution));
					return true;
				},
				onFinished: (date, execution) => {
					if (execution.reason === "scheduled") this.changeState("idle");
					this.emitter.emit("execution:finished", this.createContext(date, execution));
					return true;
				},
				onError: (date, error, execution) => {
					logger_1.default.error(error);
					this.emitter.emit("execution:failed", this.createContext(date, execution));
					this.changeState("idle");
				},
				onOverlap: (date) => {
					this.emitter.emit("execution:overlap", this.createContext(date));
				},
				onMissedExecution: (date) => {
					this.emitter.emit("execution:missed", this.createContext(date));
				},
				onMaxExecutions: (date) => {
					this.emitter.emit("execution:maxReached", this.createContext(date));
					this.destroy();
				}
			};
			this.runner = new runner_1.Runner(this.timeMatcher, (date, execution) => {
				return taskFn(this.createContext(date, execution));
			}, runnerOptions);
		}
		getNextRun() {
			if (this.stateMachine.state !== "stopped") return this.runner.nextRun();
			return null;
		}
		changeState(state) {
			if (this.runner.isStarted()) this.stateMachine.changeState(state);
		}
		start() {
			if (this.runner.isStopped()) {
				this.runner.start();
				this.stateMachine.changeState("idle");
				this.emitter.emit("task:started", this.createContext(/* @__PURE__ */ new Date()));
			}
		}
		stop() {
			if (this.runner.isStarted()) {
				this.runner.stop();
				this.stateMachine.changeState("stopped");
				this.emitter.emit("task:stopped", this.createContext(/* @__PURE__ */ new Date()));
			}
		}
		getStatus() {
			return this.stateMachine.state;
		}
		destroy() {
			if (this.stateMachine.state === "destroyed") return;
			this.stop();
			this.stateMachine.changeState("destroyed");
			this.emitter.emit("task:destroyed", this.createContext(/* @__PURE__ */ new Date()));
		}
		execute() {
			return new Promise((resolve, reject) => {
				const onFail = (context) => {
					this.off("execution:finished", onFail);
					reject(context.execution?.error);
				};
				const onFinished = (context) => {
					this.off("execution:failed", onFail);
					resolve(context.execution?.result);
				};
				this.once("execution:finished", onFinished);
				this.once("execution:failed", onFail);
				this.runner.execute();
			});
		}
		on(event, fun) {
			this.emitter.on(event, fun);
		}
		off(event, fun) {
			this.emitter.off(event, fun);
		}
		once(event, fun) {
			this.emitter.once(event, fun);
		}
		createContext(executionDate, execution) {
			const localTime = new localized_time_1.LocalizedTime(executionDate, this.timezone);
			return {
				date: localTime.toDate(),
				dateLocalIso: localTime.toISO(),
				triggeredAt: /* @__PURE__ */ new Date(),
				task: this,
				execution
			};
		}
	};
	exports$1.InlineScheduledTask = InlineScheduledTask;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/task-registry.js
var require_task_registry = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.TaskRegistry = void 0;
	var tasks = /* @__PURE__ */ new Map();
	var TaskRegistry = class {
		add(task) {
			if (this.has(task.id)) throw Error(`task ${task.id} already registred!`);
			tasks.set(task.id, task);
			task.on("task:destroyed", () => {
				this.remove(task);
			});
		}
		get(taskId) {
			return tasks.get(taskId);
		}
		remove(task) {
			if (this.has(task.id)) {
				task?.destroy();
				tasks.delete(task.id);
			}
		}
		all() {
			return tasks;
		}
		has(taskId) {
			return tasks.has(taskId);
		}
		killAll() {
			tasks.forEach((id) => this.remove(id));
		}
	};
	exports$1.TaskRegistry = TaskRegistry;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.js
var require_pattern_validation = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	var index_1 = __importDefault(require_convertion());
	var validationRegex = /^(?:\d+|\*|\*\/\d+)$/;
	function isValidExpression(expression, min, max) {
		const options = expression;
		for (const option of options) {
			const optionAsInt = parseInt(option, 10);
			if (!Number.isNaN(optionAsInt) && (optionAsInt < min || optionAsInt > max) || !validationRegex.test(option)) return false;
		}
		return true;
	}
	function isInvalidSecond(expression) {
		return !isValidExpression(expression, 0, 59);
	}
	function isInvalidMinute(expression) {
		return !isValidExpression(expression, 0, 59);
	}
	function isInvalidHour(expression) {
		return !isValidExpression(expression, 0, 23);
	}
	function isInvalidDayOfMonth(expression) {
		return !isValidExpression(expression, 1, 31);
	}
	function isInvalidMonth(expression) {
		return !isValidExpression(expression, 1, 12);
	}
	function isInvalidWeekDay(expression) {
		return !isValidExpression(expression, 0, 7);
	}
	function validateFields(patterns, executablePatterns) {
		if (isInvalidSecond(executablePatterns[0])) throw new Error(`${patterns[0]} is a invalid expression for second`);
		if (isInvalidMinute(executablePatterns[1])) throw new Error(`${patterns[1]} is a invalid expression for minute`);
		if (isInvalidHour(executablePatterns[2])) throw new Error(`${patterns[2]} is a invalid expression for hour`);
		if (isInvalidDayOfMonth(executablePatterns[3])) throw new Error(`${patterns[3]} is a invalid expression for day of month`);
		if (isInvalidMonth(executablePatterns[4])) throw new Error(`${patterns[4]} is a invalid expression for month`);
		if (isInvalidWeekDay(executablePatterns[5])) throw new Error(`${patterns[5]} is a invalid expression for week day`);
	}
	function validate(pattern) {
		if (typeof pattern !== "string") throw new TypeError("pattern must be a string!");
		const patterns = pattern.split(" ");
		const executablePatterns = (0, index_1.default)(pattern);
		if (patterns.length === 5) patterns.unshift("0");
		validateFields(patterns, executablePatterns);
	}
	exports$1.default = validate;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.js
var require_background_scheduled_task = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	var path_1$1 = __require("path");
	var child_process_1 = __require("child_process");
	var create_id_1 = require_create_id();
	var stream_1 = __require("stream");
	var state_machine_1 = require_state_machine();
	var localized_time_1 = require_localized_time();
	var logger_1 = __importDefault(require_logger());
	var time_matcher_1 = require_time_matcher();
	var daemonPath = (0, path_1$1.resolve)(new URL(".", import.meta.url).pathname.replace(/\/+$/, ""), "daemon.js");
	var TaskEmitter = class extends stream_1.EventEmitter {};
	var BackgroundScheduledTask = class {
		emitter;
		id;
		name;
		cronExpression;
		taskPath;
		options;
		forkProcess;
		stateMachine;
		constructor(cronExpression, taskPath, options) {
			this.cronExpression = cronExpression;
			this.taskPath = taskPath;
			this.options = options;
			this.id = (0, create_id_1.createID)("task");
			this.name = options?.name || this.id;
			this.emitter = new TaskEmitter();
			this.stateMachine = new state_machine_1.StateMachine("stopped");
			this.on("task:stopped", () => {
				this.forkProcess?.kill();
				this.forkProcess = void 0;
				this.stateMachine.changeState("stopped");
			});
			this.on("task:destroyed", () => {
				this.forkProcess?.kill();
				this.forkProcess = void 0;
				this.stateMachine.changeState("destroyed");
			});
		}
		getNextRun() {
			if (this.stateMachine.state !== "stopped") return new time_matcher_1.TimeMatcher(this.cronExpression, this.options?.timezone).getNextMatch(/* @__PURE__ */ new Date());
			return null;
		}
		start() {
			return new Promise((resolve, reject) => {
				if (this.forkProcess) return resolve(void 0);
				const timeout = setTimeout(() => {
					reject(/* @__PURE__ */ new Error("Start operation timed out"));
				}, 5e3);
				try {
					this.forkProcess = (0, child_process_1.fork)(daemonPath);
					this.forkProcess.on("error", (err) => {
						clearTimeout(timeout);
						reject(/* @__PURE__ */ new Error(`Error on daemon: ${err.message}`));
					});
					this.forkProcess.on("exit", (code, signal) => {
						if (code !== 0 && signal !== "SIGTERM") {
							const erro = /* @__PURE__ */ new Error(`node-cron daemon exited with code ${code || signal}`);
							logger_1.default.error(erro);
							clearTimeout(timeout);
							reject(erro);
						}
					});
					this.forkProcess.on("message", (message) => {
						if (message.jsonError) {
							if (message.context?.execution) {
								message.context.execution.error = deserializeError(message.jsonError);
								delete message.jsonError;
							}
						}
						if (message.context?.task?.state) this.stateMachine.changeState(message.context?.task?.state);
						if (message.context) {
							const execution = message.context?.execution;
							delete execution?.hasError;
							const context = this.createContext(new Date(message.context.date), execution);
							this.emitter.emit(message.event, context);
						}
					});
					this.once("task:started", () => {
						this.stateMachine.changeState("idle");
						clearTimeout(timeout);
						resolve(void 0);
					});
					this.forkProcess.send({
						command: "task:start",
						path: this.taskPath,
						cron: this.cronExpression,
						options: this.options
					});
				} catch (error) {
					reject(error);
				}
			});
		}
		stop() {
			return new Promise((resolve, reject) => {
				if (!this.forkProcess) return resolve(void 0);
				const timeoutId = setTimeout(() => {
					clearTimeout(timeoutId);
					reject(/* @__PURE__ */ new Error("Stop operation timed out"));
				}, 5e3);
				const cleanupAndResolve = () => {
					clearTimeout(timeoutId);
					this.off("task:stopped", onStopped);
					this.forkProcess = void 0;
					resolve(void 0);
				};
				const onStopped = () => {
					cleanupAndResolve();
				};
				this.once("task:stopped", onStopped);
				this.forkProcess.send({ command: "task:stop" });
			});
		}
		getStatus() {
			return this.stateMachine.state;
		}
		destroy() {
			return new Promise((resolve, reject) => {
				if (!this.forkProcess) return resolve(void 0);
				const timeoutId = setTimeout(() => {
					clearTimeout(timeoutId);
					reject(/* @__PURE__ */ new Error("Destroy operation timed out"));
				}, 5e3);
				const onDestroy = () => {
					clearTimeout(timeoutId);
					this.off("task:destroyed", onDestroy);
					resolve(void 0);
				};
				this.once("task:destroyed", onDestroy);
				this.forkProcess.send({ command: "task:destroy" });
			});
		}
		execute() {
			return new Promise((resolve, reject) => {
				if (!this.forkProcess) return reject(/* @__PURE__ */ new Error("Cannot execute background task because it hasn't been started yet. Please initialize the task using the start() method before attempting to execute it."));
				const timeoutId = setTimeout(() => {
					cleanupListeners();
					reject(/* @__PURE__ */ new Error("Execution timeout exceeded"));
				}, 5e3);
				const cleanupListeners = () => {
					clearTimeout(timeoutId);
					this.off("execution:finished", onFinished);
					this.off("execution:failed", onFail);
				};
				const onFinished = (context) => {
					cleanupListeners();
					resolve(context.execution?.result);
				};
				const onFail = (context) => {
					cleanupListeners();
					reject(context.execution?.error || /* @__PURE__ */ new Error("Execution failed without specific error"));
				};
				this.once("execution:finished", onFinished);
				this.once("execution:failed", onFail);
				this.forkProcess.send({ command: "task:execute" });
			});
		}
		on(event, fun) {
			this.emitter.on(event, fun);
		}
		off(event, fun) {
			this.emitter.off(event, fun);
		}
		once(event, fun) {
			this.emitter.once(event, fun);
		}
		createContext(executionDate, execution) {
			const localTime = new localized_time_1.LocalizedTime(executionDate, this.options?.timezone);
			return {
				date: localTime.toDate(),
				dateLocalIso: localTime.toISO(),
				triggeredAt: /* @__PURE__ */ new Date(),
				task: this,
				execution
			};
		}
	};
	function deserializeError(str) {
		const data = JSON.parse(str);
		const err = new (globalThis[data.name] || Error)(data.message);
		if (data.stack) err.stack = data.stack;
		Object.keys(data).forEach((key) => {
			if (![
				"name",
				"message",
				"stack"
			].includes(key)) err[key] = data[key];
		});
		return err;
	}
	exports$1.default = BackgroundScheduledTask;
}));
//#endregion
//#region ../../node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/node-cron.js
var require_node_cron = /* @__PURE__ */ __commonJSMin(((exports$1) => {
	var __importDefault = exports$1 && exports$1.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	Object.defineProperty(exports$1, "__esModule", { value: true });
	exports$1.nodeCron = exports$1.getTask = exports$1.getTasks = void 0;
	exports$1.schedule = schedule;
	exports$1.createTask = createTask;
	exports$1.solvePath = solvePath;
	exports$1.validate = validate;
	var inline_scheduled_task_1 = require_inline_scheduled_task();
	var task_registry_1 = require_task_registry();
	var pattern_validation_1 = __importDefault(require_pattern_validation());
	var background_scheduled_task_1 = __importDefault(require_background_scheduled_task());
	var path_1 = __importDefault(__require("path"));
	var url_1 = __require("url");
	var registry = new task_registry_1.TaskRegistry();
	function schedule(expression, func, options) {
		const task = createTask(expression, func, options);
		task.start();
		return task;
	}
	function createTask(expression, func, options) {
		let task;
		if (func instanceof Function) task = new inline_scheduled_task_1.InlineScheduledTask(expression, func, options);
		else {
			const taskPath = solvePath(func);
			task = new background_scheduled_task_1.default(expression, taskPath, options);
		}
		registry.add(task);
		return task;
	}
	function solvePath(filePath) {
		if (path_1.default.isAbsolute(filePath)) return (0, url_1.pathToFileURL)(filePath).href;
		if (filePath.startsWith("file://")) return filePath;
		const stackLines = (/* @__PURE__ */ new Error()).stack?.split("\n");
		if (stackLines) {
			stackLines?.shift();
			const match = (stackLines?.find((line) => {
				return line.indexOf(new URL(import.meta.url).pathname) === -1;
			}))?.match(/(file:\/\/)?(((\/?)(\w:))?([/\\].+)):\d+:\d+/);
			if (match) {
				const dir = `${match[5] ?? ""}${path_1.default.dirname(match[6])}`;
				return (0, url_1.pathToFileURL)(path_1.default.resolve(dir, filePath)).href;
			}
		}
		throw new Error(`Could not locate task file ${filePath}`);
	}
	function validate(expression) {
		try {
			(0, pattern_validation_1.default)(expression);
			return true;
		} catch (e) {
			return false;
		}
	}
	exports$1.getTasks = registry.all;
	exports$1.getTask = registry.get;
	exports$1.nodeCron = {
		schedule,
		createTask,
		validate,
		getTasks: exports$1.getTasks,
		getTask: exports$1.getTask
	};
	exports$1.default = exports$1.nodeCron;
}));
//#endregion
//#region ../core/dist/llm/onboarding/login-engine.js
/**
* `runOnboardingLogin` — the 3-step onboarding login engine (T11724 · M3).
*
* The imperative front-door flow that takes a provider from a cold start to a
* usable, validated profile binding. It ORCHESTRATES the already-merged
* credential / catalog / config / resolver accessors — it does NOT reimplement
* OAuth, construct a transport, or open an LLM client (Gate-13: chokepoint only).
*
* ## The four steps
*
*   1. **connect**  — resolve the provider (alias → canonical via
*      {@link getProviderProfile}) and land a credential (Account) in the
*      multi-credential pool via {@link addCredential}. The OAuth token / API
*      key is supplied by the caller (`opts.token`): the owner-driven browser
*      step (PKCE / device-code) runs in `cleo llm login` BEFORE this engine,
*      so the engine itself is non-interactive and testable. OAuth-less
*      providers pass the same `opts.token` as an API key (AC4).
*   2. **select**   — choose a model from the catalog: `opts.model` when given
*      (validated against the catalog), else the latest by `release_date` via
*      {@link resolveProviderDefaultModel}.
*   3. **bind**     — write the `llm.default` (or `llm.roles[role]`) config
*      binding tying the account + model together via {@link setConfigValue}.
*   4. **validate** — round-trip the binding through {@link resolveLLMForSystem}
*      and confirm the resolved provider/model are consistent. NOT a live LLM
*      call: no token is materialized; only resolution metadata is asserted,
*      and the secret stays behind the sealed handle.
*
* ## Security (AC2)
*
* The {@link OnboardingResult} envelope carries NO secret fields — never an
* access token, API key, or refresh token. The plaintext is materialized only
* at the wire via the sealed handle, never on this envelope.
*
* ## Testability (AC3)
*
* Every step depends only on the injectable {@link OnboardingDeps} seam, which
* defaults to the real core accessors. Tests pass a {@link StubOnboardingDeps}
* (stub catalog + in-memory pool/config + stub resolver) to exercise each step
* in isolation without a live browser, network, or disk.
*
* @module llm/onboarding/login-engine
* @task T11724
* @epic T11671 (E6-ONBOARDING-FRONT-DOOR)
*/
var logger$1 = getLogger("llm-onboarding");
/**
* The default dependency set — the real, merged core accessors.
*
* @task T11724
*/
function defaultDeps() {
	return {
		getProviderProfile,
		addCredential: (input) => addCredential(input),
		catalogKeyForProvider,
		resolveProviderDefaultModel: (catalogKey) => resolveProviderDefaultModel(catalogKey),
		validateModelForProvider: (model, catalogKey) => validateModelForProvider(model, catalogKey),
		setConfigValue: (key, value) => setConfigValue(key, value, void 0, { global: true }),
		resolve: async (_provider, projectRoot, role) => {
			const resolved = await resolveLLMForSystem({
				kind: "role",
				id: role ?? "consolidation"
			}, projectRoot !== void 0 ? { projectRoot } : void 0);
			return {
				provider: resolved.provider,
				model: resolved.model,
				sealedCredential: resolved.sealedCredential ? { tokenPreview: resolved.sealedCredential.tokenPreview } : null
			};
		}
	};
}
/** Build an `ok` step trace. @internal */
function ok(step, detail) {
	return {
		step,
		status: "ok",
		detail
	};
}
/** Build a `failed` step trace carrying a stable `E_*` code. @internal */
function fail(step, code, detail) {
	return {
		step,
		status: "failed",
		detail,
		code
	};
}
/** Build a `skipped` step trace (downstream of a failure). @internal */
function skipped(step, detail) {
	return {
		step,
		status: "skipped",
		detail
	};
}
/**
* Compose the partial envelope returned when a step fails: the failed step's
* trace plus a `skipped` trace for every remaining step, so the 4-row shape is
* always stable.
*
* @internal
*/
function withSkips(done, failedAt) {
	const order = [
		"connect",
		"select",
		"bind",
		"validate"
	];
	const steps = [...done, failedAt];
	const seen = new Set(steps.map((s) => s.step));
	for (const name of order) if (!seen.has(name)) steps.push(skipped(name, "skipped — earlier step failed"));
	return order.map((name) => steps.find((s) => s.step === name));
}
/**
* Run the 3-step onboarding login flow for a provider (T11724 · M3).
*
* Walks **connect → select → bind → validate**, reusing the merged credential /
* catalog / config / resolver accessors. Returns a typed {@link OnboardingResult}
* envelope with the per-step trace and the non-secret binding identifiers.
*
* Like the resolver chokepoint, this engine **never throws** for an expected
* failure (missing credential, unknown model, config write error): it returns
* an envelope whose failing step carries `status: 'failed'` and a stable `E_*`
* `code`, with `validated: false`. Unexpected accessor errors are caught and
* mapped to a failed step trace as well.
*
* @param provider - Provider name or alias (e.g. `'anthropic'`, `'codex'`).
* @param opts     - {@link OnboardingLoginOptions} (token, model, role, …).
* @param deps     - Injectable accessor seam (AC3); defaults to real core accessors.
* @returns The typed onboarding result envelope; never throws on expected failure.
*
* @task T11724
*/
async function runOnboardingLogin(provider, opts = {}, deps = defaultDeps()) {
	const label = opts.label?.trim() ? opts.label.trim() : "oauth-login";
	const done = [];
	let canonicalProvider;
	let authMode;
	try {
		const profile = await deps.getProviderProfile(provider);
		if (!profile) return envelope(withSkips(done, fail("connect", "E_UNKNOWN_PROVIDER", `Unknown provider '${provider}'. Run \`cleo llm list-providers\` to see supported providers.`)), provider, label, null, null, null, false);
		canonicalProvider = profile.name;
		authMode = opts.authMode ?? (profile.oauth ? "oauth" : "api_key");
		if (opts.credentialAlreadyStored) done.push(ok("connect", `account '${label}' connected (${authMode}, pre-stored)`));
		else {
			if (!opts.token) return envelope(withSkips(done, fail("connect", "E_ONBOARDING_NO_CREDENTIAL", `No credential supplied for '${canonicalProvider}'. Complete \`cleo llm login ${canonicalProvider}\` (OAuth) or pass an API key.`)), canonicalProvider, label, null, null, null, false);
			const storedAuthType = authMode === "oauth" ? "oauth" : "api_key";
			await deps.addCredential({
				provider: canonicalProvider,
				label,
				authType: storedAuthType,
				accessToken: opts.token,
				source: "onboarding-login"
			});
			done.push(ok("connect", `account '${label}' connected (${authMode})`));
		}
	} catch (err) {
		return envelope(withSkips(done, fail("connect", "E_ONBOARDING_CONNECT_FAILED", errMsg(err))), provider, label, null, null, null, false);
	}
	let modelId;
	try {
		const catalogKey = deps.catalogKeyForProvider(canonicalProvider);
		if (opts.model) {
			const validation = deps.validateModelForProvider(opts.model, catalogKey);
			if (!validation.valid && validation.reason === "not-found") return envelope(withSkips(done, fail("select", "E_MODEL_NOT_IN_CATALOG", `Model '${opts.model}' is not in the catalog for provider '${canonicalProvider}'. Run \`cleo llm refresh-catalog\` to update the catalog.`)), canonicalProvider, label, authMode, null, null, false);
			modelId = opts.model;
			done.push(ok("select", `model '${modelId}' selected (explicit, validated)`));
		} else {
			const latest = deps.resolveProviderDefaultModel(catalogKey);
			if (!latest) return envelope(withSkips(done, fail("select", "E_NO_CATALOG_MODEL", `No catalog model available for '${canonicalProvider}'. Run \`cleo llm refresh-catalog\`, or pass an explicit \`--model\`.`)), canonicalProvider, label, authMode, null, null, false);
			modelId = latest;
			done.push(ok("select", `model '${modelId}' selected (catalog default, latest release_date)`));
		}
	} catch (err) {
		return envelope(withSkips(done, fail("select", "E_ONBOARDING_SELECT_FAILED", errMsg(err))), canonicalProvider, label, authMode, null, null, false);
	}
	let profileName;
	try {
		if (opts.role) {
			if (!WHOAMI_ROLE_IDS.includes(opts.role)) return envelope(withSkips(done, fail("bind", "E_INVALID_ROLE", `Invalid role '${opts.role}'. Valid roles: ${WHOAMI_ROLE_IDS.join(", ")}.`)), canonicalProvider, label, authMode, modelId, null, false);
			await deps.setConfigValue(`llm.roles.${opts.role}.provider`, canonicalProvider);
			await deps.setConfigValue(`llm.roles.${opts.role}.model`, modelId);
			await deps.setConfigValue(`llm.roles.${opts.role}.credentialLabel`, label);
			profileName = opts.role;
			done.push(ok("bind", `profile '${profileName}' bound → ${canonicalProvider}/${modelId}`));
		} else {
			await deps.setConfigValue("llm.default.provider", canonicalProvider);
			await deps.setConfigValue("llm.default.model", modelId);
			profileName = "default";
			done.push(ok("bind", `default binding → ${canonicalProvider}/${modelId}`));
		}
	} catch (err) {
		return envelope(withSkips(done, fail("bind", "E_ONBOARDING_BIND_FAILED", errMsg(err))), canonicalProvider, label, authMode, modelId, null, false);
	}
	try {
		const resolved = await deps.resolve(canonicalProvider, opts.projectRoot, opts.role);
		const providerMatch = resolved.provider === canonicalProvider;
		const modelMatch = resolved.model === modelId;
		const hasHandle = resolved.sealedCredential !== null;
		if (providerMatch && modelMatch && hasHandle) {
			done.push(ok("validate", `round-trip ok — resolves to ${resolved.provider}/${resolved.model} (sealed handle ${resolved.sealedCredential?.tokenPreview ?? "…"})`));
			return envelope(done, canonicalProvider, label, authMode, modelId, profileName, true);
		}
		const step = fail("validate", "E_ONBOARDING_VALIDATION_FAILED", `binding inconsistent — ${!hasHandle ? "no credential handle resolved" : `resolved ${resolved.provider}/${resolved.model}, expected ${canonicalProvider}/${modelId}`}`);
		return envelope([...done, step], canonicalProvider, label, authMode, modelId, profileName, false);
	} catch (err) {
		const step = fail("validate", "E_ONBOARDING_VALIDATION_FAILED", errMsg(err));
		return envelope([...done, step], canonicalProvider, label, authMode, modelId, profileName, false);
	}
}
/**
* Assemble the {@link OnboardingResult} envelope. Centralised so every return
* path produces an identically-shaped, secret-free result.
*
* @internal
*/
function envelope(steps, provider, accountLabel, authMode, modelId, profileName, validated) {
	return {
		steps,
		provider,
		accountLabel,
		authMode,
		modelId,
		profileName,
		validated
	};
}
/**
* Extract a short, secret-free message from an unknown thrown value. The
* onboarding accessors already redact their own error strings; this is a final
* defensive narrow so the envelope never carries a raw stack.
*
* @internal
*/
function errMsg(err) {
	const raw = err instanceof Error ? err.message : String(err);
	logger$1.debug({ err: raw }, "onboarding: step accessor error");
	return raw;
}
//#endregion
//#region ../core/dist/llm/onboarding/front-door.js
/**
* `runFrontDoorLogin` — the shared onboarding front-door orchestrator (T11725 · M3).
*
* This is the ONE function every front-door entry point (`cleo login`,
* `cleo auth login`, `cleo llm login`) dispatches to. It owns the small amount
* of orchestration that sits ABOVE the 3-step onboarding engine
* ({@link runOnboardingLogin}):
*
*   1. **resolve provider** — alias → canonical via {@link getProviderProfile}.
*   2. **resolve auth method** — caller-supplied, else inferred from the
*      provider profile (OAuth when the profile advertises an `oauth` config,
*      else `api_key`).
*   3. **acquire the credential secret**:
*        - `api_key` → the caller passes the raw key in {@link FrontDoorLoginOptions.token}.
*        - `oauth`   → the caller's injected {@link OAuthTokenAcquirer} runs the
*          interactive browser / device-code dance and RETURNS the token. The
*          interactive step lives in the CLI (so this module stays
*          non-interactive and unit-testable); the acquirer is a seam, not a hard
*          dependency.
*   4. **run the engine** — hand the acquired token to {@link runOnboardingLogin},
*      which performs connect → select → bind → validate and returns the typed,
*      secret-free {@link OnboardingResult} envelope.
*
* ## Why a core orchestrator (and not CLI glue)
*
* Keeping this in `@cleocode/core` means the CLI handlers for all three entry
* points are THIN — they parse flags, build a {@link ReadlineWizardIO}, supply
* the OAuth acquirer, and call THIS function. No entry point re-implements the
* picker, the auth-method inference, or the engine call (AC2: no duplicated
* handler logic). Studio / the future API surface reuse the same orchestrator.
*
* ## Security
*
* The returned {@link OnboardingResult} carries NO secret fields (inherited from
* the engine contract). The raw token flows only from the acquirer into the
* engine's connect step; it is never logged or surfaced on the envelope.
*
* @module llm/onboarding/front-door
* @task T11725
* @epic T11671 (E6-ONBOARDING-FRONT-DOOR)
*/
/**
* The default front-door dependency set — the real, merged core accessors.
*
* @internal
*/
function defaultFrontDoorDeps() {
	return {
		getProviderProfile,
		runOnboardingLogin: (provider, opts, deps) => runOnboardingLogin(provider, opts, deps)
	};
}
/**
* Resolve the auth method for a provider profile: caller override wins, else
* `oauth` when the profile advertises an OAuth config, else `api_key`.
*
* @param profile - The resolved provider profile.
* @param override - Optional explicit auth mode from the caller.
* @returns The effective {@link OnboardingAuthMode}.
* @task T11725
*/
function resolveAuthMode(profile, override) {
	if (override) return override;
	return profile.oauth ? "oauth" : "api_key";
}
/**
* Run the shared onboarding front-door flow for a provider (T11725 · M3).
*
* Resolves the provider + auth method, acquires the credential secret (raw API
* key from `opts.token`, or via the injected {@link OAuthTokenAcquirer} for the
* OAuth path), then delegates the heavy lifting to {@link runOnboardingLogin}.
*
* Mirrors the engine's never-throw-on-expected-failure contract: an unknown
* provider or a missing api-key secret returns a failed {@link OnboardingResult}
* envelope rather than throwing. The OAuth acquirer's own errors DO propagate —
* an aborted browser flow is a hard, caller-visible failure.
*
* @param provider     - Provider name or alias (e.g. `'anthropic'`, `'codex'`).
* @param opts         - {@link FrontDoorLoginOptions}.
* @param acquireOAuth - Interactive OAuth token acquirer (the CLI's browser flow).
*   Only invoked on the `oauth` path; may be omitted when the flow is `api_key`.
* @param deps         - Injectable accessor seam; defaults to real core accessors.
* @returns The typed onboarding result envelope.
* @task T11725
*/
async function runFrontDoorLogin(provider, opts = {}, acquireOAuth, deps = defaultFrontDoorDeps()) {
	const profile = await deps.getProviderProfile(provider);
	if (!profile) return deps.runOnboardingLogin(provider, buildEngineOptions(opts, opts.token, false));
	const canonicalProvider = profile.name;
	const authMode = resolveAuthMode(profile, opts.authMode);
	let token = opts.token;
	let credentialAlreadyStored = false;
	let label = opts.label;
	if (authMode === "oauth") {
		if (!acquireOAuth) throw new Error(`OAuth login for '${canonicalProvider}' requires an interactive token acquirer. This entry point cannot complete an OAuth flow non-interactively — supply an API key with --api-key/--token, or run on a terminal.`);
		const acquired = await acquireOAuth(canonicalProvider);
		credentialAlreadyStored = true;
		label = label ?? acquired.label;
		token = void 0;
	}
	return deps.runOnboardingLogin(canonicalProvider, buildEngineOptions({
		...opts,
		authMode,
		label
	}, token, credentialAlreadyStored));
}
/**
* Map the front-door options + resolved token to the engine's option shape.
*
* Centralised so the unknown-provider deferral path and the happy path build
* identical engine options.
*
* @internal
*/
function buildEngineOptions(opts, token, credentialAlreadyStored) {
	const engineOpts = {};
	if (token !== void 0) engineOpts.token = token;
	if (opts.authMode !== void 0) engineOpts.authMode = opts.authMode;
	if (opts.label !== void 0) engineOpts.label = opts.label;
	if (opts.model !== void 0) engineOpts.model = opts.model;
	if (opts.role !== void 0) engineOpts.role = opts.role;
	if (opts.projectRoot !== void 0) engineOpts.projectRoot = opts.projectRoot;
	if (credentialAlreadyStored) engineOpts.credentialAlreadyStored = true;
	return engineOpts;
}
require_node_cron();
//#endregion
//#region ../core/dist/sentient/daemon.js
/** Relative subpath under a project root where sentient state lives. */
var SENTIENT_STATE_FILE = ".cleo/sentient-state.json";
process.env["CLEO_HYGIENE_CRON"];

//#region ../core/dist/llm/local-model-fit.js
/**
* Local-model fit ranking for Ollama-backed open-weight models (T11982).
*
* ## Purpose
*
* Given the current machine's hardware (RAM, VRAM), rank 2–3 curated
* open-weight models that are actually likely to run well. This is the
* `cleo llm fit` wizard building block — it powers T11983's one-keypress
* pull+connect flow.
*
* ## Vendor-vs-inspire decision (llmfit)
*
* Evaluated `github.com/AlexsJones/llmfit` (MIT, Go, ~400 LOC). The library
* is a standalone Go binary — not a TS/ESM package. Its key contribution is
* the IDEA: use RAM thresholds to gate model selection, prefer VRAM when
* available, and produce a ranked list with human-readable reasons. We
* **vendor the IDEA** (TS-native re-implementation) rather than shelling out
* to a Go binary or adding a cross-language dependency. The model thresholds
* below are independently calibrated from Ollama's documentation and the
* Hugging Face model cards, not copy-pasted from llmfit.
*
* ## Gate-13 compliance
*
* This module:
* - MUST NOT construct any transport or SDK client.
* - MUST NOT read `process.env.*_API_KEY` directly.
* - MUST NOT define a new `resolveLLMFor*` function.
* - MUST NOT hardcode model-id literals in logic — all model IDs come from
*   the {@link LOCAL_MODEL_CANDIDATES} data table.
*
* The hardware detection (`os.totalmem`, `os.freemem`, `/proc/meminfo`,
* `nvidia-smi`) is plain system inspection — not covered by Gate-13 (which
* governs LLM resolution / transport construction).
*
* Ollama liveness re-uses {@link probeOllamaAlive} from
* `cross-provider-selector.ts` (no duplication). The `/api/tags` fetch is
* a vanilla HTTP call to localhost, not a transport construction.
*
* @module llm/local-model-fit
* @task T11982
* @epic T11671
*/
var execFileAsync = promisify(execFile);
var logger = getLogger("llm-local-model-fit");
/**
* Curated candidate table. These are the ONLY models `rankLocalModelFit`
* can recommend — expand this table to add new models.
*
* Exclusions:
* - `qwen2:0.5b` — proof-of-life only; deliberately absent from this list.
*
* @task T11982
*/
var LOCAL_MODEL_CANDIDATES = [
	{
		modelTag: "gemma4:e2b",
		displayName: "Gemma 4 E2B (edge)",
		family: "gemma4",
		minRamGb: 4,
		recommendedRamGb: 6,
		minVramGb: 3,
		recommendedVramGb: 5,
		diskSizeGb: 7.2,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 128
	},
	{
		modelTag: "gemma4:e4b",
		displayName: "Gemma 4 E4B (edge)",
		family: "gemma4",
		minRamGb: 6,
		recommendedRamGb: 8,
		minVramGb: 6,
		recommendedVramGb: 8,
		diskSizeGb: 9.6,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 128
	},
	{
		modelTag: "gemma4:12b",
		displayName: "Gemma 4 12B",
		family: "gemma4",
		minRamGb: 10,
		recommendedRamGb: 16,
		minVramGb: 8,
		recommendedVramGb: 12,
		diskSizeGb: 7.6,
		quantNote: "QAT (default pull)",
		codeSpecialist: false,
		contextLengthK: 256
	},
	{
		modelTag: "qwen3:1.7b",
		displayName: "Qwen 3 1.7B",
		family: "qwen3",
		minRamGb: 3,
		recommendedRamGb: 4,
		minVramGb: 2,
		recommendedVramGb: 3,
		diskSizeGb: 1.4,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 32
	},
	{
		modelTag: "qwen3:4b",
		displayName: "Qwen 3 4B",
		family: "qwen3",
		minRamGb: 6,
		recommendedRamGb: 8,
		minVramGb: 4,
		recommendedVramGb: 6,
		diskSizeGb: 2.6,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 32
	},
	{
		modelTag: "qwen3:8b",
		displayName: "Qwen 3 8B",
		family: "qwen3",
		minRamGb: 8,
		recommendedRamGb: 12,
		minVramGb: 7,
		recommendedVramGb: 10,
		diskSizeGb: 5.2,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 32
	},
	{
		modelTag: "llama3.2:3b",
		displayName: "Llama 3.2 3B",
		family: "llama3.2",
		minRamGb: 4,
		recommendedRamGb: 6,
		minVramGb: 3,
		recommendedVramGb: 4,
		diskSizeGb: 2,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 128
	},
	{
		modelTag: "qwen2.5-coder:3b",
		displayName: "Qwen 2.5 Coder 3B",
		family: "qwen2.5-coder",
		minRamGb: 4,
		recommendedRamGb: 6,
		minVramGb: 3,
		recommendedVramGb: 4,
		diskSizeGb: 1.9,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: true,
		contextLengthK: 128
	},
	{
		modelTag: "phi4-mini:3.8b",
		displayName: "Phi 4 Mini 3.8B",
		family: "phi4",
		minRamGb: 4,
		recommendedRamGb: 6,
		minVramGb: 3,
		recommendedVramGb: 4,
		diskSizeGb: 2.5,
		quantNote: "Q4_K_M (default pull)",
		codeSpecialist: false,
		contextLengthK: 128
	}
];
/**
* Read `MemAvailable` from `/proc/meminfo` on Linux.
*
* Returns `null` on non-Linux or if the file is unreadable.
*
* @internal
*/
async function readLinuxMemAvailableBytes() {
	try {
		const content = await readFile("/proc/meminfo", "utf-8");
		for (const line of content.split("\n")) if (line.startsWith("MemAvailable:")) {
			const match = /MemAvailable:\s+(\d+)\s+kB/.exec(line);
			if (match?.[1]) return parseInt(match[1], 10) * 1024;
		}
		return null;
	} catch {
		return null;
	}
}
/**
* Detect NVIDIA VRAM via `nvidia-smi`.
*
* Returns `null` fields if the binary is absent or errors.
*
* @internal
*/
async function detectNvidiaVram() {
	try {
		const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=memory.total,memory.free", "--format=csv,noheader,nounits"]);
		const parts = stdout.trim().split(",");
		if (parts.length >= 2) {
			const totalMib = parseInt(parts[0].trim(), 10);
			const freeMib = parseInt(parts[1].trim(), 10);
			if (!Number.isNaN(totalMib) && !Number.isNaN(freeMib)) return {
				totalBytes: totalMib * 1024 * 1024,
				freeBytes: freeMib * 1024 * 1024,
				method: "nvidia-smi"
			};
		}
		return null;
	} catch {
		return null;
	}
}
/**
* Detect AMD VRAM via `rocm-smi`.
*
* Returns `null` fields if the binary is absent or errors.
*
* @internal
*/
async function detectRocmVram() {
	try {
		const { stdout } = await execFileAsync("rocm-smi", [
			"--showmeminfo",
			"vram",
			"--csv"
		]);
		const totalMatch = /Total\s+VRAM.*?(\d+)/i.exec(stdout);
		const usedMatch = /Used\s+VRAM.*?(\d+)/i.exec(stdout);
		if (totalMatch?.[1]) {
			const totalBytes = parseInt(totalMatch[1], 10) * 1024 * 1024;
			const usedBytes = usedMatch?.[1] ? parseInt(usedMatch[1], 10) * 1024 * 1024 : null;
			return {
				totalBytes,
				freeBytes: usedBytes !== null ? totalBytes - usedBytes : null,
				method: "rocm-smi"
			};
		}
		return null;
	} catch {
		return null;
	}
}
/**
* Capture a full hardware snapshot.
*
* Never throws — VRAM detection failures degrade gracefully to `null`.
*
* @param overrides - Override fields for testing (avoids OS calls in unit tests).
* @returns A hardware snapshot describing this machine's capabilities.
*
* @task T11982
*/
async function captureHardwareSnapshot(overrides) {
	const platform = overrides?.platform ?? process.platform;
	const totalRamBytes = overrides?.totalRamBytes ?? totalmem();
	let availableRamBytes;
	if (overrides?.availableRamBytes !== void 0) availableRamBytes = overrides.availableRamBytes;
	else if (platform === "linux") availableRamBytes = await readLinuxMemAvailableBytes() ?? freemem();
	else availableRamBytes = freemem();
	let vram;
	if (overrides?.vram !== void 0) vram = overrides.vram;
	else {
		const nvidia = await detectNvidiaVram();
		if (nvidia) vram = nvidia;
		else {
			const amd = await detectRocmVram();
			if (amd) vram = amd;
			else if (platform === "darwin") vram = {
				totalBytes: totalRamBytes,
				freeBytes: availableRamBytes,
				method: "apple-unified"
			};
			else vram = {
				totalBytes: null,
				freeBytes: null,
				method: "none"
			};
		}
	}
	return {
		totalRamBytes,
		availableRamBytes,
		vram,
		platform
	};
}
/**
* Parse raw `/api/tags` response from Ollama.
*
* @internal
*/
function parseOllamaTagsResponse(body) {
	if (typeof body !== "object" || body === null || !Array.isArray(body["models"])) return [];
	const models = body.models;
	const result = [];
	for (const m of models) {
		if (typeof m !== "object" || m === null) continue;
		const model = m;
		const name = typeof model["name"] === "string" ? model["name"] : "";
		const details = typeof model["details"] === "object" && model["details"] !== null ? model["details"] : {};
		const parameterSize = typeof details["parameter_size"] === "string" ? details["parameter_size"] : "";
		const quantizationLevel = typeof details["quantization_level"] === "string" ? details["quantization_level"] : "";
		const family = typeof details["family"] === "string" ? details["family"] : "";
		if (name) result.push({
			name,
			parameterSize,
			quantizationLevel,
			family
		});
	}
	return result;
}
/**
* Fetch the list of locally-pulled models from Ollama's `/api/tags` endpoint.
*
* Uses the HTTP API (not shelling out) — Gate-13 does not govern plain `fetch`
* calls to localhost.
*
* @param baseUrl - Ollama base URL (default `http://localhost:11434`).
* @param fetchFn - Injectable `fetch` for testing (defaults to global `fetch`).
* @returns Array of pulled models, or empty array if Ollama is not reachable.
*
* @task T11982
*/
async function listOllamaPulledModels(baseUrl = "http://localhost:11434", fetchFn = fetch) {
	try {
		const controller = new AbortController();
		const timeout = setTimeout(() => controller.abort(), 1e3);
		let response;
		try {
			response = await fetchFn(`${baseUrl}/api/tags`, { signal: controller.signal });
		} finally {
			clearTimeout(timeout);
		}
		if (!response.ok) return [];
		return parseOllamaTagsResponse(await response.json());
	} catch {
		return [];
	}
}
/** Score bonus when the model is already pulled locally. */
var ALREADY_PULLED_BONUS = 50;
/** Score bonus when VRAM is sufficient for full GPU offload. */
var VRAM_FULL_OFFLOAD_BONUS = 40;
/** Score bonus when VRAM meets the minimum (partial offload). */
var VRAM_PARTIAL_BONUS = 15;
/** Score bonus when available RAM exceeds the recommended threshold. */
var RAM_COMFORTABLE_BONUS = 20;
/** Score bonus when RAM is sufficient but below recommended. */
var RAM_SUFFICIENT_BONUS = 5;
/** Penalty when the model barely meets the minimum RAM. */
var RAM_TIGHT_PENALTY = -10;
/** Score bonus for models with longer context windows. */
var LONG_CONTEXT_BONUS = 8;
/**
* Compute the fit score for a candidate given this machine's hardware.
*
* @internal
*/
function scoreCandidate(candidate, snapshot, alreadyPulled) {
	const ramGb = snapshot.totalRamBytes / 1024 ** 3;
	const availableRamGb = snapshot.availableRamBytes / 1024 ** 3;
	const vramTotalGb = snapshot.vram.totalBytes !== null ? snapshot.vram.totalBytes / 1024 ** 3 : null;
	const vramFreeGb = snapshot.vram.freeBytes !== null ? snapshot.vram.freeBytes / 1024 ** 3 : null;
	let score = 0;
	const reasons = [];
	let fitTier = "marginal";
	if (vramTotalGb !== null && vramFreeGb !== null && candidate.minVramGb > 0) if (vramFreeGb >= candidate.recommendedVramGb) {
		score += VRAM_FULL_OFFLOAD_BONUS;
		reasons.push(`fits in ${vramFreeGb.toFixed(1)} GB free VRAM (recommended: ${candidate.recommendedVramGb} GB)`);
		fitTier = "excellent";
	} else if (vramFreeGb >= candidate.minVramGb) {
		score += VRAM_PARTIAL_BONUS;
		reasons.push(`partial GPU offload possible (${vramFreeGb.toFixed(1)} GB free VRAM, min: ${candidate.minVramGb} GB)`);
		fitTier = "good";
	} else reasons.push(`CPU inference only — VRAM ${vramFreeGb.toFixed(1)} GB below model minimum ${candidate.minVramGb} GB`);
	else if (candidate.minVramGb === 0) reasons.push("optimised for CPU inference");
	else if (snapshot.vram.method === "none") reasons.push("no GPU detected — will use CPU inference");
	if (availableRamGb >= candidate.recommendedRamGb) {
		score += RAM_COMFORTABLE_BONUS;
		reasons.push(`${availableRamGb.toFixed(1)} GB available RAM (recommended: ${candidate.recommendedRamGb} GB)`);
		if (fitTier !== "excellent") fitTier = "good";
	} else if (ramGb >= candidate.recommendedRamGb) {
		score += RAM_SUFFICIENT_BONUS;
		reasons.push(`${ramGb.toFixed(1)} GB total RAM meets recommendation (some swap may be used)`);
		if (fitTier !== "excellent") fitTier = "good";
	} else if (ramGb >= candidate.minRamGb) {
		score += RAM_TIGHT_PENALTY;
		reasons.push(`${ramGb.toFixed(1)} GB RAM meets minimum — expect slower inference (recommended: ${candidate.recommendedRamGb} GB)`);
	}
	if (candidate.contextLengthK >= 128) {
		score += LONG_CONTEXT_BONUS;
		reasons.push(`${candidate.contextLengthK}k context window`);
	}
	if (alreadyPulled) {
		score += ALREADY_PULLED_BONUS;
		reasons.push("already pulled — no download needed");
	}
	return {
		score,
		reasons,
		fitTier
	};
}
/**
* Rank the 2–3 best-fit local open-weight models for this machine.
*
* ## Floor
*
* Machines with total RAM below {@link LOCAL_FIT_FLOOR_GB} (4 GB) receive no
* recommendations. This deliberately excludes `qwen2:0.5b` (proof-of-life
* only) — it is absent from {@link LOCAL_MODEL_CANDIDATES}.
*
* ## Algorithm
*
*   1. Capture hardware snapshot.
*   2. Probe Ollama liveness and fetch the pulled-models list.
*   3. Filter candidates to those whose `minRamGb` ≤ total RAM.
*   4. Score remaining candidates (VRAM fit + RAM comfort + already-pulled bonus).
*   5. Return the top 3 (or fewer), ordered by score descending.
*
* @param opts - Override hardware snapshot for testing.
* @returns Fit envelope with ranked candidates.
*
* @task T11982
*/
async function rankLocalModelFit(opts) {
	const ollamaBaseUrl = opts?.ollamaBaseUrl ?? "http://localhost:11434";
	const [snapshot, ollamaRunning] = await Promise.all([captureHardwareSnapshot(opts?.hardwareOverride), probeOllamaAlive(ollamaBaseUrl)]);
	const pulledModels = opts?.pulledModelsOverride ?? (ollamaRunning ? await listOllamaPulledModels(ollamaBaseUrl, opts?.fetchFn) : []);
	const pulledTags = new Set(pulledModels.map((m) => m.name));
	const ramGb = snapshot.totalRamBytes / 1024 ** 3;
	const hardware = {
		totalRamGb: ramGb,
		availableRamGb: snapshot.availableRamBytes / 1024 ** 3,
		vramTotalGb: snapshot.vram.totalBytes !== null ? snapshot.vram.totalBytes / 1024 ** 3 : null,
		vramFreeGb: snapshot.vram.freeBytes !== null ? snapshot.vram.freeBytes / 1024 ** 3 : null,
		vramMethod: snapshot.vram.method
	};
	if (ramGb < 4) {
		logger.warn({
			ramGb: ramGb.toFixed(1),
			floorGb: 4
		}, "local-model-fit: RAM below floor — no local model recommended");
		return {
			hardware,
			ollamaRunning,
			pulledModels,
			recommendations: [],
			noRecommendationReason: `This machine has only ${ramGb.toFixed(1)} GB RAM. Local LLM inference requires at least 4 GB. Use a cloud provider instead.`
		};
	}
	const eligible = LOCAL_MODEL_CANDIDATES.filter((c) => ramGb >= c.minRamGb);
	if (eligible.length === 0) return {
		hardware,
		ollamaRunning,
		pulledModels,
		recommendations: [],
		noRecommendationReason: `No curated model fits the available RAM (${ramGb.toFixed(1)} GB).`
	};
	const scored = eligible.map((candidate) => {
		const alreadyPulled = pulledTags.has(candidate.modelTag);
		const { score, reasons, fitTier } = scoreCandidate(candidate, snapshot, alreadyPulled);
		return {
			candidate,
			score,
			alreadyPulled,
			reasons,
			fitTier,
			pullCommand: `ollama pull ${candidate.modelTag}`
		};
	});
	scored.sort((a, b) => {
		if (b.score !== a.score) return b.score - a.score;
		return a.candidate.modelTag.localeCompare(b.candidate.modelTag);
	});
	return {
		hardware,
		ollamaRunning,
		pulledModels,
		recommendations: scored.slice(0, 3),
		noRecommendationReason: null
	};
}

export { SENTIENT_STATE_FILE as S, rankLocalModelFit as a, runFrontDoorLogin as b, require_node_cron as c, resolveLLMForSystem as r };
//# sourceMappingURL=local-model-fit2-J1xCIhC_.js.map
