/** * run-routing.ts — where a skill run executes: on this machine, or on the * configured Skills API. * * One resolver serves every run surface (the CLI `skills run` path, the MCP * `run_skill` tool, and the `skills schedule run` surface) so they cannot drift * apart. * * A skill run is REMOTE only when all three hold: * * 1. a credential resolves on the fleet ladder (an argument, an env pointer, * the macOS Keychain, `~/.hasna/skills/config/credentials`, or * `$HASNA_SKILLS_API_KEY`) — see lib/fleet-credentials.ts; * 2. an authority follows from it (`$HASNA_SKILLS_API_URL`, the Keychain * `api-url` item, the credentials file, else the fleet gateway); * 3. the skill carries the server-owned marker from its published contract * (`skills.runtime: "hosted"` or `skills.source: "remote" | * "private-hosted"` in the skill's `package.json` — see * isHostedMetadataPackage in hosted-skill-set.ts). * * Otherwise the run is LOCAL, and local stays the default: an unconfigured * install never sends anything anywhere. * * A server-owned skill never falls back to local execution. Without the origin * or the credential the resolver fails closed with an error naming the exact * setup command, per the product brief: "Premium or server-executed skills * fail closed without API credentials and do not fall back to bundled local * execution." * * Premium-catalog and pricing metadata is server-side and never ships in this * package; the resolver concerns routing only. */ import type { SkillMeta } from "./registry-types.js"; export type RunRoutingErrorCode = "REMOTE_REQUIRES_ORIGIN" | "REMOTE_REQUIRES_CREDENTIAL"; export type RunRouting = { route: "remote"; apiKey: string; apiOrigin: string; } | { route: "local"; } | { route: "error"; code: RunRoutingErrorCode; error: string; }; /** * Is execution of this skill server-owned per its published contract? * The marker is the skill contract's declaration; absent means local * execution is the contract. */ export declare function isServerOwnedSkill(skill: Pick): boolean; /** * Decide the route for one skill run from injectable inputs, so tests can mock * the origin and the credential without touching the ambient environment. */ export declare function resolveRunRouting(skill: Pick, apiKey: string | null | undefined, apiUrl: string | undefined): RunRouting; /** * Resolve the route against the ambient configuration and credential store. * The returned `apiKey` on the remote route is the same value the resolver * validated, so the caller never re-reads (and cannot diverge from) the * credential the decision was made with. */ export declare function resolveConfiguredRunRouting(skill: Pick, env?: Record): Promise;