@lunora/config 1.0.0-alpha.11 → 1.0.0-alpha.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +2 -2
  3. package/__assets__/package-og.svg +1 -1
  4. package/dist/cloudflare/index.d.mts +661 -0
  5. package/dist/cloudflare/index.d.ts +661 -0
  6. package/dist/cloudflare/index.mjs +1 -0
  7. package/dist/index.d.mts +689 -824
  8. package/dist/index.d.ts +689 -824
  9. package/dist/index.mjs +1 -22
  10. package/dist/packem_shared/ACCENT-CLeV5v0K.mjs +7 -0
  11. package/dist/packem_shared/AGENT_MODE_ENV-B54hVQ_w.mjs +1 -0
  12. package/dist/packem_shared/AGENT_RULES_DIR-hP9TiDNx.mjs +1 -0
  13. package/dist/packem_shared/ALLOW_FORWARDED_ENV-DGdbCDre.mjs +1 -0
  14. package/dist/packem_shared/CLOUDFLARE_DRIVER-DS7cF7Cl.mjs +1 -0
  15. package/dist/packem_shared/DEFAULT_DEPLOY_TARGET-CBoD0Zha.mjs +1 -0
  16. package/dist/packem_shared/DEV_DAEMON_ENV-D9Z83rlU.mjs +4 -0
  17. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs +6 -0
  18. package/dist/packem_shared/LINKED_PROJECT_DIR-CMzUvj-1.mjs +2 -0
  19. package/dist/packem_shared/LUNORA_CONFIG_FILE-DoVUD52W.mjs +1 -0
  20. package/dist/packem_shared/LUNORA_EVENT_SOURCE-ZclWASXS.mjs +1 -0
  21. package/dist/packem_shared/LunoraReporter-BnXUqh8t.mjs +6 -0
  22. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-BgmvEPA-.mjs +1 -0
  23. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-CUYWD0Mh.mjs +1 -0
  24. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-ws6iE0y-.mjs +1 -0
  25. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-BwmyesjD.mjs +1 -0
  26. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-BNB1ogaj.mjs +1 -0
  27. package/dist/packem_shared/SEED_ENDPOINT-BaYShU8q.mjs +1 -0
  28. package/dist/packem_shared/WORKERS_CACHE_MIN_DATE-B1h_wNDN.mjs +1 -0
  29. package/dist/packem_shared/WRANGLER_FILES-Bi_18Pj6.mjs +1 -0
  30. package/dist/packem_shared/applyAdditiveEdit-Cff30cSa.mjs +6 -0
  31. package/dist/packem_shared/assetContentType-DNZyUOuF.mjs +1 -0
  32. package/dist/packem_shared/buildPackageSecretsBlock-DlAPMSwt.mjs +18 -0
  33. package/dist/packem_shared/classifyPolicyEdit-BzC_MfYK.mjs +24 -0
  34. package/dist/packem_shared/collectWranglerSecretVariables-jPUxMC9j.mjs +1 -0
  35. package/dist/packem_shared/createConfirm-7IL0kZyE.mjs +7 -0
  36. package/dist/packem_shared/detectFramework-VTQfCNXy.mjs +1 -0
  37. package/dist/packem_shared/discoverAgentInfo-BJm0QtoI.mjs +1 -0
  38. package/dist/packem_shared/discoverContainerInfo-CYG9j2LY.mjs +1 -0
  39. package/dist/packem_shared/discoverSchemaInfo-C8X9mo-i.mjs +1 -0
  40. package/dist/packem_shared/discoverWorkflowInfo-Bbc0u2gE.mjs +1 -0
  41. package/dist/packem_shared/inferLunoraBindings-tf2nExl_.mjs +1 -0
  42. package/dist/packem_shared/jsonc-edit-BZVpxVA0.mjs +1 -0
  43. package/dist/packem_shared/parseDevVariable-B193_28t.mjs +1 -0
  44. package/dist/packem_shared/parseSchema-BQjgz6bk.mjs +1 -0
  45. package/dist/packem_shared/{policy-scaffold.d-DCmwn7zQ.d.mts → policy-scaffold.d-CFd2FlqG.d.mts} +22 -22
  46. package/dist/packem_shared/{policy-scaffold.d-DCmwn7zQ.d.ts → policy-scaffold.d-CFd2FlqG.d.ts} +22 -22
  47. package/dist/packem_shared/reconcileWranglerBindings-BCwTpuLO.mjs +1 -0
  48. package/dist/packem_shared/reconcileWranglerCompatibilityDate-BXNiEQ7p.mjs +1 -0
  49. package/dist/packem_shared/reconcileWranglerCrons-BmQa_kGL.mjs +1 -0
  50. package/dist/packem_shared/renderStudioHtml-B2AkxcW1.mjs +15 -0
  51. package/dist/packem_shared/schema-info.d-DS0bUsWE.d.mts +375 -0
  52. package/dist/packem_shared/schema-info.d-DS0bUsWE.d.ts +375 -0
  53. package/dist/packem_shared/serveJsonHandler-DQ-Yqa4J.mjs +1 -0
  54. package/dist/packem_shared/streamContainerLogs-BPYBNrWS.mjs +2 -0
  55. package/dist/packem_shared/wranglerToAlchemy-Fh5KfTPB.mjs +6 -0
  56. package/dist/packem_shared/write-atomic-Cdr2oQb-.mjs +1 -0
  57. package/dist/studio-host/index.d.mts +166 -92
  58. package/dist/studio-host/index.d.ts +166 -92
  59. package/dist/studio-host/index.mjs +1 -7
  60. package/package.json +15 -8
  61. package/dist/packem_shared/ACCENT-DW1XJn8i.mjs +0 -40
  62. package/dist/packem_shared/AGENT_RULES_DIR-lcgC08aE.mjs +0 -40
  63. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs +0 -37
  64. package/dist/packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs +0 -52
  65. package/dist/packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs +0 -34
  66. package/dist/packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs +0 -86
  67. package/dist/packem_shared/LunoraReporter-Ci-bDCK9.mjs +0 -70
  68. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs +0 -70
  69. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-CiC2IGKx.mjs +0 -103
  70. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs +0 -105
  71. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-DRSNSOOp.mjs +0 -476
  72. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs +0 -99
  73. package/dist/packem_shared/SEED_ENDPOINT-DVCjaGO-.mjs +0 -61
  74. package/dist/packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs +0 -25
  75. package/dist/packem_shared/applyAdditiveEdit-C-snTFEV.mjs +0 -228
  76. package/dist/packem_shared/buildPackageSecretsBlock-DNzNRu7T.mjs +0 -188
  77. package/dist/packem_shared/classifyPolicyEdit-BHeAqF8P.mjs +0 -99
  78. package/dist/packem_shared/createConfirm-fvpdgJ9s.mjs +0 -100
  79. package/dist/packem_shared/detectFramework-Br-BcPBq.mjs +0 -41
  80. package/dist/packem_shared/discoverContainerInfo-BXFs6Wav.mjs +0 -19
  81. package/dist/packem_shared/discoverSchemaInfo-BB-CKlTK.mjs +0 -25
  82. package/dist/packem_shared/discoverWorkflowInfo-CedvR0mn.mjs +0 -19
  83. package/dist/packem_shared/inferLunoraBindings-DIku9mTN.mjs +0 -302
  84. package/dist/packem_shared/loadStudioAssets-Csk5RS4E.mjs +0 -28
  85. package/dist/packem_shared/parseDevVariable-CJiq2IwE.mjs +0 -30
  86. package/dist/packem_shared/parseSchema-DSeyktvG.mjs +0 -107
  87. package/dist/packem_shared/reconcileWranglerBindings-DTHmqTbL.mjs +0 -277
  88. package/dist/packem_shared/renderStudioHtml-449Ysn75.mjs +0 -37
  89. package/dist/packem_shared/serveJsonHandler-B4OLTGLS.mjs +0 -86
package/dist/index.d.mts CHANGED
@@ -1,42 +1,103 @@
1
- import { ContainerIR, WorkflowIR } from '@lunora/codegen';
2
- export type { ContainerIR, WorkflowIR } from '@lunora/codegen';
1
+ import { AgentIR, ContainerIR, WorkflowIR } from '@lunora/codegen';
2
+ export type { AgentIR, ContainerIR, WorkflowIR } from '@lunora/codegen';
3
+ import { Writable } from 'node:stream';
4
+ import { D as DeployDriver } from "./packem_shared/schema-info.d-DS0bUsWE.mjs";
5
+ export { type a as DeployRequest, type b as DevRequest, type c as DiscoverSchemaInfoResult, type d as DriverContext, type e as DriverToolchain, type f as InferOptions, type g as InferredAgent, type I as InferredBindings, type h as InferredContainer, type i as InferredWorkflow, type N as NamedResource, type P as ProvisionResult, type R as ResourceGraph, type S as SchemaInfo, type j as SecretRequest, type k as ShardNamespaceResource, type T as TailRequest, type l as ToolchainCommand, m as discoverSchemaInfo, n as inferLunoraBindings, p as packageNamesFromBindings } from "./packem_shared/schema-info.d-DS0bUsWE.mjs";
3
6
  import 'ts-morph';
4
- export { type A as AdditivePolicyEdit, type D as DestructivePolicyEdit, type P as PolicyEdit, type a as PolicyScaffoldFailureReason, type b as ScaffoldFileResult, type S as ScaffoldPolicyEdit, type c as WireResult, type W as WireRlsEdit, d as classifyPolicyEdit, s as scaffoldPolicyFile, w as wireRlsIntoProcedure } from "./packem_shared/policy-scaffold.d-DCmwn7zQ.mjs";
7
+ export { type A as AdditivePolicyEdit, type D as DestructivePolicyEdit, type P as PolicyEdit, type a as PolicyScaffoldFailureReason, type b as ScaffoldFileResult, type S as ScaffoldPolicyEdit, type c as WireResult, type W as WireRlsEdit, d as classifyPolicyEdit, s as scaffoldPolicyFile, w as wireRlsIntoProcedure } from "./packem_shared/policy-scaffold.d-CFd2FlqG.mjs";
8
+ interface DiscoverAgentInfoResult {
9
+ /** Discovered agent definitions; `[]` when none are declared or parsing failed. */
10
+ agents: ReadonlyArray<AgentIR>;
11
+ /** Parse error message, when `lunora/agents.ts` exists but could not be analyzed. */
12
+ error?: string;
13
+ }
14
+ /**
15
+ * Discover the project's `defineAgent` declarations. Returns `{ agents: [] }`
16
+ * when the project has no `lunora/agents.ts` (not an error), or
17
+ * `{ agents: [], error }` when the file exists but could not be parsed — callers
18
+ * decide whether that is a warning (validator) or ignorable (inference).
19
+ */
20
+ declare const discoverAgentInfo: (projectRoot: string, schemaDirectory: string) => DiscoverAgentInfoResult;
21
+ interface DiscoverContainerInfoResult {
22
+ /** Discovered container definitions; `[]` when none are declared or parsing failed. */
23
+ containers: ReadonlyArray<ContainerIR>;
24
+ /** Parse error message, when `lunora/containers.ts` exists but could not be analyzed. */
25
+ error?: string;
26
+ }
27
+ /**
28
+ * Discover the project's `defineContainer` declarations. Returns
29
+ * `{ containers: [] }` when the project has no `lunora/containers.ts` (not an
30
+ * error), or `{ containers: [], error }` when the file exists but could not be
31
+ * parsed — callers decide whether that is a warning (validator) or ignorable
32
+ * (inference).
33
+ */
34
+ declare const discoverContainerInfo: (projectRoot: string, schemaDirectory: string) => DiscoverContainerInfoResult;
35
+ interface DiscoverWorkflowInfoResult {
36
+ /** Parse error message, when `lunora/workflows.ts` exists but could not be analyzed. */
37
+ error?: string;
38
+ /** Discovered workflow definitions; `[]` when none are declared or parsing failed. */
39
+ workflows: ReadonlyArray<WorkflowIR>;
40
+ }
5
41
  /**
6
- * Project-relative directory the Lunora agent skills ("rules") install into.
7
- * This is the portable [Agent Skills](https://tanstack.com/intent/latest/docs/registry)
8
- * location — Cursor, Claude Code, and GitHub Copilot all discover skills here.
9
- */
42
+ * Discover the project's `defineWorkflow` declarations. Returns
43
+ * `{ workflows: [] }` when the project has no `lunora/workflows.ts` (not an
44
+ * error), or `{ workflows: [], error }` when the file exists but could not be
45
+ * parsed — callers decide whether that is a warning (validator) or ignorable
46
+ * (inference).
47
+ */
48
+ declare const discoverWorkflowInfo: (projectRoot: string, schemaDirectory: string) => DiscoverWorkflowInfoResult;
49
+ /** Minimal env shape — `process.env` structurally, injectable for tests. */
50
+ type EnvLike = Readonly<Record<string, string | undefined>>;
51
+ /** One detected agent: which tool, and which env var gave it away. */
52
+ interface AgentDetection {
53
+ /** Human-readable agent name, for the "agent detected" log line. */
54
+ name: string;
55
+ /** The environment variable that matched. */
56
+ variable: string;
57
+ }
58
+ /** Env var that forces agent mode on (`1`/`true`) or off (`0`/`false`), overriding detection. */
59
+ declare const AGENT_MODE_ENV = "LUNORA_AGENT_MODE";
60
+ /**
61
+ * Detect the AI agent driving this process, or `undefined` when none is.
62
+ * `LUNORA_AGENT_MODE` wins over `@visulima/find-ai-runner`'s marker table in
63
+ * both directions. Pure — pass a custom `env` in tests.
64
+ */
65
+ declare const detectAiAgent: (env?: EnvLike) => AgentDetection | undefined;
66
+ /**
67
+ * Project-relative directory the Lunora agent skills ("rules") install into.
68
+ * This is the portable [Agent Skills](https://tanstack.com/intent/latest/docs/registry)
69
+ * location — Cursor, Claude Code, and GitHub Copilot all discover skills here.
70
+ */
10
71
  declare const AGENT_RULES_DIR = ".agents/skills";
11
72
  /** Env var the once-per-process-tree hint guard ({@link claimAgentRulesHint}) sets. */
12
73
  declare const AGENT_RULES_HINT_ENV = "LUNORA_RULES_HINT_SHOWN";
13
74
  /**
14
- * The Lunora agent skills shipped by `@lunora/cli`. The first entry (`lunora`)
15
- * is the router skill — its presence is what {@link detectAgentRules} treats as
16
- * "rules installed", since every other skill is reachable through it.
17
- */
75
+ * The Lunora agent skills shipped by `@lunora/cli`. The first entry (`lunora`)
76
+ * is the router skill — its presence is what {@link detectAgentRules} treats as
77
+ * "rules installed", since every other skill is reachable through it.
78
+ */
18
79
  declare const LUNORA_SKILL_NAMES: ReadonlyArray<string>;
19
80
  /** The router skill whose presence marks the rule set as installed. */
20
81
  declare const ROOT_SKILL_NAME = "lunora";
21
82
  /**
22
- * The single "rules not installed" message shared by every surface (the CLI
23
- * `lunora dev` summary and the Vite dev plugin), so the wording and the
24
- * pointer at `lunora rules install` stay identical wherever it appears.
25
- */
83
+ * The single "rules not installed" message shared by every surface (the CLI
84
+ * `lunora dev` summary and the Vite dev plugin), so the wording and the
85
+ * pointer at `lunora rules install` stay identical wherever it appears.
86
+ */
26
87
  declare const AGENT_RULES_HINT = "Lunora AI rules not installed — run `lunora rules install` so your coding agent knows how to use Lunora.";
27
88
  /**
28
- * Process-tree guard so the hint is emitted at most once. The first surface to
29
- * print it sets {@link AGENT_RULES_HINT_ENV} on `process.env`; later surfaces (a
30
- * Vite dev-server restart, or a child process that inherited the env) read it
31
- * and stay quiet. Returns `true` the first time, `false` afterwards.
32
- */
89
+ * Process-tree guard so the hint is emitted at most once. The first surface to
90
+ * print it sets {@link AGENT_RULES_HINT_ENV} on `process.env`; later surfaces (a
91
+ * Vite dev-server restart, or a child process that inherited the env) read it
92
+ * and stay quiet. Returns `true` the first time, `false` afterwards.
93
+ */
33
94
  declare const claimAgentRulesHint: () => boolean;
34
95
  interface AgentRulesStatus {
35
96
  /**
36
- * True when the `lunora` router skill is installed. We key on the router
37
- * (not "all nine present") so a project that intentionally trims the set
38
- * still counts as installed and isn't nagged.
39
- */
97
+ * True when the `lunora` router skill is installed. We key on the router
98
+ * (not "all nine present") so a project that intentionally trims the set
99
+ * still counts as installed and isn't nagged.
100
+ */
40
101
  readonly installed: boolean;
41
102
  /** Skill names with no `SKILL.md` under `&lt;root>/.agents/skills/&lt;name>/`. */
42
103
  readonly missing: ReadonlyArray<string>;
@@ -44,39 +105,96 @@ interface AgentRulesStatus {
44
105
  readonly present: ReadonlyArray<string>;
45
106
  }
46
107
  /**
47
- * Detect whether the Lunora agent skills are installed in `projectRoot` by
48
- * checking the skills folder for each `SKILL.md`. Pure filesystem reads, safe to
49
- * call on every dev-server / CLI startup — the CLI, the Vite plugin, and the
50
- * studio host all use it to decide whether to surface the "rules not installed"
51
- * hint.
52
- */
108
+ * Detect whether the Lunora agent skills are installed in `projectRoot` by
109
+ * checking the skills folder for each `SKILL.md`. Pure filesystem reads, safe to
110
+ * call on every dev-server / CLI startup — the CLI, the Vite plugin, and the
111
+ * studio host all use it to decide whether to surface the "rules not installed"
112
+ * hint.
113
+ */
53
114
  declare const detectAgentRules: (projectRoot: string) => AgentRulesStatus;
54
- interface DiscoverContainerInfoResult {
55
- /** Discovered container definitions; `[]` when none are declared or parsing failed. */
56
- containers: ReadonlyArray<ContainerIR>;
57
- /** Parse error message, when `lunora/containers.ts` exists but could not be analyzed. */
58
- error?: string;
115
+ /** Severity a container output line is surfaced at: `stderr` → `error`, `stdout` → `info`. */
116
+ type ContainerLogLevel = "error" | "info";
117
+ /** One declared container to follow, identified by the names codegen lifts from `defineContainer`. */
118
+ interface ContainerLogSource {
119
+ /** Generated Durable Object class name, e.g. `TranscoderContainer`. Wrangler's dev image is `cloudflare-dev/&lt;lowercased>`. */
120
+ className: string;
121
+ /** The `lunora/containers.ts` export name, e.g. `transcoder` — used as the display tag. */
122
+ exportName: string;
123
+ }
124
+ /** A single line of container output handed back to the caller. */
125
+ interface ContainerLogLine {
126
+ /** `"error"` for the container's stderr, `"info"` for its stdout. */
127
+ level: ContainerLogLevel;
128
+ /** The container's export name (`transcoder`), for tagging the line. */
129
+ name: string;
130
+ /** One output line, with the trailing newline (and any `\r`) stripped. */
131
+ text: string;
132
+ }
133
+ interface ContainerLogStreamOptions {
134
+ /** The declared containers to follow. An empty list yields an inert handle. */
135
+ containers: ReadonlyArray<ContainerLogSource>;
136
+ /** Injected Docker client — defaults to a real lazily-imported `dockerode` instance. Tests pass a stub. */
137
+ docker?: DockerLike;
138
+ /** Called once per container output line. */
139
+ onLine: (line: ContainerLogLine) => void;
140
+ /** Called once when the Docker engine can't be reached (re-armed after it recovers). Defaults to silent. */
141
+ onUnavailable?: (message: string) => void;
142
+ /** Poll interval override, in ms. */
143
+ pollIntervalMs?: number;
144
+ }
145
+ /** Handle controlling a running log stream. */
146
+ interface ContainerLogStreamHandle {
147
+ /** Stop polling and tear down every attached log stream. Idempotent. */
148
+ close: () => void;
149
+ }
150
+ /** The minimal structural slice of a `dockerode` log stream this module consumes. */
151
+ interface DockerLogStream {
152
+ destroy: () => void;
153
+ on: (event: "data" | "end" | "error", listener: (chunk?: Buffer) => void) => void;
154
+ }
155
+ /** The minimal structural slice of a `dockerode` instance this module consumes. */
156
+ interface DockerLike {
157
+ getContainer: (id: string) => {
158
+ logs: (options: {
159
+ follow: true;
160
+ stderr: true;
161
+ stdout: true;
162
+ tail: "all";
163
+ timestamps: false;
164
+ }) => Promise<DockerLogStream>;
165
+ };
166
+ listContainers: (options: {
167
+ filters: {
168
+ status: ["running"];
169
+ };
170
+ }) => Promise<{
171
+ Id: string;
172
+ Image: string;
173
+ }[]>;
174
+ modem: {
175
+ demuxStream: (stream: DockerLogStream, stdout: Writable, stderr: Writable) => void;
176
+ };
59
177
  }
60
178
  /**
61
- * Discover the project's `defineContainer` declarations. Returns
62
- * `{ containers: [] }` when the project has no `lunora/containers.ts` (not an
63
- * error), or `{ containers: [], error }` when the file exists but could not be
64
- * parsed — callers decide whether that is a warning (validator) or ignorable
65
- * (inference).
66
- */
67
- declare const discoverContainerInfo: (projectRoot: string, schemaDirectory: string) => DiscoverContainerInfoResult;
179
+ * Follow the local Docker logs of every declared container, emitting each output
180
+ * line through `onLine` tagged with its export name. Polls for containers (they
181
+ * start lazily on first request and may be replaced on restart), attaches once
182
+ * per container id, and drops streams whose container has gone. Returns
183
+ * immediately with a `close()` handle; all work happens asynchronously.
184
+ */
185
+ declare const streamContainerLogs: (options: ContainerLogStreamOptions) => ContainerLogStreamHandle;
68
186
  /**
69
- * The meta-frameworks Lunora can compose with, plus `"none"` for a standalone
70
- * SPA / SSR-less project (the current default). Mirrors PLAN4 §2.4.
71
- */
187
+ * The meta-frameworks Lunora can compose with, plus `"none"` for a standalone
188
+ * SPA / SSR-less project (the current default). Mirrors PLAN4 §2.4.
189
+ */
72
190
  type DetectedFramework = "astro" | "none" | "nuxt" | "react-router" | "solid-start" | "sveltekit" | "tanstack-start" | "tanstack-start-solid";
73
191
  /**
74
- * void's class model (PLAN4 §3). Class A is Vite-native and Lunora owns the
75
- * worker entry (`createWorker({ httpRouter })`). Class B frameworks own their own
76
- * Cloudflare adapter, so Lunora injects its worker composition into the
77
- * framework's server entry via hooks (PLAN4 M4). Class C is non-CF / SSR-less —
78
- * ship the client adapter + a standalone Lunora worker (today's default).
79
- */
192
+ * void's class model (PLAN4 §3). Class A is Vite-native and Lunora owns the
193
+ * worker entry (`createWorker({ httpRouter })`). Class B frameworks own their own
194
+ * Cloudflare adapter, so Lunora injects its worker composition into the
195
+ * framework's server entry via hooks (PLAN4 M4). Class C is non-CF / SSR-less —
196
+ * ship the client adapter + a standalone Lunora worker (today's default).
197
+ */
80
198
  type FrameworkClass = "A" | "B" | "C";
81
199
  interface FrameworkDetection {
82
200
  /** void's composition class for the detected framework. */
@@ -85,148 +203,201 @@ interface FrameworkDetection {
85
203
  framework: DetectedFramework;
86
204
  }
87
205
  /**
88
- * Detect which meta-framework a project uses by inspecting its `package.json`
89
- * dependencies, and classify it under void's class-A/B/C model (PLAN4 §3).
90
- *
91
- * Pure and best-effort: never throws. An unknown / missing / malformed
92
- * `package.json` yields `{ framework: "none", class: "C" }` so the standalone
93
- * SPA flow is preserved.
94
- */
206
+ * Read and parse the project `package.json`, returning its merged
207
+ * `dependencies` + `devDependencies` name set (empty on any failure). Public
208
+ * so sibling consumers (e.g. the CLI's Vite-project detection) share one
209
+ * best-effort reader instead of re-parsing `package.json` themselves.
210
+ */
211
+ declare const readProjectDependencyNames: (root: string) => ReadonlySet<string>;
212
+ /**
213
+ * Detect which meta-framework a project uses by inspecting its `package.json`
214
+ * dependencies, and classify it under void's class-A/B/C model (PLAN4 §3).
215
+ *
216
+ * Pure and best-effort: never throws. An unknown / missing / malformed
217
+ * `package.json` yields `{ framework: "none", class: "C" }` so the standalone
218
+ * SPA flow is preserved.
219
+ */
95
220
  declare const detectFramework: (root: string) => FrameworkDetection;
96
- /**
97
- * The `.dev.vars` line grammar — one owner, shared by every reader/writer of the
98
- * file so the format can't drift between packages. `@lunora/cli`'s `env`
99
- * command (parse/serialize) and `@lunora/config`'s scaffolder (comment-
100
- * preserving rewrite) do different *transforms*, but they agree on these
101
- * primitives: the filename, what a `KEY` looks like, how lines split, and how
102
- * quotes strip.
103
- */
221
+ /** Directory holding per-checkout Lunora state (gitignored by convention). */
222
+ declare const DEV_STATE_DIR = ".lunora";
223
+ /** The dev-server state filename, relative to the project root. */
224
+ declare const DEV_STATE_FILE: string;
225
+ /** Log file a backgrounded dev server's output is captured to, relative to the project root. */
226
+ declare const DEV_LOG_FILE: string;
227
+ /**
228
+ * Marker env `lunora dev --background` sets on the detached server process
229
+ * (the daemon `lunora dev` or `vite dev`), so it records itself as
230
+ * `background: true` — and, for the CLI daemon, never re-detects an agent and
231
+ * recurses into background mode again.
232
+ */
233
+ declare const DEV_DAEMON_ENV = "LUNORA_DEV_DAEMON";
234
+ /** Env carrying the capture-log path into the detached server, recorded in the state file. */
235
+ declare const DEV_LOG_FILE_ENV = "LUNORA_DEV_LOG_FILE";
236
+ /**
237
+ * Env carrying the PID of a parent that holds a *provisional* state record it
238
+ * expects the child dev server to supersede. The CLI claims `.lunora/dev.json`
239
+ * with its own PID before spawning the real server (closing the duplicate-start
240
+ * race for the vite flavor and the wrangler daemon), then hands its PID down
241
+ * via this variable; the child's claim (see {@link claimDevServerState}'s
242
+ * `supersedePid`) may replace exactly that record with the authoritative
243
+ * URL + PID.
244
+ */
245
+ declare const DEV_HANDOFF_ENV = "LUNORA_DEV_HANDOFF_PID";
246
+ /** How the recorded dev server runs. */
247
+ type DevServerMode = "cli" | "vite";
248
+ /** The state record persisted to `.lunora/dev.json`. */
249
+ interface DevServerState {
250
+ /** Whether the server was detached into the background (`lunora dev --background`). */
251
+ background?: boolean;
252
+ /** Absolute path of the log file capturing the server's output, when captured. */
253
+ logFile?: string;
254
+ /** `"cli"` for the `lunora dev` wrangler orchestration, `"vite"` for a Vite dev server. */
255
+ mode: DevServerMode;
256
+ /** PID of the process to signal for shutdown (the orchestrating CLI or the Vite process). */
257
+ pid: number;
258
+ /** ISO-8601 stamp written at startup, purely informational (drives `status` uptime). */
259
+ startedAt?: string;
260
+ /** The embedded studio server's URL, when it runs. */
261
+ studioUrl?: string;
262
+ /** The primary URL serving the worker/app. */
263
+ url: string;
264
+ }
265
+ /**
266
+ * True when a process with `pid` is currently alive AND signalable by this
267
+ * user. Signal `0` performs the existence check without delivering anything.
268
+ *
269
+ * `EPERM` ("alive but another user's process") deliberately counts as NOT
270
+ * alive here: every dev server this module tracks was spawned by the current
271
+ * user, so a PID we cannot signal is by definition a recycled PID — treating
272
+ * it as running would wedge the lockfile permanently and aim `dev stop`'s
273
+ * kill escalation at an innocent process.
274
+ */
275
+ declare const isProcessAlive: (pid: number) => boolean;
276
+ /**
277
+ * True when the record's PID verifiably still refers to the dev server that
278
+ * wrote the record — not merely "some process exists with that number".
279
+ * Guards against PID reuse: a server process necessarily starts BEFORE its
280
+ * record is written, so a process that started after `startedAt` (+ skew) is
281
+ * a recycled PID wearing the corpse's number. The start-time check runs only
282
+ * where the platform exposes it (Linux); elsewhere liveness alone decides.
283
+ */
284
+ declare const isRecordedProcessCurrent: (state: DevServerState) => boolean;
285
+ /**
286
+ * Read the state record from `.lunora/dev.json`, or `undefined` when there is
287
+ * no usable record. Best-effort; performs NO liveness check — use
288
+ * {@link readLiveDevServerState} for "is a dev server actually running".
289
+ */
290
+ declare const readDevServerState: (projectRoot: string) => DevServerState | undefined;
291
+ /**
292
+ * Write the state record to `.lunora/dev.json`, creating the `.lunora/`
293
+ * directory when absent. Returns the absolute path written, or `undefined`
294
+ * when the write failed (state is convenience metadata — a read-only checkout
295
+ * must never crash dev startup).
296
+ */
297
+ declare const writeDevServerState: (projectRoot: string, state: DevServerState) => string | undefined;
298
+ /**
299
+ * Merge `patch` into the existing state record, when one exists. Used by the
300
+ * CLI to stamp `background`/`logFile` onto the record the Vite plugin wrote.
301
+ * Returns the merged record, or `undefined` when there was nothing to update.
302
+ */
303
+ declare const updateDevServerState: (projectRoot: string, patch: Partial<DevServerState>) => DevServerState | undefined;
304
+ /**
305
+ * Remove `.lunora/dev.json`. Idempotent and never throws. When `expectedPid`
306
+ * is given, the file is only removed while it still records that PID — so a
307
+ * shutting-down server can't clobber the record a newer server just wrote.
308
+ */
309
+ declare const clearDevServerState: (projectRoot: string, expectedPid?: number) => void;
310
+ /**
311
+ * The state record of a dev server that is verifiably running right now, or
312
+ * `undefined`. A record whose PID is dead — or recycled onto a different
313
+ * process (see {@link isRecordedProcessCurrent}) — is stale: it is cleared on
314
+ * the spot so subsequent starts don't keep re-reading a corpse.
315
+ */
316
+ declare const readLiveDevServerState: (projectRoot: string) => DevServerState | undefined;
317
+ /** Result of {@link claimDevServerState}: claimed, or lost to the live server already recorded. */
318
+ interface ClaimDevServerStateResult {
319
+ /** The live record that won the race, when `ok` is `false`. */
320
+ existing?: DevServerState;
321
+ /** Whether this process now owns the record. */
322
+ ok: boolean;
323
+ }
324
+ declare const claimDevServerState: (projectRoot: string, state: DevServerState, options?: {
325
+ supersedePid?: number;
326
+ }) => ClaimDevServerStateResult;
327
+ /**
328
+ * The `.dev.vars` line grammar — one owner, shared by every reader/writer of the
329
+ * file so the format can't drift between packages. `@lunora/cli`'s `env`
330
+ * command (parse/serialize) and `@lunora/config`'s scaffolder (comment-
331
+ * preserving rewrite) do different *transforms*, but they agree on these
332
+ * primitives: the filename, what a `KEY` looks like, how lines split, and how
333
+ * quotes strip.
334
+ */
104
335
  /** The conventional filename for local Cloudflare dev secrets (gitignored). */
105
336
  declare const DEV_VARS_FILE: string;
106
337
  /** Its committed, secret-free counterpart that scaffolding reads from. */
107
338
  declare const DEV_VARS_EXAMPLE_FILE: string;
108
339
  /** A bare `KEY` identifier — the part left of `=` in a `.dev.vars` line. */
109
340
  declare const DEV_VARS_KEY_PATTERN: RegExp;
110
- /** Splits file content into lines on either newline style. */
111
-
112
- /**
113
- * Parse `.dev.vars` content into its `{ key, value }` entries, in file order,
114
- * with values unquoted and comments/blank/invalid lines dropped. The canonical
115
- * read of the whole file — callers that just want the variables (rather than a
116
- * comment-preserving rewrite) use this instead of hand-rolling the split loop.
117
- */
341
+ /**
342
+ * Parse `.dev.vars` content into its `{ key, value }` entries, in file order,
343
+ * with values unquoted and comments/blank/invalid lines dropped. The canonical
344
+ * read of the whole file — callers that just want the variables (rather than a
345
+ * comment-preserving rewrite) use this instead of hand-rolling the split loop.
346
+ */
118
347
  declare const parseDevVariableEntries: (content: string) => {
119
348
  key: string;
120
349
  value: string;
121
350
  }[];
122
- interface DiscoverWorkflowInfoResult {
123
- /** Parse error message, when `lunora/workflows.ts` exists but could not be analyzed. */
124
- error?: string;
125
- /** Discovered workflow definitions; `[]` when none are declared or parsing failed. */
126
- workflows: ReadonlyArray<WorkflowIR>;
127
- }
128
- /**
129
- * Discover the project's `defineWorkflow` declarations. Returns
130
- * `{ workflows: [] }` when the project has no `lunora/workflows.ts` (not an
131
- * error), or `{ workflows: [], error }` when the file exists but could not be
132
- * parsed — callers decide whether that is a warning (validator) or ignorable
133
- * (inference).
134
- */
135
- declare const discoverWorkflowInfo: (projectRoot: string, schemaDirectory: string) => DiscoverWorkflowInfoResult;
136
- interface DurableObjectSpec {
137
- binding: string;
138
- className: string;
139
- }
140
351
  /**
141
- * A `defineContainer` declaration plus whether its generated DO class is
142
- * exported by the worker entry. Only exported containers are safe to
143
- * provision — wrangler rejects a `containers[].class_name` (and its Durable
144
- * Object binding) that the worker doesn't export.
145
- */
146
- interface InferredContainer extends ContainerIR {
147
- exported: boolean;
148
- }
149
- /**
150
- * A `defineWorkflow` declaration plus whether its generated
151
- * `WorkflowEntrypoint` class is exported by the worker entry. Only exported
152
- * workflows are safe to provision — wrangler rejects a `workflows[].class_name`
153
- * the worker doesn't export. Workflows are NOT Durable Objects, so this never
154
- * implies a `durable_objects` binding or migration.
155
- */
156
- interface InferredWorkflow extends WorkflowIR {
157
- exported: boolean;
158
- }
159
- interface InferredBindings {
160
- /** Containers declared in `lunora/containers.ts` (exported or not — see {@link InferredContainer.exported}). */
161
- containers: InferredContainer[];
162
- /** Durable Objects the worker entry exports → safe to bind. */
163
- durableObjects: DurableObjectSpec[];
164
- /** Schema declares a `.global()` table → needs the `DB` D1 binding. */
165
- needsD1: boolean;
166
- /** Human-readable provenance for each inferred binding / hint, for logging. */
167
- signals: string[];
168
- /** `@lunora/ai` is imported or `env.AI` is used → needs the `ai` Workers AI binding. */
169
- usesAi: boolean;
170
- /** `@lunora/analytics` is imported → self-describing `analytics_engine_datasets` binding (auto-writeable). */
171
- usesAnalytics: boolean;
172
- /** `@lunora/auth` is imported (sessions may be D1- or `SessionDO`-backed). */
173
- usesAuth: boolean;
174
- /** `@lunora/browser` is imported → self-describing `browser` binding (auto-writeable). */
175
- usesBrowser: boolean;
176
- /** `@lunora/hyperdrive` is imported (binding needs an un-mintable remote `id`; hint-only). */
177
- usesHyperdrive: boolean;
178
- /** `@lunora/images` is imported → self-describing `images` binding (auto-writeable). */
179
- usesImages: boolean;
180
- /** `@lunora/kv` is imported (namespace binding name + id are user-defined; hint-only). */
181
- usesKv: boolean;
182
- /** `@lunora/mail` is imported (Resend API key must be set in `.dev.vars`; no binding). */
183
- usesMail: boolean;
184
- /** `@lunora/payment` is imported (provider secrets must be set in `.dev.vars`; no binding). */
185
- usesPayment: boolean;
186
- /** `@lunora/pipelines` is imported (binding needs an un-mintable remote pipeline name; hint-only). */
187
- usesPipelines: boolean;
188
- /** `@lunora/scheduler` is imported. */
189
- usesScheduler: boolean;
190
- /** `@lunora/storage` is imported (R2 bucket binding name is user-defined). */
191
- usesStorage: boolean;
192
- /** Workflows declared in `lunora/workflows.ts` (exported or not — see {@link InferredWorkflow.exported}). */
193
- workflows: InferredWorkflow[];
194
- }
195
- interface InferOptions {
196
- projectRoot: string;
197
- /** Directories (relative to root) to scan. Defaults to `lunora` + `src`. */
198
- scanDirs?: ReadonlyArray<string>;
199
- /** Lunora source directory holding `schema.ts`. Defaults to `lunora`. */
200
- schemaDir?: string;
201
- }
202
- /**
203
- * Scan a Lunora project and report which Cloudflare bindings its code implies.
204
- * Read-only: performs no writes. Binding provisioning is driven by the worker
205
- * entry's Durable Object exports plus the schema's D1 need; capability imports
206
- * surface as hints.
207
- */
208
- declare const inferLunoraBindings: (options: InferOptions) => Promise<InferredBindings>;
209
- /**
210
- * Derive the list of `@lunora/*` package names that are actively used by a
211
- * project, based on its already-resolved {@link InferredBindings}.
212
- *
213
- * This is the canonical bridge between binding inference and the package-aware
214
- * `.dev.vars.example` scaffolding in `scaffold-dev-variables.ts`. The result is
215
- * a stable, predictable slice of {@link CAPABILITY_SOURCES} source values,
216
- * filtered to the flags that are `true` in `bindings` — in CAPABILITY_SOURCES
217
- * declaration order.
218
- */
219
- declare const packageNamesFromBindings: (bindings: InferredBindings) => string[];
352
+ * Escape a runtime string for safe literal interpolation into a `RegExp`
353
+ * source. The one canonical implementation — `@lunora/config`'s own
354
+ * `infer-bindings.ts` (type-only-export detection) and `@lunora/cli`'s `env`
355
+ * command both need this and used to carry their own (functionally
356
+ * identical) copy; grammar this fundamental gets one owner like everything
357
+ * else in this module.
358
+ */
359
+ declare const escapeRegExp: (value: string) => string;
360
+ /**
361
+ * Surgically upsert a single `KEY="value"` line in raw `.dev.vars` content,
362
+ * leaving every comment, blank line, and untouched entry verbatim. Rebuilding
363
+ * the whole file from the parsed entry map (an earlier approach) silently
364
+ * dropped all `# …` comments and blank lines — including the documentation the
365
+ * registry installer and scaffolder write — and re-quoted lines the user never
366
+ * touched. If the key already has a line it is replaced in place; otherwise the
367
+ * new line is appended with a single trailing newline. Always quotes the value
368
+ * to preserve a whitespace round-trip; callers (`env set`, `env generate --set`,
369
+ * `deploy`'s minted-secret disclosure) reject newline/`"`/`\` up front so the
370
+ * verbatim quote is safe.
371
+ *
372
+ * Duplicate `KEY=` lines are collapsed down to exactly one. The shared read
373
+ * path (`parseDevVariableEntries` above) is last-wins — it keeps overwriting a
374
+ * Map entry as it walks the file, so with duplicate lines the LAST one wins at
375
+ * read time. Replacing only the first match (as a plain, non-global
376
+ * `.replace()` does) left that later, untouched duplicate still winning at read
377
+ * time — a `set` that silently didn't take effect. The first matching line is
378
+ * replaced in place (preserving its position in the file); every later
379
+ * duplicate is dropped entirely (including its own trailing newline).
380
+ */
381
+ declare const upsertDevVariableLine: (content: string, key: string, value: string) => string;
382
+ /** The default deploy target — today's behavior for every project. */
383
+ declare const DEFAULT_DEPLOY_TARGET = "cloudflare";
384
+ /** The ids a caller may select, for error messages and `--target` help text. */
385
+ declare const deployTargetIds: () => ReadonlyArray<string>;
386
+ /**
387
+ * Resolve a target name to its driver.
388
+ * @throws when `target` names no registered driver — never falls back to the default.
389
+ */
390
+ declare const resolveDeployDriver: (target?: string) => DeployDriver;
220
391
  /** Directory holding per-checkout Lunora state (gitignored by convention). */
221
392
  declare const LINKED_PROJECT_DIR = ".lunora";
222
393
  /** The canonical link filename, relative to the project root. */
223
394
  declare const LINKED_PROJECT_FILE: string;
224
395
  /**
225
- * The link record persisted to `.lunora/project.json`. Every field is optional
226
- * so a partially-populated link (e.g. a worker name with no URL yet) still
227
- * round-trips. `linkedAt` is an ISO-8601 stamp written at link time, purely
228
- * informational.
229
- */
396
+ * The link record persisted to `.lunora/project.json`. Every field is optional
397
+ * so a partially-populated link (e.g. a worker name with no URL yet) still
398
+ * round-trips. `linkedAt` is an ISO-8601 stamp written at link time, purely
399
+ * informational.
400
+ */
230
401
  interface LinkedProject {
231
402
  /** Cloudflare account id the worker lives under, when known. */
232
403
  account?: string;
@@ -240,34 +411,17 @@ interface LinkedProject {
240
411
  workerUrl?: string;
241
412
  }
242
413
  /**
243
- * Read the link record from `.lunora/project.json`, or `undefined` when there
244
- * is no usable link. Best-effort: a missing file, parse error, or unexpected
245
- * shape all collapse to `undefined`.
246
- */
414
+ * Read the link record from `.lunora/project.json`, or `undefined` when there
415
+ * is no usable link. Best-effort: a missing file, parse error, or unexpected
416
+ * shape all collapse to `undefined`.
417
+ */
247
418
  declare const readLinkedProject: (projectRoot: string) => LinkedProject | undefined;
248
419
  /**
249
- * Write the link record to `.lunora/project.json`, creating the `.lunora/`
250
- * directory when absent. Only defined fields are persisted (so an empty value
251
- * never clobbers a known one). Returns the absolute path written.
252
- */
420
+ * Write the link record to `.lunora/project.json`, creating the `.lunora/`
421
+ * directory when absent. Only defined fields are persisted (so an empty value
422
+ * never clobbers a known one). Returns the absolute path written.
423
+ */
253
424
  declare const writeLinkedProject: (projectRoot: string, link: LinkedProject) => string;
254
- /**
255
- * Shared formatter for the structured log events the Lunora runtime emits to the
256
- * worker's `console` during development. Both the CLI `dev` command and the Vite
257
- * plugin pipe worker output through `formatLunoraEvent` so a developer sees
258
- * attributed, readable lines instead of raw JSON.
259
- *
260
- * The runtime emits two event shapes, each a single `console` line tagged
261
- * `source: "lunora"` (see `@lunora/do`'s `request-log.ts`): a `type: "log"`
262
- * event per `ctx.log.*` call, and a `type: "request"` event per RPC dispatch
263
- * (opt-in for successful calls, always for errors).
264
- *
265
- * This module is intentionally dependency-free and colour-free: it returns the
266
- * severity plus a plain display string, leaving ANSI/level colouring to each
267
- * caller (the CLI routes through its `pail` logger; the Vite plugin dims inline).
268
- * Any line that is not a lunora event returns `undefined`, signalling the caller
269
- * to pass it through unchanged.
270
- */
271
425
  /** Severity a formatted line should be surfaced at, mapped onto the three logger channels. */
272
426
  type LunoraLineLevel = "error" | "info" | "warn";
273
427
  /** A formatted lunora event: the channel to surface it on, the display text, and which event produced it. */
@@ -282,11 +436,11 @@ interface LunoraFormattedLine {
282
436
  /** Stable `source` tag every lunora console event carries. Mirrors `REQUEST_LOG_EVENT_SOURCE` in `@lunora/do`. */
283
437
  declare const LUNORA_EVENT_SOURCE = "lunora";
284
438
  /**
285
- * Parse a single worker-output line and, when it is a lunora structured event,
286
- * return its severity plus display text. Returns `undefined` for anything else —
287
- * non-JSON lines, JSON that isn't a lunora event, or an unrecognised event type
288
- * — so the caller passes the original line through untouched. Pure and total.
289
- */
439
+ * Parse a single worker-output line and, when it is a lunora structured event,
440
+ * return its severity plus display text. Returns `undefined` for anything else —
441
+ * non-JSON lines, JSON that isn't a lunora event, or an unrecognised event type
442
+ * — so the caller passes the original line through untouched. Pure and total.
443
+ */
290
444
  declare const formatLunoraEvent: (line: string) => LunoraFormattedLine | undefined;
291
445
  declare class LunoraReporter {
292
446
  #private;
@@ -295,32 +449,32 @@ declare class LunoraReporter {
295
449
  log(meta: unknown): void;
296
450
  }
297
451
  /**
298
- * Per-package secret-requirements registry for `.dev.vars` scaffolding.
299
- *
300
- * Each entry maps a `@lunora/*` package name → the secrets it requires at
301
- * runtime, expressed as `{ key, description, docsUrl }` records. The scaffolder
302
- * in {@link ./scaffold-dev-variables} reads this registry to emit package-aware
303
- * `.dev.vars.example` entries (placeholders + inline doc-pointer comments).
304
- *
305
- * ## Adding a new add-on
306
- *
307
- * When a new `@lunora/*` package requires runtime secrets, add one entry to
308
- * {@link PACKAGE_SECRETS_REGISTRY} keyed by its exact npm package name. Each
309
- * `SecretEntry` in the array needs:
310
- *
311
- * - `key` — the env-var name the package reads from `env` (e.g. `RESEND_API_KEY`).
312
- * - `description` — one line describing the secret and how to obtain it.
313
- * - `docsUrl` — a stable URL to the package/provider docs.
314
- *
315
- * The registry lives in `@lunora/config` so that add-ons themselves never need
316
- * to depend on it (no circular coupling). Add-ons document their secrets in
317
- * their own READMEs; the registry duplicates that knowledge in a machine-readable
318
- * form that the scaffolder can consume.
319
- *
320
- * **Never write a real secret value** in this file — `placeholderValue` entries
321
- * are the only allowed values (they must pass `isPlaceholderValue` from
322
- * scaffold-dev-variables).
323
- */
452
+ * Per-package secret-requirements registry for `.dev.vars` scaffolding.
453
+ *
454
+ * Each entry maps a `@lunora/*` package name → the secrets it requires at
455
+ * runtime, expressed as `{ key, description, docsUrl }` records. The scaffolder
456
+ * in {@link ./scaffold-dev-variables} reads this registry to emit package-aware
457
+ * `.dev.vars.example` entries (placeholders + inline doc-pointer comments).
458
+ *
459
+ * ## Adding a new add-on
460
+ *
461
+ * When a new `@lunora/*` package requires runtime secrets, add one entry to
462
+ * {@link PACKAGE_SECRETS_REGISTRY} keyed by its exact npm package name. Each
463
+ * `SecretEntry` in the array needs:
464
+ *
465
+ * - `key` — the env-var name the package reads from `env` (e.g. `RESEND_API_KEY`).
466
+ * - `description` — one line describing the secret and how to obtain it.
467
+ * - `docsUrl` — a stable URL to the package/provider docs.
468
+ *
469
+ * The registry lives in `@lunora/config` so that add-ons themselves never need
470
+ * to depend on it (no circular coupling). Add-ons document their secrets in
471
+ * their own READMEs; the registry duplicates that knowledge in a machine-readable
472
+ * form that the scaffolder can consume.
473
+ *
474
+ * **Never write a real secret value** in this file — `placeholderValue` entries
475
+ * are the only allowed values (they must pass `isPlaceholderValue` from
476
+ * scaffold-dev-variables).
477
+ */
324
478
  /** A single secret variable required by a package. */
325
479
  interface SecretEntry {
326
480
  /** One sentence describing what this secret is and how to obtain it. */
@@ -330,96 +484,128 @@ interface SecretEntry {
330
484
  /** The env-var key as it appears in `.dev.vars`, e.g. `AUTH_SECRET`. */
331
485
  key: string;
332
486
  /**
333
- * The placeholder value written into `.dev.vars.example`.
334
- * Must pass `isPlaceholderValue` from scaffold-dev-variables so the
335
- * scaffolder regenerates it when generating `.dev.vars`. Use angle-bracket
336
- * conventions or a recognised marker like `replace-with-openssl-rand-hex-32`.
337
- * Non-secret env-vars (e.g. `AUTH_URL`) may carry a real default value.
338
- */
487
+ * The placeholder value written into `.dev.vars.example`.
488
+ * Must pass `isPlaceholderValue` from scaffold-dev-variables so the
489
+ * scaffolder regenerates it when generating `.dev.vars`. Use angle-bracket
490
+ * conventions or a recognised marker like `replace-with-openssl-rand-hex-32`.
491
+ * Non-secret env-vars (e.g. `AUTH_URL`) may carry a real default value.
492
+ */
339
493
  placeholderValue: string;
340
494
  }
341
495
  /**
342
- * Secrets every Lunora project needs regardless of which capability packages are
343
- * installed — scaffolded into `.dev.vars` always. `LUNORA_ADMIN_TOKEN` is the
344
- * bearer the local Studio uses to call the worker's admin endpoints (the data
345
- * browser, schema edits) in dev; the worker reads the SAME `.dev.vars` value via
346
- * its admin gate, so both agree and the Studio authenticates without a prompt.
347
- * Without it, every `/_lunora/admin/*` call is `ADMIN_FORBIDDEN` (403).
348
- */
349
-
350
- /**
351
- * The canonical registry of per-package secret requirements.
352
- *
353
- * Keys are exact npm package names (e.g. `"@lunora/auth"`). Values are
354
- * non-empty arrays of {@link SecretEntry} — one entry per required secret key.
355
- *
356
- * The scaffolder in `scaffold-dev-variables.ts` calls
357
- * {@link secretsForPackages} to resolve the applicable entries from this map
358
- * given the set of detected capability package names.
359
- */
496
+ * The canonical registry of per-package secret requirements.
497
+ *
498
+ * Keys are exact npm package names (e.g. `"@lunora/auth"`). Values are
499
+ * non-empty arrays of {@link SecretEntry} — one entry per required secret key.
500
+ *
501
+ * The scaffolder in `scaffold-dev-variables.ts` calls
502
+ * {@link secretsForPackages} to resolve the applicable entries from this map
503
+ * given the set of detected capability package names.
504
+ */
360
505
  declare const PACKAGE_SECRETS_REGISTRY: Readonly<Record<string, ReadonlyArray<SecretEntry>>>;
361
506
  /**
362
- * Collect all secret entries required by the given set of package names. The
363
- * order follows the order of `packageNames` (stable, predictable output), and
364
- * within each package the entries are returned in registry declaration order.
365
- *
366
- * Only packages present in {@link PACKAGE_SECRETS_REGISTRY} contribute entries;
367
- * unknown package names are silently ignored — this makes the call site resilient
368
- * to future capability flags whose packages have no secrets.
369
- */
507
+ * Collect all secret entries required by the given set of package names. The
508
+ * order follows the order of `packageNames` (stable, predictable output), and
509
+ * within each package the entries are returned in registry declaration order.
510
+ *
511
+ * Only packages present in {@link PACKAGE_SECRETS_REGISTRY} contribute entries;
512
+ * unknown package names are silently ignored — this makes the call site resilient
513
+ * to future capability flags whose packages have no secrets.
514
+ */
370
515
  declare const secretsForPackages: (packageNames: ReadonlyArray<string>) => SecretEntry[];
371
516
  /** The canonical project-config filename probed at the project root. */
372
517
  declare const LUNORA_CONFIG_FILE = "lunora.json";
373
518
  /**
374
- * The parsed `remote` preference from `lunora.json`:
375
- *
376
- * - `true` / `false` — the boolean form: enable or explicitly disable remote dev.
377
- * - `undefined` — no usable preference (file absent, key absent, or malformed).
378
- *
379
- * The object form (scoping which binding kinds go remote) is reserved for a
380
- * future increment; for now an object value is treated as "enabled" (truthy
381
- * presence) so forward-written configs still turn remote on.
382
- */
519
+ * The parsed `remote` preference from `lunora.json`:
520
+ *
521
+ * - `true` / `false` — the boolean form: enable or explicitly disable remote dev.
522
+ * - `undefined` — no usable preference (file absent, key absent, or malformed).
523
+ *
524
+ * The object form (scoping which binding kinds go remote) is reserved for a
525
+ * future increment; for now an object value is treated as "enabled" (truthy
526
+ * presence) so forward-written configs still turn remote on.
527
+ */
383
528
  type RemotePreference = boolean | undefined;
384
529
  /** The structural slice of `lunora.json` Lunora reads. */
385
530
  interface LunoraProjectConfig {
386
531
  remote?: unknown;
532
+ target?: unknown;
387
533
  }
388
534
  /**
389
- * Interpret a raw `remote` value into a tri-state preference. A boolean passes
390
- * through; an object is treated as enabled (the documented-but-not-yet-honored
391
- * scoping form is still an opt-in); anything else (string, number, null) is no
392
- * preference.
393
- */
535
+ * Interpret a raw `remote` value into a tri-state preference. A boolean passes
536
+ * through; an object is treated as enabled (the documented-but-not-yet-honored
537
+ * scoping form is still an opt-in); anything else (string, number, null) is no
538
+ * preference.
539
+ */
394
540
  declare const interpretRemote: (value: unknown) => RemotePreference;
395
541
  /**
396
- * Read the project's `remote` preference from `lunora.json`, or `undefined` when
397
- * there's no usable preference. Best-effort: never throws — a missing file,
398
- * parse error, or unexpected shape all collapse to `undefined` so the caller
399
- * falls through to the env/flag layers.
400
- */
542
+ * Read the project's `remote` preference, or `undefined` when there is no
543
+ * usable one — the caller then falls through to the env/flag layers.
544
+ */
401
545
  declare const readProjectRemotePreference: (projectRoot: string) => RemotePreference;
402
546
  /**
403
- * Whether we can interactively prompt — stdin must be a TTY. In CI / piped
404
- * contexts this is false, and callers should fall back to a non-interactive
405
- * default (skip, or require an explicit `--yes`) rather than hang on a read.
406
- */
547
+ * Read the project's deploy `target` from `lunora.json`, or `undefined` when
548
+ * absent.
549
+ *
550
+ * Delegates to `@lunora/codegen` rather than re-reading the file here.
551
+ * `runCodegen` has to resolve the same key without this package (config
552
+ * depends on codegen, not the reverse), so the parser lives there and this is a
553
+ * re-export — two readers of one key is exactly the kind of copy that drifts.
554
+ */
555
+ declare const readProjectTarget: (projectRoot: string) => string | undefined;
556
+ /**
557
+ * Resolve the deploy target for a command: an explicit `--target` wins, then
558
+ * `lunora.json`, then the registry default.
559
+ *
560
+ * **This is the canonical resolution point, and the reason it is one place.**
561
+ * Codegen tailors the emitted `ctx.*` surface to a target while deploy picks
562
+ * the driver that ships it. Resolve those separately and they can disagree,
563
+ * producing an app that builds cleanly and fails at runtime with nothing in the
564
+ * build to explain it. Every caller — CLI commands, the Vite plugin,
565
+ * `runCodegen`'s own fallback — resolves through here or through
566
+ * {@link resolveTargetOrThrow}, so there is exactly one precedence order to
567
+ * reason about.
568
+ */
569
+ declare const resolveProjectTarget: (projectRoot: string, explicit?: string) => string;
570
+ /**
571
+ * Resolve the deploy target as {@link resolveProjectTarget} does, then reject
572
+ * one that no registered driver serves.
573
+ *
574
+ * Lives here rather than in `@lunora/cli` because nothing about it is
575
+ * CLI-shaped and the Vite plugin needs the same guard — a validator only the
576
+ * CLI could reach left `vite build` emitting the default surface for a
577
+ * mis-declared target, silently.
578
+ *
579
+ * Callers that go on to resolve a driver get this for free; the ones that never
580
+ * look a driver up — codegen, the Vite plugin — need it, because otherwise
581
+ * nothing in their path ever rejects the name.
582
+ * @param projectRoot Directory containing `lunora.json`.
583
+ * @param explicit A caller-supplied target, if any.
584
+ * @throws when the resolved target names no registered driver.
585
+ * @returns the resolved, registered target id.
586
+ */
587
+ declare const resolveTargetOrThrow: (projectRoot: string, explicit?: string) => string;
588
+ /**
589
+ * Whether we can interactively prompt — stdin must be a TTY. In CI / piped
590
+ * contexts this is false, and callers should fall back to a non-interactive
591
+ * default (skip, or require an explicit `--yes`) rather than hang on a read.
592
+ */
407
593
  declare const isInteractive: () => boolean;
408
594
  /**
409
- * Ask a yes/no question on stdin. With `defaultYes`, an empty answer (just
410
- * Enter) counts as yes and the prompt should read `[Y/n]`; otherwise empty is
411
- * no (`[y/N]`). Shared by the CLI (`reset`, `dev`) and the Vite dev server.
412
- */
595
+ * Ask a yes/no question on stdin. With `defaultYes`, an empty answer (just
596
+ * Enter) counts as yes and the prompt should read `[Y/n]`; otherwise empty is
597
+ * no (`[y/N]`). Shared by the CLI (`reset`, `dev`) and the Vite dev server.
598
+ */
413
599
  declare const promptYesNo: (prompt: string, options?: {
414
600
  defaultYes?: boolean;
415
601
  }) => Promise<boolean>;
416
602
  /**
417
- * Build a default-yes `confirm(message)` for the scaffolders' `ensureDevVariables`:
418
- * an interactive `[Y/n]` prompt (optionally prefixed, e.g. `"[lunora] "`) when
419
- * stdin is a TTY, or an immediate `false` otherwise — so CI declines silently
420
- * instead of blocking. Keeps the "non-interactive ⇒ decline" policy in one place
421
- * rather than re-stated at every call site.
422
- */
603
+ * Build a default-yes `confirm(message)` for the scaffolders' `ensureDevVariables`:
604
+ * an interactive `[Y/n]` prompt (optionally prefixed, e.g. `"[lunora] "`) when
605
+ * stdin is a TTY, or an immediate `false` otherwise — so CI declines silently
606
+ * instead of blocking. Keeps the "non-interactive ⇒ decline" policy in one place
607
+ * rather than re-stated at every call site.
608
+ */
423
609
  declare const createConfirm: (prefix?: string) => ((message: string) => Promise<boolean>);
424
610
  /** One choice in a {@link promptSelect} list. `value` is returned; `label` (and optional `description`) are shown. */
425
611
  interface SelectOption<T extends string> {
@@ -428,261 +614,67 @@ interface SelectOption<T extends string> {
428
614
  value: T;
429
615
  }
430
616
  /**
431
- * Ask the user to pick one option from a numbered list on stdin. Accepts the
432
- * 1-based number or the option's `value`/`label` typed verbatim; an empty answer
433
- * (just Enter) takes `settings.default`. In a non-interactive context (CI /
434
- * piped — no TTY) it never reads and returns `settings.default` (or `undefined`),
435
- * mirroring {@link createConfirm}'s "non-interactive ⇒ fall back" policy so
436
- * automation never blocks.
437
- */
617
+ * Ask the user to pick one option from a numbered list on stdin. Accepts the
618
+ * 1-based number or the option's `value`/`label` typed verbatim; an empty answer
619
+ * (just Enter) takes `settings.default`. In a non-interactive context (CI /
620
+ * piped — no TTY) it never reads and returns `settings.default` (or `undefined`),
621
+ * mirroring {@link createConfirm}'s "non-interactive ⇒ fall back" policy so
622
+ * automation never blocks.
623
+ */
438
624
  declare const promptSelect: <T extends string>(message: string, options: ReadonlyArray<SelectOption<T>>, settings?: {
439
625
  default?: T;
440
626
  }) => Promise<T | undefined>;
627
+ /**
628
+ * Ask a free-text question on stdin, returning the trimmed answer. An empty
629
+ * answer (just Enter) takes `settings.default`. In a non-interactive context
630
+ * (CI / piped — no TTY) it never reads and returns `settings.default` (or
631
+ * `undefined`), mirroring {@link promptSelect}'s "non-interactive ⇒ fall back"
632
+ * policy so automation never blocks.
633
+ */
634
+ declare const promptText: (message: string, settings?: {
635
+ default?: string;
636
+ }) => Promise<string | undefined>;
441
637
  /** One choice in a {@link promptMultiSelect} list. Identical shape to {@link SelectOption}; `value` is returned when picked. */
442
638
  type MultiSelectOption<T extends string> = SelectOption<T>;
443
639
  /**
444
- * Ask the user to pick zero or more options from a numbered list on stdin.
445
- * Accepts a comma- or space-separated list of 1-based numbers and/or option
446
- * `value`/`label`s typed verbatim; an empty answer (just Enter) takes
447
- * `settings.defaults`. In a non-interactive context (CI / piped — no TTY) it
448
- * never reads and returns `settings.defaults ?? []`, mirroring {@link promptSelect}'s
449
- * "non-interactive ⇒ fall back" policy so automation never blocks. Unknown
450
- * tokens are ignored; the returned list is de-duplicated and preserves option
451
- * order.
452
- */
640
+ * Ask the user to pick zero or more options from a numbered list on stdin.
641
+ * Accepts a comma- or space-separated list of 1-based numbers and/or option
642
+ * `value`/`label`s typed verbatim; an empty answer (just Enter) takes
643
+ * `settings.defaults`. In a non-interactive context (CI / piped — no TTY) it
644
+ * never reads and returns `settings.defaults ?? []`, mirroring {@link promptSelect}'s
645
+ * "non-interactive ⇒ fall back" policy so automation never blocks. Unknown
646
+ * tokens are ignored; the returned list is de-duplicated and preserves option
647
+ * order.
648
+ */
453
649
  declare const promptMultiSelect: <T extends string>(message: string, options: ReadonlyArray<MultiSelectOption<T>>, settings?: {
454
650
  defaults?: ReadonlyArray<T>;
455
651
  }) => Promise<T[]>;
456
- /**
457
- * A container/workflow that is declared (so codegen emits its class) but the
458
- * worker entry never re-exports — the one wiring step the generators can't always
459
- * do for the developer. wrangler rejects a `class_name` the deployed worker
460
- * doesn't export, so a deploy fails late on this; surfacing it as structured data
461
- * lets the Vite plugin raise it in the dev error overlay (not just the console)
462
- * the moment the gap appears. The human-readable form is also folded into
463
- * {@link ReconcileBindingsResult.warnings}.
464
- */
465
- interface ExportGap {
466
- /** Generated class wrangler needs exported, e.g. `OrderPipelineWorkflow`. */
467
- className: string;
468
- /** The `lunora/{containers,workflows}.ts` export name, e.g. `orderPipeline`. */
469
- exportName: string;
470
- /** Which declaration is unexported. */
471
- kind: "container" | "workflow";
472
- /** The `_generated/{module}` to re-export from, e.g. `workflows`. */
473
- module: "containers" | "workflows";
474
- }
475
- interface ReconcileBindingsResult {
476
- /** Short labels for each binding written (e.g. `"SCHEDULER/SchedulerDO"`). */
477
- added: string[];
478
- /** `true` when `wrangler.jsonc` was rewritten. */
479
- changed: boolean;
480
- /**
481
- * Declared containers/workflows the worker entry doesn't re-export — the
482
- * structured form of the corresponding `warnings` entries, for the dev error
483
- * overlay. Empty when every declaration is wired.
484
- */
485
- exportGaps: ExportGap[];
486
- /** Reason reconciliation was skipped, for logging. */
487
- reason?: string;
488
- /** Non-fatal hints for capabilities that cannot be auto-provisioned. */
489
- warnings: string[];
490
- /** Resolved wrangler path, or `undefined` when none was found. */
491
- wranglerPath?: string;
492
- }
493
- /**
494
- * Reconcile inferred Durable Object / D1 bindings into `wrangler.jsonc`.
495
- *
496
- * Writes only when something is missing; returns `changed: false` when the
497
- * config already satisfies the inferred needs.
498
- */
499
- declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings) => ReconcileBindingsResult;
500
- /**
501
- * The wrangler config sections Lunora can safely flip to remote mode in dev,
502
- * each with the human label used in logs and the structural `shape` the entry
503
- * lives in.
504
- *
505
- * `"array"` is a top-level array of binding objects (`d1_databases`,
506
- * `kv_namespaces`, `r2_buckets`, `vectorize`, `services`). `"producers"` is
507
- * `queues.producers[]` — consumers are NOT remoted (their schema has no `remote`
508
- * field) and the edit path is two levels deep. `"object"` is a single binding
509
- * object, not an array (`ai`), whose edit path targets the section key directly.
510
- *
511
- * Every kind here was confirmed against `wrangler/config-schema.json`: the
512
- * entry's schema declares a `remote` property. Deliberately omits
513
- * `durable_objects` (no CF remote-DO mode; shards stay local) and sections whose
514
- * schema has no `remote` field (`hyperdrive`, `analytics_engine_datasets`,
515
- * `secrets_store_secrets`, queue consumers, …). Widening further is a one-line
516
- * table edit.
517
- */
518
- declare const REMOTE_ELIGIBLE_KEYS: {
519
- readonly ai: {
520
- readonly label: "AI";
521
- readonly shape: "object";
522
- };
523
- readonly d1_databases: {
524
- readonly label: "D1";
525
- readonly shape: "array";
526
- };
527
- readonly kv_namespaces: {
528
- readonly label: "KV";
529
- readonly shape: "array";
530
- };
531
- readonly queues: {
532
- readonly label: "Queue";
533
- readonly shape: "producers";
534
- };
535
- readonly r2_buckets: {
536
- readonly label: "R2";
537
- readonly shape: "array";
538
- };
539
- readonly services: {
540
- readonly label: "Service";
541
- readonly shape: "array";
542
- };
543
- readonly vectorize: {
544
- readonly label: "Vectorize";
545
- readonly shape: "array";
546
- };
547
- };
548
- type RemoteEligibleKey = keyof typeof REMOTE_ELIGIBLE_KEYS;
549
- /** One binding object as it appears in any eligible section. */
550
- interface BindingEntry {
551
- binding?: string;
552
- remote?: boolean;
553
- }
554
- /** One binding entry we mark remote, with enough provenance to log + edit it. */
555
- interface RemoteBindingPlan {
556
- /** The binding name as declared in the config (e.g. `"DB"`, `"FILES"`). */
557
- binding: string;
558
- /** Short kind label for logging (`"D1"`, `"KV"`, `"R2"`, `"Vectorize"`, …). */
559
- kind: string;
560
- /**
561
- * The jsonc edit path within {@link RemoteBindingPlan.section}, relative to
562
- * the section key: `[index]` for an `"array"` section, `["producers", index]`
563
- * for a queue producer, or `[]` for the single-object `ai` section. The
564
- * materializer prepends the section key and appends `"remote"`.
565
- */
566
- path: ReadonlyArray<number | string>;
567
- /** The wrangler config key the entry lives under. */
568
- section: RemoteEligibleKey;
569
- }
570
- /** The structural slice of a wrangler config the remote planner reads. */
571
- interface RemoteWranglerShape {
572
- ai?: BindingEntry | null;
573
- d1_databases?: ReadonlyArray<BindingEntry | null | undefined>;
574
- kv_namespaces?: ReadonlyArray<BindingEntry | null | undefined>;
575
- queues?: {
576
- producers?: ReadonlyArray<BindingEntry | null | undefined>;
577
- } | null;
578
- r2_buckets?: ReadonlyArray<BindingEntry | null | undefined>;
579
- services?: ReadonlyArray<BindingEntry | null | undefined>;
580
- vectorize?: ReadonlyArray<BindingEntry | null | undefined>;
581
- }
582
- /**
583
- * Inspect a parsed wrangler config and list every eligible binding that should
584
- * be flipped to remote mode. Pure — no file-system access, no mutation. An
585
- * entry already carrying `"remote": true` is still reported (so logging is
586
- * complete) but the materializer's edit is a harmless no-op for it.
587
- */
588
- declare const planRemoteBindings: (parsed: RemoteWranglerShape) => RemoteBindingPlan[];
589
- /**
590
- * Inject `"remote": true` onto each planned binding in the config `text`,
591
- * comment-preservingly via jsonc edits. Pure string→string; the edits target
592
- * disjoint entries so applying them sequentially is safe. The edit path is
593
- * `[section, ...plan.path, "remote"]`, which resolves to the array element, the
594
- * `queues.producers[i]` entry, or the single `ai` object as the plan demands.
595
- */
596
- declare const injectRemoteFlags: (text: string, plans: ReadonlyArray<RemoteBindingPlan>) => string;
597
- interface MaterializeOptions {
598
- /** When `false`, the call is a no-op (returns `enabled: false`). */
599
- enabled: boolean;
600
- projectRoot: string;
601
- }
602
- interface MaterializeResult {
603
- /**
604
- * Removes the generated temp config file. Always present and always safe to
605
- * call: it is idempotent, a no-op when nothing was written (disabled /
606
- * fall-through cases), and never throws if the path is already gone. The dev
607
- * command calls this on every exit path (normal, signal, error).
608
- */
609
- cleanup: () => void;
610
- /**
611
- * Absolute path to the generated temp config to pass to
612
- * `wrangler dev --config`. `undefined` when remote mode is disabled, no
613
- * wrangler config was found, it failed to parse, or it declared no eligible
614
- * binding (nothing to remote — run plain local dev).
615
- */
616
- configPath?: string;
617
- /** Whether remote mode was requested at all. */
618
- enabled: boolean;
619
- /** Why no temp config was produced, for logging (only set when none was). */
620
- reason?: string;
621
- /** The bindings flipped to remote, for the dev banner. */
622
- remoteBindings: RemoteBindingPlan[];
623
- }
624
- /**
625
- * Produce a temporary wrangler config with `"remote": true` on every eligible
626
- * binding, so `lunora dev` can run `wrangler dev --config &lt;temp>` against the
627
- * deployed D1/KV/R2 without touching the user's file.
628
- *
629
- * The temp file is written as a sibling of the source `wrangler.jsonc` (in the
630
- * project root), NOT an OS temp dir: wrangler resolves a config's relative paths
631
- * (`main`, `assets`, `migrations_dir`, …) against the **config file's own
632
- * directory**, so a temp config in `/tmp` would make wrangler look for
633
- * `/tmp/src/server.ts` and fail to start the worker. Keeping it beside the real
634
- * config preserves those relative paths. Returns `configPath: undefined` (with a
635
- * `reason`) for every fall-through case so the caller degrades to plain local
636
- * dev instead of failing.
637
- */
638
- declare const materializeRemoteWranglerConfig: (options: MaterializeOptions) => MaterializeResult;
639
- /**
640
- * Parse a `LUNORA_REMOTE` env value into the on/off decision. Truthy when set to
641
- * `"1"` or `"true"` (case-insensitive); anything else — unset, `"0"`, `"false"`,
642
- * empty — is off. Mirrors the `"1" | "true"` convention used across the runtime.
643
- */
644
- declare const isRemoteEnvEnabled: (value: string | undefined) => boolean;
645
- /** The three inputs that can switch remote-binding dev on, in precedence order. */
646
- interface RemoteEnableInputs {
647
- /**
648
- * The `remote` preference from `lunora.json` (the lowest-priority signal).
649
- * `undefined` means "no project preference"; an explicit `false` here loses
650
- * to neither the flag nor the env when those are absent — it just stays off.
651
- */
652
- configPreference?: boolean;
653
- /** The raw `LUNORA_REMOTE` env value (parsed with {@link isRemoteEnvEnabled}). */
654
- envValue?: string;
655
- /** The explicit `--remote` CLI flag — `true` when passed, `undefined`/`false` otherwise. */
656
- flag?: boolean;
657
- }
658
- /**
659
- * Resolve whether remote-binding dev is on, with a clear precedence:
660
- *
661
- * 1. an explicit `--remote` flag (highest — a deliberate per-invocation choice),
662
- * 2. then `LUNORA_REMOTE` in the environment,
663
- * 3. then the `remote` key in `lunora.json` (lowest — a project default).
664
- *
665
- * The flag and env are one-directional (they can only turn remote *on*); only
666
- * the config preference carries a meaningful `false`, and it applies solely when
667
- * neither stronger signal is present. So a project that sets `"remote": false`
668
- * is still overridable per-run by `--remote` or `LUNORA_REMOTE=1`.
669
- */
670
- declare const resolveRemoteEnabled: (inputs: RemoteEnableInputs) => boolean;
671
- /**
672
- * Whether an (already-unquoted) value looks like a fill-me-in placeholder —
673
- * empty, angle-bracketed, or containing a known marker — rather than a real
674
- * value. Used both when scaffolding (which values to regenerate) and by
675
- * `lunora env doctor` (which set values are still unfilled).
676
- */
652
+ /** Core (always-scaffolded) secrets followed by the package-specific ones for the detected capabilities. */
653
+ declare const requiredSecrets: (packageNames: ReadonlyArray<string>) => SecretEntry[];
654
+ /**
655
+ * Whether an (already-unquoted) value looks like a fill-me-in placeholder —
656
+ * empty, angle-bracketed, or containing a known marker — rather than a real
657
+ * value. Used both when scaffolding (which values to regenerate) and by
658
+ * `lunora env doctor` (which set values are still unfilled).
659
+ */
677
660
  declare const isPlaceholderValue: (value: string) => boolean;
678
661
  /**
679
- * The outcome of planning a scaffold — a discriminated union so the orchestrator
680
- * never has to re-derive whether `content` is present.
681
- *
682
- * `exists`: `.dev.vars` is already there; nothing to do.
683
- * `no-example`: nothing to scaffold from (stay silent — the project may not use secrets).
684
- * `generate`: write `content`, a copy of the example with secret-looking placeholders replaced by fresh random hex (`generatedKeys` lists which).
685
- */
662
+ * True for a secret-looking key whose value Lunora can mint locally (a random
663
+ * 32-byte hex, like `openssl rand -hex 32`) — e.g. `AUTH_SECRET`,
664
+ * `LUNORA_ADMIN_TOKEN`, `STORAGE_SIGNING_SECRET`. False for provider-issued keys
665
+ * ({@link PROVIDER_SECRET_KEYS}) and any non-secret key.
666
+ */
667
+ declare const isMintableSecretKey: (key: string) => boolean;
668
+ /** Mint a fresh strong secret value — 64 hex chars (32 bytes), like `openssl rand -hex 32`. */
669
+ declare const generateSecretValue: (randomHex?: (bytes: number) => string) => string;
670
+ /**
671
+ * The outcome of planning a scaffold — a discriminated union so the orchestrator
672
+ * never has to re-derive whether `content` is present.
673
+ *
674
+ * `exists`: `.dev.vars` is already there; nothing to do.
675
+ * `no-example`: nothing to scaffold from (stay silent — the project may not use secrets).
676
+ * `generate`: write `content`, a copy of the example with secret-looking placeholders replaced by fresh random hex (`generatedKeys` lists which).
677
+ */
686
678
  type ScaffoldPlan = {
687
679
  content: string;
688
680
  generatedKeys: string[];
@@ -695,7 +687,8 @@ type ScaffoldPlan = {
695
687
  /** Decide whether (and what) to scaffold. Pure — given the current state of the two files. */
696
688
  declare const planDevVariablesScaffold: (input: {
697
689
  devVarsExists: boolean;
698
- exampleContent: string | undefined; /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
690
+ exampleContent: string | undefined;
691
+ /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
699
692
  randomHex?: (bytes: number) => string;
700
693
  }) => ScaffoldPlan;
701
694
  interface AugmentPlan {
@@ -707,22 +700,23 @@ interface AugmentPlan {
707
700
  missingKeys: string[];
708
701
  }
709
702
  /**
710
- * Plan how to top up an existing `.dev.vars` from the example: every example key
711
- * not already present becomes an appended line (secret placeholders filled with
712
- * fresh random hex, other values copied). Pure — no I/O. Empty `missingKeys`
713
- * means the file is already complete.
714
- */
703
+ * Plan how to top up an existing `.dev.vars` from the example: every example key
704
+ * not already present becomes an appended line (secret placeholders filled with
705
+ * fresh random hex, other values copied). Pure — no I/O. Empty `missingKeys`
706
+ * means the file is already complete.
707
+ */
715
708
  declare const planDevVariablesAugment: (input: {
716
709
  exampleContent: string;
717
- existingContent: string; /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
710
+ existingContent: string;
711
+ /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
718
712
  randomHex?: (bytes: number) => string;
719
713
  }) => AugmentPlan;
720
714
  interface EnsureDevVariablesDeps {
721
715
  /**
722
- * Ask the user to confirm generating the file. Return `true` to generate.
723
- * Consumers pass a TTY-aware prompt; in non-interactive contexts they should
724
- * resolve `false` (we then report `"declined"` and the caller can hint).
725
- */
716
+ * Ask the user to confirm generating the file. Return `true` to generate.
717
+ * Consumers pass a TTY-aware prompt; in non-interactive contexts they should
718
+ * resolve `false` (we then report `"declined"` and the caller can hint).
719
+ */
726
720
  confirm: (message: string) => Promise<boolean>;
727
721
  cwd: string;
728
722
  /** Emit a human-facing line (success / hint). */
@@ -741,44 +735,121 @@ interface EnsureDevVariablesResult {
741
735
  status: EnsureDevVariablesStatus;
742
736
  }
743
737
  /**
744
- * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
745
- *
746
- * - file missing → offer to generate it (secret placeholders auto-filled);
747
- * - file present but missing keys the example lists → offer to append them;
748
- * - file present and complete → nothing to do.
749
- *
750
- * Prompts via `confirm` (skipped when `yes`); never overwrites existing values.
751
- * Returns what happened so the caller can tailor any follow-up. Shared by
752
- * `lunora dev` and the `@lunora/vite` dev server. All side effects funnel
753
- * through `confirm`/`info`/`randomHex`.
754
- */
738
+ * Atomically (over)write a `.dev.vars`-shaped file: write the content to a
739
+ * sibling temp file with owner-only permissions (`mode: 0o600`), then
740
+ * `rename` it over the target. The rename is atomic within one filesystem,
741
+ * so a reader can never observe a half-written file, and an interrupt
742
+ * mid-write can't truncate the target — for a file holding secrets that
743
+ * would destroy every other local value alongside the new one, and the new
744
+ * value itself has no other recoverable copy if it was never disclosed
745
+ * anywhere else (Cloudflare secrets are write-only). On any failure the temp
746
+ * file is removed before the error propagates; never logs or throws the
747
+ * content. Exported so a caller writing to a `.dev.vars`-shaped path outside
748
+ * this module (`lunora deploy`'s minted-secret disclosure) reuses this
749
+ * instead of hand-rolling another copy of the pattern below.
750
+ */
751
+ declare const writeDevVariablesFileAtomically: (path: string, content: string) => void;
752
+ /**
753
+ * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
754
+ *
755
+ * - file missing → offer to generate it (secret placeholders auto-filled);
756
+ * - file present but missing keys the example lists → offer to append them;
757
+ * - file present and complete → nothing to do.
758
+ *
759
+ * Prompts via `confirm` (skipped when `yes`); never overwrites existing values.
760
+ * Returns what happened so the caller can tailor any follow-up. Shared by
761
+ * `lunora dev` and the `@lunora/vite` dev server. All side effects funnel
762
+ * through `confirm`/`info`/`randomHex`.
763
+ */
755
764
  declare const ensureDevVariables: (deps: EnsureDevVariablesDeps) => Promise<EnsureDevVariablesResult>;
756
765
  /**
757
- * Build the text that should be merged into `.dev.vars.example` for the given
758
- * set of package names. Only entries whose key is not already present in
759
- * `existingKeys` are included (additive / idempotent). Returns an empty string
760
- * when there is nothing to add.
761
- *
762
- * The output is grouped by package with a blank-line separator so the file
763
- * reads cleanly when multiple packages each contribute several keys.
764
- *
765
- * **Safety invariant:** this function never writes a real secret — every value
766
- * in the output is the entry's `placeholderValue`.
767
- */
766
+ * Build the text that should be merged into `.dev.vars.example` for the given
767
+ * set of package names. Only entries whose key is not already present in
768
+ * `existingKeys` are included (additive / idempotent). Returns an empty string
769
+ * when there is nothing to add.
770
+ *
771
+ * The output is grouped by package with a blank-line separator so the file
772
+ * reads cleanly when multiple packages each contribute several keys.
773
+ *
774
+ * **Safety invariant:** this function never writes a real secret — every value
775
+ * in the output is the entry's `placeholderValue`.
776
+ */
768
777
  declare const buildPackageSecretsBlock: (packageNames: ReadonlyArray<string>, existingKeys: ReadonlySet<string>) => string;
769
778
  /**
770
- * Write (or update) `.dev.vars.example` so that it contains the secrets
771
- * required by `packageNames`. Existing lines are never removed or rewritten;
772
- * new entries are appended (with a blank-line separator after existing content).
773
- *
774
- * Idempotent: re-running with the same `packageNames` does not duplicate keys
775
- * already in the file. Returns the list of keys that were actually appended.
776
- *
777
- * **Safety invariant:** only placeholder values are written — no real secrets.
778
- */
779
+ * Write (or update) `.dev.vars.example` so that it contains the secrets
780
+ * required by `packageNames`. Existing lines are never removed or rewritten;
781
+ * new entries are appended (with a blank-line separator after existing content).
782
+ *
783
+ * Idempotent: re-running with the same `packageNames` does not duplicate keys
784
+ * already in the file. Returns the list of keys that were actually appended.
785
+ *
786
+ * **Safety invariant:** only placeholder values are written — no real secrets.
787
+ */
779
788
  declare const ensureDevVariablesExample: (cwd: string, packageNames: ReadonlyArray<string>) => string[];
789
+ interface DevSecretsFillPlan {
790
+ /** {@link CORE_SECRETS} keys appended because they were absent (each generated). */
791
+ addedKeys: string[];
792
+ /** The full new file content to write. */
793
+ content: string;
794
+ /** Existing empty/placeholder secret-keyed entries filled with fresh values. */
795
+ filledKeys: string[];
796
+ }
797
+ /**
798
+ * Plan the in-place generation of dev secrets for a `.dev.vars`. First, every
799
+ * line whose KEY looks like a secret (`*_SECRET`, `*_TOKEN`, `*_KEY`,
800
+ * `*_PASSWORD`) and whose value is empty or a placeholder gets a freshly
801
+ * generated value — so a `lunora add`-scaffolded `.dev.vars` (which writes each
802
+ * secret blank) becomes usable on `lunora dev` / `vite dev` without the user
803
+ * running `openssl` by hand. Second, any {@link CORE_SECRETS} key absent from
804
+ * the file is appended (generated) — notably `LUNORA_ADMIN_TOKEN`, which the
805
+ * local Studio needs to call the worker's admin gate in dev (without it the
806
+ * Studio shows its login gate).
807
+ *
808
+ * Pure (given `randomHex`): real (non-placeholder) values are never touched, and
809
+ * comments + non-secret entries are preserved verbatim.
810
+ */
811
+ declare const planDevSecretsFill: (input: {
812
+ existingContent: string;
813
+ randomHex?: (bytes: number) => string;
814
+ }) => DevSecretsFillPlan;
815
+ interface FillDevSecretsResult {
816
+ /** Core secret keys appended (generated) because they were missing. */
817
+ addedKeys: string[];
818
+ /** Existing empty/placeholder secrets filled with generated values. */
819
+ filledKeys: string[];
820
+ /** `created` = no `.dev.vars` existed; `filled` = topped up an existing one; `unchanged` = nothing to do. */
821
+ status: "created" | "filled" | "unchanged";
822
+ }
823
+ /**
824
+ * Generate any missing/empty dev secrets in the project's `.dev.vars`, in place.
825
+ *
826
+ * Complements {@link ensureDevVariables} (which scaffolds `.dev.vars` from
827
+ * `.dev.vars.example`). A `lunora add`-scaffolded project writes secrets blank
828
+ * straight into `.dev.vars` (no example) and never includes `LUNORA_ADMIN_TOKEN`
829
+ * — so the worker boots with empty secrets and the Studio shows its login gate.
830
+ * This fills those gaps at dev startup, so both `lunora dev` and the
831
+ * `@lunora/vite` dev server give a working project with zero manual `openssl`.
832
+ *
833
+ * Never overwrites a real (non-placeholder) value. The write is atomic + owner-
834
+ * only (temp + rename, `mode: 0o600`), matching the other `.dev.vars` writers.
835
+ */
836
+ declare const fillDevSecrets: (deps: {
837
+ cwd: string;
838
+ info?: (message: string) => void;
839
+ randomHex?: (bytes: number) => string;
840
+ }) => FillDevSecretsResult;
841
+ /** Storage backends `.global()` accepts. A closed union, so it is never free text. */
842
+ type GlobalBackend = "d1" | "hyperdrive";
780
843
  /** Add a new table to `defineSchema({ ... })`. */
781
844
  interface AddTableEdit {
845
+ /**
846
+ * Mark the table `.global()`. Pass `{}` for the D1 default, or a backend for
847
+ * an external store — `lunora introspect` uses `{ backend: "hyperdrive" }`
848
+ * because the rows live in the database it read.
849
+ */
850
+ readonly global?: {
851
+ readonly backend?: GlobalBackend;
852
+ };
782
853
  readonly kind: "addTable";
783
854
  readonly table: string;
784
855
  }
@@ -788,11 +859,11 @@ interface AddOptionalColumnEdit {
788
859
  readonly kind: "addOptionalColumn";
789
860
  readonly table: string;
790
861
  /**
791
- * Inner validator expression text WITHOUT the `v.optional(...)` wrapper, e.g.
792
- * `v.string()`. Always wrapped in `v.optional(...)` on apply, because only
793
- * optional columns are additive-safe (a required column needs a backfill
794
- * migration).
795
- */
862
+ * Inner validator expression text WITHOUT the `v.optional(...)` wrapper, e.g.
863
+ * `v.string()`. Always wrapped in `v.optional(...)` on apply, because only
864
+ * optional columns are additive-safe (a required column needs a backfill
865
+ * migration).
866
+ */
796
867
  readonly validator: string;
797
868
  }
798
869
  /** Add a secondary index to an existing table. */
@@ -806,9 +877,9 @@ interface AddIndexEdit {
806
877
  /** Additive edits — the only requests {@link applyAdditiveEdit} applies. */
807
878
  type AdditiveEdit = AddIndexEdit | AddOptionalColumnEdit | AddTableEdit;
808
879
  /**
809
- * Destructive edits — never applied directly; routed to the migration handoff
810
- * (plan 024 Item 5). Carried as data so the editor can describe the request.
811
- */
880
+ * Destructive edits — never applied directly; routed to the migration handoff
881
+ * (plan 024 Item 5). Carried as data so the editor can describe the request.
882
+ */
812
883
  interface DestructiveEdit {
813
884
  readonly column?: string;
814
885
  readonly kind: "changeColumnType" | "dropColumn" | "dropTable" | "makeRequired" | "renameColumn";
@@ -819,9 +890,9 @@ interface DestructiveEdit {
819
890
  /** Any edit the editor can request. */
820
891
  type SchemaEdit = AdditiveEdit | DestructiveEdit;
821
892
  /**
822
- * Classify an edit request. Additive edits ({@link AdditiveEdit}) apply
823
- * directly; everything else changes stored data and is destructive.
824
- */
893
+ * Classify an edit request. Additive edits ({@link AdditiveEdit}) apply
894
+ * directly; everything else changes stored data and is destructive.
895
+ */
825
896
  declare const classifyEdit: (edit: SchemaEdit) => "additive" | "destructive";
826
897
  /** Failure reasons an additive edit can report. */
827
898
  type ApplyFailureReason = "aliased-define-schema" | "destructive" | "duplicate-column" | "duplicate-index" | "duplicate-table" | "invalid-identifier" | "invalid-validator" | "no-define-schema" | "non-object-argument" | "unknown-table";
@@ -834,10 +905,10 @@ type ApplyEditResult = {
834
905
  text: string;
835
906
  };
836
907
  /**
837
- * Apply an **additive** edit to a schema source string, preserving formatting.
838
- * Destructive edits are refused with `{ ok: false, reason: "destructive" }`;
839
- * route them through the migration handoff (plan 024 Item 5).
840
- */
908
+ * Apply an **additive** edit to a schema source string, preserving formatting.
909
+ * Destructive edits are refused with `{ ok: false, reason: "destructive" }`;
910
+ * route them through the migration handoff (plan 024 Item 5).
911
+ */
841
912
  declare const applyAdditiveEdit: (source: string, edit: SchemaEdit) => ApplyEditResult;
842
913
  /** A single declared column: its name and the raw validator expression text. */
843
914
  interface SchemaColumn {
@@ -876,40 +947,15 @@ type ParseSchemaResult = {
876
947
  tables: ReadonlyArray<SchemaTable>;
877
948
  };
878
949
  /**
879
- * Every `CallExpression` at or below a node. `getDescendantsOfKind` excludes the
880
- * node itself, but a table initializer often is the outermost call in the
881
- * `defineTable(...).global().index(...)` chain, so include it explicitly.
882
- */
883
-
884
- /**
885
- * Parse the tables (with typed columns + indexes) out of a `lunora/schema.ts`
886
- * source string. Returns a tagged result so callers can render a helpful
887
- * message per failure mode without throwing.
888
- */
950
+ * Parse the tables (with typed columns + indexes) out of a `lunora/schema.ts`
951
+ * source string. Returns a tagged result so callers can render a helpful
952
+ * message per failure mode without throwing.
953
+ */
889
954
  declare const parseSchema: (source: string) => ParseSchemaResult;
890
- interface SchemaInfo {
891
- /** Whether the lunora schema declares any `.global()` table. */
892
- hasGlobalTable: boolean;
893
- /** Names of vector indexes declared via `.vectorize()` / `defineVectorIndex()`. */
894
- vectorIndexNames?: ReadonlyArray<string>;
895
- }
896
- interface DiscoverSchemaInfoResult {
897
- /** Parse error message, when the schema exists but could not be analyzed. */
898
- error?: string;
899
- /** Schema facts, or `undefined` when no `schema.ts` exists or parsing failed. */
900
- info: SchemaInfo | undefined;
901
- }
902
- /**
903
- * Discover {@link SchemaInfo} for a project. Returns `{ info: undefined }` when
904
- * the project declares no `schema.ts` (not an error), or `{ info: undefined,
905
- * error }` when a present schema could not be parsed — callers decide whether a
906
- * parse failure is a warning (validator) or simply ignorable (inference).
907
- */
908
- declare const discoverSchemaInfo: (projectRoot: string, schemaDirectory: string) => DiscoverSchemaInfoResult;
909
955
  /**
910
- * A badge: the short colored label that prefixes a line. `bg`/`fg` are hex so the
911
- * same value drives both colorize's `bgHex().hex()` and the tui `&lt;Text>` props.
912
- */
956
+ * A badge: the short colored label that prefixes a line. `bg`/`fg` are hex so the
957
+ * same value drives both colorize's `bgHex().hex()` and the tui `&lt;Text>` props.
958
+ */
913
959
  interface BadgeSpec {
914
960
  bg: `#${string}`;
915
961
  fg: `#${string}`;
@@ -925,24 +971,24 @@ type BadgeName = LevelBadgeName | StepBadgeName;
925
971
  /** The ordered step-phase names, used to register custom pail log types. */
926
972
  declare const STEP_BADGE_NAMES: ReadonlyArray<StepBadgeName>;
927
973
  /**
928
- * Every badge, keyed by name. Levels get their conventional colors (red/amber/
929
- * green/blue/grey); step phases follow create-astro's green→purple→cyan rhythm.
930
- */
974
+ * Every badge, keyed by name. Levels get their conventional colors (red/amber/
975
+ * green/blue/grey); step phases follow create-astro's green→purple→cyan rhythm.
976
+ */
931
977
  declare const BADGES: Record<BadgeName, BadgeSpec>;
932
978
  /**
933
- * Luna, the mascot: the folklore rabbit-in-the-moon — a bunny tucked inside the
934
- * moon disc. Pure ASCII so it renders the same everywhere (including piped logs).
935
- * The CLI signs off the `init` flow with it, the way create-astro closes with
936
- * Houston.
937
- */
979
+ * Luna, the mascot: the folklore rabbit-in-the-moon — a bunny tucked inside the
980
+ * moon disc. Pure ASCII so it renders the same everywhere (including piped logs).
981
+ * The CLI signs off the `init` flow with it, the way create-astro closes with
982
+ * Houston.
983
+ */
938
984
  declare const LUNA_NAME = "Luna";
939
985
  declare const LUNA_SIGNOFF = "Safe travels, voyager.";
940
986
  declare const LUNA_BUNNY: string;
941
987
  /**
942
- * {@link LUNA_BUNNY} with its leading newline stripped, ready to render inline
943
- * (beside the name + sign-off). Both render paths — the tui mascot frame and the
944
- * pail off-TTY fallback — use this so neither re-implements the strip.
945
- */
988
+ * {@link LUNA_BUNNY} with its leading newline stripped, ready to render inline
989
+ * (beside the name + sign-off). Both render paths — the tui mascot frame and the
990
+ * pail off-TTY fallback — use this so neither re-implements the strip.
991
+ */
946
992
  declare const LUNA_ART: string;
947
993
  /** The colored part of a badge — the word with one space of padding each side. */
948
994
  declare const padBadge: (text: string) => string;
@@ -956,185 +1002,4 @@ declare const badgeWidth: (_spec: BadgeSpec) => number;
956
1002
  declare const paintBadge: (spec: BadgeSpec) => string;
957
1003
  /** Dim continuation text (a step's chosen answer, shown under the question). */
958
1004
  declare const paintAnswer: (text: string) => string;
959
- /** Candidate wrangler config filenames, in the order every consumer probes them. */
960
- declare const WRANGLER_FILES: readonly ["wrangler.jsonc", "wrangler.json"];
961
- /** Locate the project's wrangler config, or `undefined` when none exists. */
962
- declare const findWranglerFile: (projectRoot: string) => string | undefined;
963
- interface ReadWranglerResult<T> {
964
- /** Parsed config, or `undefined` when the file was not valid JSONC. */
965
- parsed: T | undefined;
966
- /** Raw file text — needed for comment-preserving `modify`/`applyEdits`. */
967
- text: string;
968
- }
969
- /**
970
- * Read and JSONC-parse a wrangler config file. Returns the raw `text` (for
971
- * structural edits) alongside `parsed`, which is `undefined` when the file is
972
- * not valid JSONC or does not parse to an object. Allows trailing commas, as
973
- * wrangler does.
974
- */
975
- declare const readWranglerJsonc: <T = unknown>(wranglerPath: string) => ReadWranglerResult<T>;
976
- declare const REQUIRED_COMPATIBILITY_DATE: string;
977
- declare const REQUIRED_FLAG: string;
978
- interface WranglerDurableObjectBinding {
979
- class_name?: string;
980
- name?: string;
981
- }
982
- /**
983
- * A `tail_consumers` entry: a Worker that receives this Worker's tail events
984
- * (logs, exceptions, fetch metadata) for forwarding to an external sink. See
985
- * `withTailConsumer` for the wiring helper.
986
- */
987
- interface TailConsumer {
988
- /** Optional Cloudflare environment of the consumer Worker. */
989
- environment?: string;
990
- /** Name of the Worker that consumes tail events. */
991
- service?: string;
992
- }
993
- /** A wrangler `containers[]` entry (parsed from untrusted JSONC). */
994
- interface WranglerContainerEntry {
995
- class_name?: string;
996
- image?: string;
997
- instance_type?: string | {
998
- disk_mb?: number;
999
- memory_mib?: number;
1000
- vcpu?: number;
1001
- };
1002
- max_instances?: number;
1003
- }
1004
- /**
1005
- * A wrangler `workflows[]` entry (parsed from untrusted JSONC). Unlike
1006
- * containers, workflows are NOT Durable Objects — the entry stands alone (no
1007
- * `durable_objects` binding, no migration class).
1008
- */
1009
- interface WranglerWorkflowEntry {
1010
- binding?: string;
1011
- class_name?: string;
1012
- name?: string;
1013
- }
1014
- interface WranglerConfig {
1015
- analytics_engine_datasets?: ReadonlyArray<{
1016
- binding?: string;
1017
- dataset?: string;
1018
- } | null | undefined>;
1019
- assets?: {
1020
- binding?: string;
1021
- directory?: string;
1022
- html_handling?: string;
1023
- not_found_handling?: string;
1024
- };
1025
- browser?: {
1026
- binding?: string;
1027
- };
1028
- compatibility_date?: string;
1029
- compatibility_flags?: ReadonlyArray<string>;
1030
- containers?: ReadonlyArray<WranglerContainerEntry | null | undefined>;
1031
- d1_databases?: ReadonlyArray<{
1032
- binding?: string;
1033
- }>;
1034
- dispatch_namespaces?: ReadonlyArray<{
1035
- binding?: string;
1036
- namespace?: string;
1037
- outbound?: unknown;
1038
- } | null | undefined>;
1039
- durable_objects?: {
1040
- bindings?: ReadonlyArray<WranglerDurableObjectBinding>;
1041
- };
1042
- hyperdrive?: ReadonlyArray<{
1043
- binding?: string;
1044
- id?: string;
1045
- localConnectionString?: string;
1046
- } | null | undefined>;
1047
- images?: {
1048
- binding?: string;
1049
- };
1050
- kv_namespaces?: ReadonlyArray<{
1051
- binding?: string;
1052
- id?: string;
1053
- } | null | undefined>;
1054
- logpush?: boolean;
1055
- migrations?: ReadonlyArray<{
1056
- new_classes?: ReadonlyArray<string>;
1057
- new_sqlite_classes?: ReadonlyArray<string>;
1058
- } | null | undefined>;
1059
- mtls_certificates?: ReadonlyArray<{
1060
- binding?: string;
1061
- certificate_id?: string;
1062
- } | null | undefined>;
1063
- observability?: {
1064
- enabled?: boolean;
1065
- head_sampling_rate?: number;
1066
- logs?: {
1067
- enabled?: boolean;
1068
- head_sampling_rate?: number;
1069
- };
1070
- };
1071
- pipelines?: ReadonlyArray<{
1072
- binding?: string;
1073
- pipeline?: string;
1074
- } | null | undefined>;
1075
- placement?: {
1076
- mode?: string;
1077
- };
1078
- r2_buckets?: ReadonlyArray<{
1079
- binding?: string;
1080
- }>;
1081
- send_email?: ReadonlyArray<{
1082
- allowed_destination_addresses?: ReadonlyArray<string>;
1083
- destination_address?: string;
1084
- name?: string;
1085
- } | null | undefined>;
1086
- services?: ReadonlyArray<{
1087
- binding?: string;
1088
- entrypoint?: string;
1089
- environment?: string;
1090
- service?: string;
1091
- } | null | undefined>;
1092
- tail_consumers?: ReadonlyArray<TailConsumer | null | undefined>;
1093
- vars?: Record<string, unknown>;
1094
- vectorize?: ReadonlyArray<{
1095
- binding?: string;
1096
- index_name?: string;
1097
- } | null | undefined>;
1098
- workflows?: ReadonlyArray<WranglerWorkflowEntry | null | undefined>;
1099
- }
1100
- interface WranglerValidationReport {
1101
- errors: string[];
1102
- valid: boolean;
1103
- warnings: string[];
1104
- }
1105
- /**
1106
- * Return a new `WranglerConfig` with `consumer` present in `tail_consumers`,
1107
- * wiring this Worker to forward its tail events (logs/exceptions) to another
1108
- * Worker that fans them out to an external sink. Pure and idempotent: an
1109
- * existing entry with the same `service` + `environment` is left untouched
1110
- * rather than duplicated, so it is safe to call on every codegen/deploy.
1111
- */
1112
- declare const withTailConsumer: (wrangler: WranglerConfig, consumer: TailConsumer) => WranglerConfig;
1113
- /**
1114
- * Pure validator: given a parsed `WranglerConfig` object and an optional
1115
- * `SchemaInfo`, produce a structured report. Performs no I/O.
1116
- */
1117
- declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, schema?: SchemaInfo) => WranglerValidationReport;
1118
- /**
1119
- * Convenience alias matching the original task-spec signature
1120
- * `validateWrangler(wranglerJson, schema)` returning
1121
- * `{ valid, errors, warnings }`.
1122
- */
1123
- declare const validateWrangler: typeof validateWranglerConfig;
1124
- interface WranglerProjectValidationOptions {
1125
- projectRoot: string;
1126
- schemaDir?: string;
1127
- }
1128
- interface WranglerProjectValidationResult {
1129
- problems: ReadonlyArray<string>;
1130
- report: WranglerValidationReport;
1131
- wranglerPath: string | undefined;
1132
- }
1133
- /**
1134
- * File-system aware variant: reads `wrangler.jsonc`/`wrangler.json` from
1135
- * the given project root, discovers the schema (if any), and delegates to
1136
- * `validateWranglerConfig`. Returns the legacy
1137
- * `{ problems, wranglerPath }` shape plus the structured `report`.
1138
- */
1139
- declare const validateWranglerProject: (options: WranglerProjectValidationOptions) => WranglerProjectValidationResult;
1140
- export { ACCENT, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DestructiveEdit, type DetectedFramework, type DiscoverContainerInfoResult, type DiscoverSchemaInfoResult, type DiscoverWorkflowInfoResult, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type ExportGap, type FrameworkClass, type FrameworkDetection, type InferOptions, type InferredBindings, type InferredContainer, type InferredWorkflow, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MaterializeOptions, type MaterializeResult, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, REMOTE_ELIGIBLE_KEYS, REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, ROOT_SKILL_NAME, type ReadWranglerResult, type ReconcileBindingsResult, type RemoteBindingPlan, type RemoteEnableInputs, type RemotePreference, type RemoteWranglerShape, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaInfo, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, type TailConsumer, WRANGLER_FILES, type WranglerConfig, type WranglerContainerEntry, type WranglerProjectValidationOptions, type WranglerProjectValidationResult, type WranglerValidationReport, type WranglerWorkflowEntry, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, classifyEdit, createConfirm, detectAgentRules, detectFramework, discoverContainerInfo, discoverSchemaInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, findWranglerFile, formatLunoraEvent, inferLunoraBindings, injectRemoteFlags, interpretRemote, isInteractive, isPlaceholderValue, isRemoteEnvEnabled, materializeRemoteWranglerConfig, packageNamesFromBindings, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevVariablesAugment, planDevVariablesScaffold, planRemoteBindings, promptMultiSelect, promptSelect, promptYesNo, readLinkedProject, readProjectRemotePreference, readWranglerJsonc, reconcileWranglerBindings, resolveRemoteEnabled, secretsForPackages, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer, writeLinkedProject };
1005
+ export { ACCENT, AGENT_MODE_ENV, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentDetection, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DeployDriver, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DevServerMode, type DevServerState, type DiscoverAgentInfoResult, type DiscoverContainerInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type FillDevSecretsResult, type FrameworkClass, type FrameworkDetection, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, escapeRegExp, fillDevSecrets, formatLunoraEvent, generateSecretValue, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, secretsForPackages, streamContainerLogs, updateDevServerState, upsertDevVariableLine, writeDevServerState, writeDevVariablesFileAtomically, writeLinkedProject };