@lunora/config 1.0.0-alpha.26 → 1.0.0-alpha.261

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 (100) hide show
  1. package/LICENSE.md +135 -0
  2. package/README.md +3 -3
  3. package/dist/cloudflare/index.d.mts +969 -0
  4. package/dist/cloudflare/index.d.ts +969 -0
  5. package/dist/cloudflare/index.mjs +1 -0
  6. package/dist/index.d.mts +864 -921
  7. package/dist/index.d.ts +864 -921
  8. package/dist/index.mjs +1 -23
  9. package/dist/packem_shared/ACCENT-CotPTfv7.mjs +7 -0
  10. package/dist/packem_shared/AGENT_MODE_ENV-D8MvSwPE.mjs +1 -0
  11. package/dist/packem_shared/AGENT_RULES_DIR-C54Ta_vz.mjs +1 -0
  12. package/dist/packem_shared/ALLOW_FORWARDED_ENV-BUndghJx.mjs +1 -0
  13. package/dist/packem_shared/BINDING_MANIFEST_VERSION-B8dwEKLn.mjs +1 -0
  14. package/dist/packem_shared/CLOUDFLARE_DRIVER-Ci9oJMYb.mjs +1 -0
  15. package/dist/packem_shared/CODEGEN_ENV-ZsCxV1kP.mjs +1 -0
  16. package/dist/packem_shared/COMPOSED_WORKER_ENTRY-Bj793dir.mjs +1 -0
  17. package/dist/packem_shared/DEFAULT_DEPLOY_TARGET-B-e9mqgX.mjs +1 -0
  18. package/dist/packem_shared/DEV_BINDINGS_FILE-pyOiNLP4.mjs +4 -0
  19. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-CqLorAxT.mjs +6 -0
  20. package/dist/packem_shared/LINKED_PROJECT_DIR-Ba5qPEwo.mjs +2 -0
  21. package/dist/packem_shared/LUNORA_EVENT_SOURCE-DYdcDU9G.mjs +1 -0
  22. package/dist/packem_shared/LUNORA_IGNORED_PATHS-BHABA9u-.mjs +19 -0
  23. package/dist/packem_shared/LunoraReporter-f277GLNT.mjs +6 -0
  24. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-De7gLGIy.mjs +1 -0
  25. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-0QiGKazX.mjs +1 -0
  26. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-D-edJjVY.mjs +1 -0
  27. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-SdRHaUwh.mjs +1 -0
  28. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-Dbt0faMd.mjs +1 -0
  29. package/dist/packem_shared/SEED_ENDPOINT-2B56PN_H.mjs +1 -0
  30. package/dist/packem_shared/WORKERS_CACHE_MIN_DATE-0k9ABJtF.mjs +1 -0
  31. package/dist/packem_shared/WRANGLER_FILES-UNW-xY5u.mjs +1 -0
  32. package/dist/packem_shared/addArgsFor-CWb4cPij.mjs +1 -0
  33. package/dist/packem_shared/applyAdditiveEdit-CH0VhMJ2.mjs +6 -0
  34. package/dist/packem_shared/applyStudioAssetCache-DHQEJuuv.mjs +1 -0
  35. package/dist/packem_shared/assetContentType-CUb5bpbI.mjs +1 -0
  36. package/dist/packem_shared/buildPackageSecretsBlock-yv9mjKEW.mjs +19 -0
  37. package/dist/packem_shared/classifyPolicyEdit-D8NwrHxR.mjs +24 -0
  38. package/dist/packem_shared/collectExportGaps-BJw9LdRV.mjs +2 -0
  39. package/dist/packem_shared/collectWranglerSecretVariables-D-usPfT8.mjs +1 -0
  40. package/dist/packem_shared/createConfirm-D5mKTMHj.mjs +7 -0
  41. package/dist/packem_shared/describePreservedCrons-Ci5r2aph.mjs +1 -0
  42. package/dist/packem_shared/detectFramework-CUiVEHDQ.mjs +1 -0
  43. package/dist/packem_shared/discover-info-Vj9d4qYt.mjs +1 -0
  44. package/dist/packem_shared/discoverAgentInfo-CTdCSfYM.mjs +1 -0
  45. package/dist/packem_shared/discoverContainerInfo-EhZtEBQs.mjs +1 -0
  46. package/dist/packem_shared/discoverSchemaInfo-DJvevsCm.mjs +1 -0
  47. package/dist/packem_shared/discoverWorkflowInfo-BsPvlo6O.mjs +1 -0
  48. package/dist/packem_shared/interpretRemote-CQ06qpjM.mjs +1 -0
  49. package/dist/packem_shared/jsonc-edit-BZgQIdAH.mjs +1 -0
  50. package/dist/packem_shared/lunora-manifest-V77OcyFC.mjs +6 -0
  51. package/dist/packem_shared/package-secrets-registry-DvtTiK4b.mjs +1 -0
  52. package/dist/packem_shared/parseDevVariable-teAAahVD.mjs +1 -0
  53. package/dist/packem_shared/parseSchema-D1Wbr1qv.mjs +1 -0
  54. package/dist/packem_shared/policy-scaffold.d-D2H4_G01.d.mts +86 -0
  55. package/dist/packem_shared/policy-scaffold.d-D2H4_G01.d.ts +86 -0
  56. package/dist/packem_shared/reconcileWranglerCompatibilityDate-7OO_xaKR.mjs +1 -0
  57. package/dist/packem_shared/renderStudioHtml-ByowDtMl.mjs +15 -0
  58. package/dist/packem_shared/runPostCodegenHook-CVrOTiGB.mjs +1 -0
  59. package/dist/packem_shared/schema-info.d-DvKkJ7HJ.d.mts +331 -0
  60. package/dist/packem_shared/schema-info.d-DvKkJ7HJ.d.ts +331 -0
  61. package/dist/packem_shared/serveJsonHandler-Dv0EdCb9.mjs +1 -0
  62. package/dist/packem_shared/streamContainerLogs-0nko1qpv.mjs +2 -0
  63. package/dist/packem_shared/wranglerToAlchemy-SDoS_XY0.mjs +6 -0
  64. package/dist/packem_shared/write-atomic-9d9xs5ON.mjs +1 -0
  65. package/dist/studio-host/index.d.mts +250 -95
  66. package/dist/studio-host/index.d.ts +250 -95
  67. package/dist/studio-host/index.mjs +1 -7
  68. package/package.json +15 -7
  69. package/dist/packem_shared/ACCENT-DW1XJn8i.mjs +0 -40
  70. package/dist/packem_shared/AGENT_RULES_DIR-lcgC08aE.mjs +0 -40
  71. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs +0 -37
  72. package/dist/packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs +0 -52
  73. package/dist/packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs +0 -34
  74. package/dist/packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs +0 -86
  75. package/dist/packem_shared/LunoraReporter-Ci-bDCK9.mjs +0 -70
  76. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs +0 -70
  77. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-CiC2IGKx.mjs +0 -103
  78. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs +0 -105
  79. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-2YYyWJUq.mjs +0 -561
  80. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs +0 -99
  81. package/dist/packem_shared/SEED_ENDPOINT-DVCjaGO-.mjs +0 -61
  82. package/dist/packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs +0 -25
  83. package/dist/packem_shared/applyAdditiveEdit-C-snTFEV.mjs +0 -228
  84. package/dist/packem_shared/buildPackageSecretsBlock-DWDKHViT.mjs +0 -243
  85. package/dist/packem_shared/classifyPolicyEdit-BHeAqF8P.mjs +0 -99
  86. package/dist/packem_shared/createConfirm-fvpdgJ9s.mjs +0 -100
  87. package/dist/packem_shared/detectFramework-Br-BcPBq.mjs +0 -41
  88. package/dist/packem_shared/discoverContainerInfo-BXFs6Wav.mjs +0 -19
  89. package/dist/packem_shared/discoverSchemaInfo-BB-CKlTK.mjs +0 -25
  90. package/dist/packem_shared/discoverWorkflowInfo-CedvR0mn.mjs +0 -19
  91. package/dist/packem_shared/inferLunoraBindings-ByMwG-gH.mjs +0 -352
  92. package/dist/packem_shared/loadStudioAssets-Csk5RS4E.mjs +0 -28
  93. package/dist/packem_shared/parseDevVariable-CJiq2IwE.mjs +0 -30
  94. package/dist/packem_shared/parseSchema-DSeyktvG.mjs +0 -107
  95. package/dist/packem_shared/policy-scaffold.d-DCmwn7zQ.d.mts +0 -74
  96. package/dist/packem_shared/policy-scaffold.d-DCmwn7zQ.d.ts +0 -74
  97. package/dist/packem_shared/reconcileWranglerBindings-Cruwwx1M.mjs +0 -334
  98. package/dist/packem_shared/renderStudioHtml-449Ysn75.mjs +0 -37
  99. package/dist/packem_shared/serveJsonHandler-B4OLTGLS.mjs +0 -86
  100. package/dist/packem_shared/streamContainerLogs-BZ4cOZwH.mjs +0 -157
package/dist/index.d.ts CHANGED
@@ -1,76 +1,154 @@
1
- import { ContainerIR, WorkflowIR, QueueIR } 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
3
  import { Writable } from 'node:stream';
4
- import 'ts-morph';
5
- 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.js";
4
+ import { D as DeployDriver } from "./packem_shared/schema-info.d-DvKkJ7HJ.js";
5
+ export { C as COMPOSED_WORKER_ENTRY, type a as DeployRequest, type b as DevRequest, type c as DiscoverSchemaInfoResult, type d as DriverToolchain, e as GENERATED_CLASS_MODULES, type G as GeneratedClassModule, type f as InferOptions, type g as InferredAgent, type I as InferredBindings, type h as InferredContainer, type i as InferredWorkflow, type S as SchemaInfo, type j as SecretRequest, type T as TailRequest, type k as ToolchainCommand, l as discoverSchemaInfo, m as inferLunoraBindings, p as packageNamesFromBindings } from "./packem_shared/schema-info.d-DvKkJ7HJ.js";
6
+ 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, r as resolveServerModule, s as scaffoldPolicyFile, w as wireRlsIntoProcedure } from "./packem_shared/policy-scaffold.d-D2H4_G01.js";
7
+ interface DiscoverAgentInfoResult {
8
+ /** Discovered agent definitions; `[]` when none are declared or parsing failed. */
9
+ agents: ReadonlyArray<AgentIR>;
10
+ /** Parse error message, when `lunora/agents.ts` exists but could not be analyzed. */
11
+ error?: string;
12
+ }
13
+ /**
14
+ * Discover the project's `defineAgent` declarations. Returns `{ agents: [] }`
15
+ * when the project has no `lunora/agents.ts` (not an error), or
16
+ * `{ agents: [], error }` when the file exists but could not be parsed — callers
17
+ * decide whether that is a warning (validator) or ignorable (inference).
18
+ */
19
+ declare const discoverAgentInfo: (projectRoot: string, schemaDirectory: string) => DiscoverAgentInfoResult;
20
+ interface DiscoverContainerInfoResult {
21
+ /** Discovered container definitions; `[]` when none are declared or parsing failed. */
22
+ containers: ReadonlyArray<ContainerIR>;
23
+ /** Parse error message, when `lunora/containers.ts` exists but could not be analyzed. */
24
+ error?: string;
25
+ }
26
+ /**
27
+ * Discover the project's `defineContainer` declarations. Returns
28
+ * `{ containers: [] }` when the project has no `lunora/containers.ts` (not an
29
+ * error), or `{ containers: [], error }` when the file exists but could not be
30
+ * parsed — callers decide whether that is a warning (validator) or ignorable
31
+ * (inference).
32
+ */
33
+ declare const discoverContainerInfo: (projectRoot: string, schemaDirectory: string) => DiscoverContainerInfoResult;
34
+ interface DiscoverWorkflowInfoResult {
35
+ /** Parse error message, when `lunora/workflows.ts` exists but could not be analyzed. */
36
+ error?: string;
37
+ /** Discovered workflow definitions; `[]` when none are declared or parsing failed. */
38
+ workflows: ReadonlyArray<WorkflowIR>;
39
+ }
6
40
  /**
7
- * Project-relative directory the Lunora agent skills ("rules") install into.
8
- * This is the portable [Agent Skills](https://tanstack.com/intent/latest/docs/registry)
9
- * location — Cursor, Claude Code, and GitHub Copilot all discover skills here.
10
- */
41
+ * Discover the project's `defineWorkflow` declarations. Returns
42
+ * `{ workflows: [] }` when the project has no `lunora/workflows.ts` (not an
43
+ * error), or `{ workflows: [], error }` when the file exists but could not be
44
+ * parsed — callers decide whether that is a warning (validator) or ignorable
45
+ * (inference).
46
+ */
47
+ declare const discoverWorkflowInfo: (projectRoot: string, schemaDirectory: string) => DiscoverWorkflowInfoResult;
48
+ /** Minimal env shape — `process.env` structurally, injectable for tests. */
49
+ type EnvLike = Readonly<Record<string, string | undefined>>;
50
+ /** One detected agent: which tool, and which env var gave it away. */
51
+ interface AgentDetection {
52
+ /** Human-readable agent name, for the "agent detected" log line. */
53
+ name: string;
54
+ /** The environment variable that matched. */
55
+ variable: string;
56
+ }
57
+ /** Env var that forces agent mode on (`1`/`true`) or off (`0`/`false`), overriding detection. */
58
+ declare const AGENT_MODE_ENV = "LUNORA_AGENT_MODE";
59
+ /**
60
+ * Detect the AI agent driving this process, or `undefined` when none is.
61
+ * `LUNORA_AGENT_MODE` wins over `@visulima/find-ai-runner`'s marker table in
62
+ * both directions. Pure — pass a custom `env` in tests.
63
+ */
64
+ declare const detectAiAgent: (env?: EnvLike) => AgentDetection | undefined;
65
+ /**
66
+ * Project-relative directory the Lunora agent skills ("rules") install into.
67
+ * This is the portable [Agent Skills](https://tanstack.com/intent/latest/docs/registry)
68
+ * location — Cursor, Claude Code, and GitHub Copilot all discover skills here.
69
+ */
11
70
  declare const AGENT_RULES_DIR = ".agents/skills";
12
71
  /** Env var the once-per-process-tree hint guard ({@link claimAgentRulesHint}) sets. */
13
72
  declare const AGENT_RULES_HINT_ENV = "LUNORA_RULES_HINT_SHOWN";
14
73
  /**
15
- * The Lunora agent skills shipped by `@lunora/cli`. The first entry (`lunora`)
16
- * is the router skill — its presence is what {@link detectAgentRules} treats as
17
- * "rules installed", since every other skill is reachable through it.
18
- */
74
+ * The Lunora agent skills shipped by `@lunora/cli`. The first entry (`lunora`)
75
+ * is the router skill — its presence is what {@link detectAgentRules} treats as
76
+ * "rules installed", since every other skill is reachable through it.
77
+ *
78
+ * `lunora rules install` enumerates `@lunora/cli`'s `skills/` directory rather
79
+ * than reading this list, so a skill missing here still installs — but
80
+ * `lunora rules check` counts against it, and reported "9/9 skills" with an
81
+ * empty "Missing:" line for a project holding all 14. The CLI's rules test
82
+ * pins the two together, since this package cannot see that directory.
83
+ */
19
84
  declare const LUNORA_SKILL_NAMES: ReadonlyArray<string>;
20
85
  /** The router skill whose presence marks the rule set as installed. */
21
86
  declare const ROOT_SKILL_NAME = "lunora";
22
87
  /**
23
- * The single "rules not installed" message shared by every surface (the CLI
24
- * `lunora dev` summary and the Vite dev plugin), so the wording and the
25
- * pointer at `lunora rules install` stay identical wherever it appears.
26
- */
88
+ * The single "rules not installed" message shared by every surface (the CLI
89
+ * `lunora dev` summary and the Vite dev plugin), so the wording and the
90
+ * pointer at `lunora rules install` stay identical wherever it appears.
91
+ */
27
92
  declare const AGENT_RULES_HINT = "Lunora AI rules not installed — run `lunora rules install` so your coding agent knows how to use Lunora.";
28
93
  /**
29
- * Process-tree guard so the hint is emitted at most once. The first surface to
30
- * print it sets {@link AGENT_RULES_HINT_ENV} on `process.env`; later surfaces (a
31
- * Vite dev-server restart, or a child process that inherited the env) read it
32
- * and stay quiet. Returns `true` the first time, `false` afterwards.
33
- */
94
+ * Process-tree guard so the hint is emitted at most once. The first surface to
95
+ * print it sets {@link AGENT_RULES_HINT_ENV} on `process.env`; later surfaces (a
96
+ * Vite dev-server restart, or a child process that inherited the env) read it
97
+ * and stay quiet. Returns `true` the first time, `false` afterwards.
98
+ */
34
99
  declare const claimAgentRulesHint: () => boolean;
35
100
  interface AgentRulesStatus {
36
101
  /**
37
- * True when the `lunora` router skill is installed. We key on the router
38
- * (not "all nine present") so a project that intentionally trims the set
39
- * still counts as installed and isn't nagged.
40
- */
102
+ * True when the `lunora` router skill is installed. We key on the router
103
+ * (not "all nine present") so a project that intentionally trims the set
104
+ * still counts as installed and isn't nagged.
105
+ */
41
106
  readonly installed: boolean;
42
- /** Skill names with no `SKILL.md` under `&lt;root>/.agents/skills/&lt;name>/`. */
107
+ /** Skill names with no `SKILL.md` under `<root>/.agents/skills/<name>/`. */
43
108
  readonly missing: ReadonlyArray<string>;
44
- /** Skill names found under `&lt;root>/.agents/skills/&lt;name>/SKILL.md`. */
109
+ /** Skill names found under `<root>/.agents/skills/<name>/SKILL.md`. */
45
110
  readonly present: ReadonlyArray<string>;
46
111
  }
47
112
  /**
48
- * Detect whether the Lunora agent skills are installed in `projectRoot` by
49
- * checking the skills folder for each `SKILL.md`. Pure filesystem reads, safe to
50
- * call on every dev-server / CLI startup — the CLI, the Vite plugin, and the
51
- * studio host all use it to decide whether to surface the "rules not installed"
52
- * hint.
53
- */
113
+ * Detect whether the Lunora agent skills are installed in `projectRoot` by
114
+ * checking the skills folder for each `SKILL.md`. Pure filesystem reads, safe to
115
+ * call on every dev-server / CLI startup — the CLI, the Vite plugin, and the
116
+ * studio host all use it to decide whether to surface the "rules not installed"
117
+ * hint.
118
+ */
54
119
  declare const detectAgentRules: (projectRoot: string) => AgentRulesStatus;
55
- interface DiscoverContainerInfoResult {
56
- /** Discovered container definitions; `[]` when none are declared or parsing failed. */
57
- containers: ReadonlyArray<ContainerIR>;
58
- /** Parse error message, when `lunora/containers.ts` exists but could not be analyzed. */
59
- error?: string;
60
- }
61
120
  /**
62
- * Discover the project's `defineContainer` declarations. Returns
63
- * `{ containers: [] }` when the project has no `lunora/containers.ts` (not an
64
- * error), or `{ containers: [], error }` when the file exists but could not be
65
- * parsed — callers decide whether that is a warning (validator) or ignorable
66
- * (inference).
67
- */
68
- declare const discoverContainerInfo: (projectRoot: string, schemaDirectory: string) => DiscoverContainerInfoResult;
121
+ * The environment switch that turns codegen off — the other spelling of
122
+ * `lunora dev --no-codegen`.
123
+ *
124
+ * The flag alone could not reach the Vite flavors: there the codegen watch lives
125
+ * in `@lunora/vite`, inside the framework dev server the CLI spawns as a separate
126
+ * process, which re-parses its own argv and never sees it. So the flag travels as
127
+ * this variable, and both the plugin and the CLI's own watcher read it — one
128
+ * switch, one meaning, every flavor.
129
+ *
130
+ * It is a DEV switch. `vite build` ignores it deliberately: skipping generation
131
+ * there would also skip the ERROR-level advisory gate that fails the build.
132
+ *
133
+ * One-directional, like `LUNORA_REMOTE`: it can turn codegen off, nothing turns
134
+ * it back on. Settable by hand for a project that runs its framework dev script
135
+ * directly rather than through `lunora dev`.
136
+ */
137
+ /** Env var that turns codegen off when it spells `0` / `false`. Unset means on. */
138
+ declare const CODEGEN_ENV = "LUNORA_CODEGEN";
139
+ /**
140
+ * Whether a {@link CODEGEN_ENV} value spells an explicit "off".
141
+ *
142
+ * Takes the VALUE, not the environment, mirroring `isRemoteEnvEnabled` in
143
+ * `./cloudflare/remote-bindings` — the same package's existing answer to the same
144
+ * question, with the opposite polarity.
145
+ */
146
+ declare const isCodegenDisabled: (value: string | undefined) => boolean;
69
147
  /** Severity a container output line is surfaced at: `stderr` → `error`, `stdout` → `info`. */
70
148
  type ContainerLogLevel = "error" | "info";
71
149
  /** One declared container to follow, identified by the names codegen lifts from `defineContainer`. */
72
150
  interface ContainerLogSource {
73
- /** Generated Durable Object class name, e.g. `TranscoderContainer`. Wrangler's dev image is `cloudflare-dev/&lt;lowercased>`. */
151
+ /** Generated Durable Object class name, e.g. `TranscoderContainer`. Wrangler's dev image is `cloudflare-dev/<lowercased>`. */
74
152
  className: string;
75
153
  /** The `lunora/containers.ts` export name, e.g. `transcoder` — used as the display tag. */
76
154
  exportName: string;
@@ -130,25 +208,25 @@ interface DockerLike {
130
208
  };
131
209
  }
132
210
  /**
133
- * Follow the local Docker logs of every declared container, emitting each output
134
- * line through `onLine` tagged with its export name. Polls for containers (they
135
- * start lazily on first request and may be replaced on restart), attaches once
136
- * per container id, and drops streams whose container has gone. Returns
137
- * immediately with a `close()` handle; all work happens asynchronously.
138
- */
211
+ * Follow the local Docker logs of every declared container, emitting each output
212
+ * line through `onLine` tagged with its export name. Polls for containers (they
213
+ * start lazily on first request and may be replaced on restart), attaches once
214
+ * per container id, and drops streams whose container has gone. Returns
215
+ * immediately with a `close()` handle; all work happens asynchronously.
216
+ */
139
217
  declare const streamContainerLogs: (options: ContainerLogStreamOptions) => ContainerLogStreamHandle;
140
218
  /**
141
- * The meta-frameworks Lunora can compose with, plus `"none"` for a standalone
142
- * SPA / SSR-less project (the current default). Mirrors PLAN4 §2.4.
143
- */
144
- type DetectedFramework = "astro" | "none" | "nuxt" | "react-router" | "solid-start" | "sveltekit" | "tanstack-start" | "tanstack-start-solid";
145
- /**
146
- * void's class model (PLAN4 §3). Class A is Vite-native and Lunora owns the
147
- * worker entry (`createWorker({ httpRouter })`). Class B frameworks own their own
148
- * Cloudflare adapter, so Lunora injects its worker composition into the
149
- * framework's server entry via hooks (PLAN4 M4). Class C is non-CF / SSR-less —
150
- * ship the client adapter + a standalone Lunora worker (today's default).
151
- */
219
+ * The meta-frameworks Lunora can compose with, plus `"none"` for a standalone
220
+ * SPA / SSR-less project (the current default). Mirrors PLAN4 §2.4.
221
+ */
222
+ type DetectedFramework = "astro" | "none" | "nuxt" | "react-router" | "solid-start" | "sveltekit" | "tanstack-start" | "tanstack-start-solid" | "vinext";
223
+ /**
224
+ * void's class model (PLAN4 §3). Class A is Vite-native and Lunora owns the
225
+ * worker entry (`createWorker({ httpRouter })`). Class B frameworks own their own
226
+ * Cloudflare adapter, so Lunora injects its worker composition into the
227
+ * framework's server entry via hooks (PLAN4 M4). Class C is non-CF / SSR-less —
228
+ * ship the client adapter + a standalone Lunora worker (today's default).
229
+ */
152
230
  type FrameworkClass = "A" | "B" | "C";
153
231
  interface FrameworkDetection {
154
232
  /** void's composition class for the detected framework. */
@@ -157,170 +235,295 @@ interface FrameworkDetection {
157
235
  framework: DetectedFramework;
158
236
  }
159
237
  /**
160
- * Detect which meta-framework a project uses by inspecting its `package.json`
161
- * dependencies, and classify it under void's class-A/B/C model (PLAN4 §3).
162
- *
163
- * Pure and best-effort: never throws. An unknown / missing / malformed
164
- * `package.json` yields `{ framework: "none", class: "C" }` so the standalone
165
- * SPA flow is preserved.
166
- */
238
+ * Read and parse the project `package.json`, returning its merged
239
+ * `dependencies` + `devDependencies` name set (empty on any failure). Public
240
+ * so sibling consumers (e.g. the CLI's Vite-project detection) share one
241
+ * best-effort reader instead of re-parsing `package.json` themselves.
242
+ */
243
+ declare const readProjectDependencyNames: (root: string) => ReadonlySet<string>;
244
+ /**
245
+ * Whether the project depends on the unscoped `lunorash` umbrella rather than
246
+ * the granular `@lunora/*` packages.
247
+ *
248
+ * Every generator that writes an import INTO the user's project has to ask:
249
+ * an umbrella-only install — the default for every starter template — never
250
+ * installs `@lunora/server`, so under pnpm's strict layout a scaffolded
251
+ * `from "@lunora/server"` is unresolvable and the worker stops bundling.
252
+ * Codegen answers the same question from its own dependency set
253
+ * (`dependencies.has("lunorash")`); this is that answer for every consumer
254
+ * that has only a project root.
255
+ */
256
+ declare const projectUsesUmbrella: (root: string) => boolean;
257
+ /**
258
+ * Detect which meta-framework a project uses by inspecting its `package.json`
259
+ * dependencies, and classify it under void's class-A/B/C model (PLAN4 §3).
260
+ *
261
+ * Pure and best-effort: never throws. An unknown / missing / malformed
262
+ * `package.json` yields `{ framework: "none", class: "C" }` so the standalone
263
+ * SPA flow is preserved.
264
+ */
167
265
  declare const detectFramework: (root: string) => FrameworkDetection;
266
+ /** Directory holding per-checkout Lunora state (gitignored by convention). */
267
+ declare const DEV_STATE_DIR = ".lunora";
268
+ /** The dev-server state filename, relative to the project root. */
269
+ declare const DEV_STATE_FILE: string;
270
+ /** Log file a backgrounded dev server's output is captured to, relative to the project root. */
271
+ declare const DEV_LOG_FILE: string;
272
+ /**
273
+ * Where `lunora dev` writes the binding manifest by default, relative to the
274
+ * project root.
275
+ *
276
+ * A fixed path so a supervisor can read it without being told one, and next to
277
+ * the state record because the two are read together: this says what the Worker
278
+ * needs and where it will serve, `dev.json` says whether it is serving yet.
279
+ */
280
+ declare const DEV_BINDINGS_FILE: string;
281
+ /**
282
+ * Marker env `lunora dev --background` sets on the detached server process
283
+ * (the daemon `lunora dev` or `vite dev`), so it records itself as
284
+ * `background: true` — and, for the CLI daemon, never re-detects an agent and
285
+ * recurses into background mode again.
286
+ */
287
+ declare const DEV_DAEMON_ENV = "LUNORA_DEV_DAEMON";
288
+ /** Env carrying the capture-log path into the detached server, recorded in the state file. */
289
+ declare const DEV_LOG_FILE_ENV = "LUNORA_DEV_LOG_FILE";
290
+ /**
291
+ * Env carrying the PID of a parent that holds a *provisional* state record it
292
+ * expects the child dev server to supersede. The CLI claims `.lunora/dev.json`
293
+ * with its own PID before spawning the real server (closing the duplicate-start
294
+ * race for the vite flavor and the wrangler daemon), then hands its PID down
295
+ * via this variable; the child's claim (see {@link claimDevServerState}'s
296
+ * `supersedePid`) may replace exactly that record with the authoritative
297
+ * URL + PID.
298
+ */
299
+ declare const DEV_HANDOFF_ENV = "LUNORA_DEV_HANDOFF_PID";
300
+ /** How the recorded dev server runs. */
301
+ type DevServerMode = "cli" | "vite";
302
+ /** The state record persisted to `.lunora/dev.json`. */
303
+ interface DevServerState {
304
+ /** Whether the server was detached into the background (`lunora dev --background`). */
305
+ background?: boolean;
306
+ /** Absolute path of the log file capturing the server's output, when captured. */
307
+ logFile?: string;
308
+ /** `"cli"` for the `lunora dev` wrangler orchestration, `"vite"` for a Vite dev server. */
309
+ mode: DevServerMode;
310
+ /** PID of the process to signal for shutdown (the orchestrating CLI or the Vite process). */
311
+ pid: number;
312
+ /**
313
+ * ISO-8601 stamp marking when the server at {@link DevServerState.url} was
314
+ * first observed accepting requests — absent until then.
315
+ *
316
+ * The rest of this record exists as soon as the server is STARTING: `url` is
317
+ * the origin it intends to serve on and `pid` is a process that exists, both
318
+ * written before anything is listening. That is enough to find and stop a
319
+ * server, and not enough for a task runner deciding when to start the next
320
+ * thing — which is what this field is for. Poll it rather than sleeping.
321
+ *
322
+ * Deliberately a claim about `url` and nothing else. `lunora dev` observes it
323
+ * by probing; `@lunora/vite` observes it from the listen callback, since its
324
+ * record is only written once Vite has resolved that URL. Both are honest
325
+ * about the one origin this record advertises — neither says anything about a
326
+ * sidecar on another port.
327
+ *
328
+ * Absent forever is a legitimate outcome: a server that never binds, or one
329
+ * slower than the probe was willing to wait. Treat missing as "not ready
330
+ * yet", never as "will never be ready".
331
+ */
332
+ readyAt?: string;
333
+ /** ISO-8601 stamp written at startup, purely informational (drives `status` uptime). */
334
+ startedAt?: string;
335
+ /** The embedded studio server's URL, when it runs. */
336
+ studioUrl?: string;
337
+ /** The primary URL serving the worker/app. */
338
+ url: string;
339
+ }
168
340
  /**
169
- * The `.dev.vars` line grammar — one owner, shared by every reader/writer of the
170
- * file so the format can't drift between packages. `@lunora/cli`'s `env`
171
- * command (parse/serialize) and `@lunora/config`'s scaffolder (comment-
172
- * preserving rewrite) do different *transforms*, but they agree on these
173
- * primitives: the filename, what a `KEY` looks like, how lines split, and how
174
- * quotes strip.
175
- */
341
+ * True when a process with `pid` is currently alive AND signalable by this
342
+ * user. Signal `0` performs the existence check without delivering anything.
343
+ *
344
+ * `EPERM` ("alive but another user's process") deliberately counts as NOT
345
+ * alive here: every dev server this module tracks was spawned by the current
346
+ * user, so a PID we cannot signal is by definition a recycled PID — treating
347
+ * it as running would wedge the lockfile permanently and aim `dev stop`'s
348
+ * kill escalation at an innocent process.
349
+ */
350
+ declare const isProcessAlive: (pid: number) => boolean;
351
+ /**
352
+ * True when the record's PID verifiably still refers to the dev server that
353
+ * wrote the record — not merely "some process exists with that number".
354
+ * Guards against PID reuse: a server process necessarily starts BEFORE its
355
+ * record is written, so a process that started after `startedAt` (+ skew) is
356
+ * a recycled PID wearing the corpse's number. The start-time check runs only
357
+ * where the platform exposes it (Linux); elsewhere liveness alone decides.
358
+ */
359
+ declare const isRecordedProcessCurrent: (state: DevServerState) => boolean;
360
+ /**
361
+ * Read the state record from `.lunora/dev.json`, or `undefined` when there is
362
+ * no usable record. Best-effort; performs NO liveness check — use
363
+ * {@link readLiveDevServerState} for "is a dev server actually running".
364
+ */
365
+ declare const readDevServerState: (projectRoot: string) => DevServerState | undefined;
366
+ /**
367
+ * Write the state record to `.lunora/dev.json`, creating the `.lunora/`
368
+ * directory when absent. Returns the absolute path written, or `undefined`
369
+ * when the write failed (state is convenience metadata — a read-only checkout
370
+ * must never crash dev startup).
371
+ */
372
+ declare const writeDevServerState: (projectRoot: string, state: DevServerState) => string | undefined;
373
+ /**
374
+ * Merge `patch` into the existing state record, when one exists. Used by the
375
+ * CLI to stamp `background`/`logFile` onto the record the Vite plugin wrote.
376
+ * Returns the merged record, or `undefined` when there was nothing to update.
377
+ *
378
+ * Pass `expectedPid` to patch only while the record still belongs to that
379
+ * process — the counterpart to {@link clearDevServerState}'s guard, and needed
380
+ * by any patch that can land LATE. A readiness stamp is the case in point: this
381
+ * process's server can answer after a newer one has replaced the record, and an
382
+ * unguarded merge would mark that newer server ready on the old one's behalf.
383
+ */
384
+ declare const updateDevServerState: (projectRoot: string, patch: Partial<DevServerState>, options?: {
385
+ expectedPid?: number;
386
+ }) => DevServerState | undefined;
387
+ /**
388
+ * Whether a record reports a server that is accepting requests, as opposed to
389
+ * one that has merely started. Lives here so the CLI's `status`, its JSON
390
+ * output, and any future reader cannot each invent their own answer.
391
+ */
392
+ declare const isDevServerReady: (state: Pick<DevServerState, "readyAt"> | undefined) => boolean;
393
+ /**
394
+ * Remove `.lunora/dev.json`. Idempotent and never throws. When `expectedPid`
395
+ * is given, the file is only removed while it still records that PID — so a
396
+ * shutting-down server can't clobber the record a newer server just wrote.
397
+ */
398
+ declare const clearDevServerState: (projectRoot: string, expectedPid?: number) => void;
399
+ /**
400
+ * The state record of a dev server that is verifiably running right now, or
401
+ * `undefined`. A record whose PID is dead — or recycled onto a different
402
+ * process (see {@link isRecordedProcessCurrent}) — is stale: it is cleared on
403
+ * the spot so subsequent starts don't keep re-reading a corpse.
404
+ */
405
+ declare const readLiveDevServerState: (projectRoot: string) => DevServerState | undefined;
406
+ /** Result of {@link claimDevServerState}: claimed, or lost to the live server already recorded. */
407
+ interface ClaimDevServerStateResult {
408
+ /** The live record that won the race, when `ok` is `false`. */
409
+ existing?: DevServerState;
410
+ /** Whether this process now owns the record. */
411
+ ok: boolean;
412
+ }
413
+ declare const claimDevServerState: (projectRoot: string, state: DevServerState, options?: {
414
+ supersedePid?: number;
415
+ }) => ClaimDevServerStateResult;
176
416
  /** The conventional filename for local Cloudflare dev secrets (gitignored). */
177
417
  declare const DEV_VARS_FILE: string;
178
418
  /** Its committed, secret-free counterpart that scaffolding reads from. */
179
419
  declare const DEV_VARS_EXAMPLE_FILE: string;
180
- /** A bare `KEY` identifier — the part left of `=` in a `.dev.vars` line. */
420
+ /**
421
+ * A bare `KEY` identifier accepted on the **write** path — `lunora env set`,
422
+ * `env unset`, `env generate`, and {@link upsertDevVariableLine}'s line
423
+ * targeting. Deliberately stricter than what the reader accepts: dotenv keys
424
+ * are `[\w.-]+`, but a key we *write* stays a conventional env-var name so it
425
+ * needs no quoting and round-trips through every consumer. Never use this to
426
+ * decide whether a line in an existing file counts — that is the reader's job.
427
+ */
181
428
  declare const DEV_VARS_KEY_PATTERN: RegExp;
182
- /** Splits file content into lines on either newline style. */
183
-
184
- /**
185
- * Parse `.dev.vars` content into its `{ key, value }` entries, in file order,
186
- * with values unquoted and comments/blank/invalid lines dropped. The canonical
187
- * read of the whole file — callers that just want the variables (rather than a
188
- * comment-preserving rewrite) use this instead of hand-rolling the split loop.
189
- */
429
+ /**
430
+ * Parse `.dev.vars` content into its `{ key, value }` entries, in file order.
431
+ *
432
+ * Delegates to dotenv's own `parse()` — the literal function `wrangler dev` /
433
+ * `@cloudflare/vite-plugin` run over this file before handing the variables to
434
+ * the worker. Using the library rather than a compatible reimplementation is
435
+ * the point: every line the worker sees, Lunora reads identically, including
436
+ * `export `-prefixed lines, dotted/dashed keys, `KEY: value` separators,
437
+ * quote stripping (single/double/backtick), `\n`/`\r` expansion inside double
438
+ * quotes, `#` comments ending unquoted values, and multi-line quoted values.
439
+ * Duplicate keys resolve last-wins, at the key's first position — dotenv
440
+ * returns an object, so a repeated key overwrites in place.
441
+ *
442
+ * The canonical read of the whole file: every reader of `.dev.vars` (and of
443
+ * `.dev.vars.example`) goes through here or through {@link
444
+ * parseDevVariableLine} below, so the format cannot drift between packages.
445
+ */
190
446
  declare const parseDevVariableEntries: (content: string) => {
191
447
  key: string;
192
448
  value: string;
193
449
  }[];
194
- interface DiscoverWorkflowInfoResult {
195
- /** Parse error message, when `lunora/workflows.ts` exists but could not be analyzed. */
196
- error?: string;
197
- /** Discovered workflow definitions; `[]` when none are declared or parsing failed. */
198
- workflows: ReadonlyArray<WorkflowIR>;
199
- }
200
- /**
201
- * Discover the project's `defineWorkflow` declarations. Returns
202
- * `{ workflows: [] }` when the project has no `lunora/workflows.ts` (not an
203
- * error), or `{ workflows: [], error }` when the file exists but could not be
204
- * parsed — callers decide whether that is a warning (validator) or ignorable
205
- * (inference).
206
- */
207
- declare const discoverWorkflowInfo: (projectRoot: string, schemaDirectory: string) => DiscoverWorkflowInfoResult;
208
- interface DurableObjectSpec {
209
- binding: string;
210
- className: string;
211
- }
212
- /**
213
- * A `defineContainer` declaration plus whether its generated DO class is
214
- * exported by the worker entry. Only exported containers are safe to
215
- * provision — wrangler rejects a `containers[].class_name` (and its Durable
216
- * Object binding) that the worker doesn't export.
217
- */
218
- interface InferredContainer extends ContainerIR {
219
- exported: boolean;
220
- }
221
- /**
222
- * A `defineWorkflow` declaration plus whether its generated
223
- * `WorkflowEntrypoint` class is exported by the worker entry. Only exported
224
- * workflows are safe to provision — wrangler rejects a `workflows[].class_name`
225
- * the worker doesn't export. Workflows are NOT Durable Objects, so this never
226
- * implies a `durable_objects` binding or migration.
227
- */
228
- interface InferredWorkflow extends WorkflowIR {
229
- exported: boolean;
230
- }
231
- /**
232
- * A queue declared in `lunora/queues.ts`. Unlike workflows, a queue needs no
233
- * worker-entry class export (its `queue()` handler rides `createWorker`), so
234
- * there is no `exported` flag — every declared queue is reconcilable into the
235
- * wrangler `queues.producers[]` / `queues.consumers[]`.
236
- */
237
- type InferredQueue = QueueIR;
238
- interface InferredBindings {
239
- /** Containers declared in `lunora/containers.ts` (exported or not — see {@link InferredContainer.exported}). */
240
- containers: InferredContainer[];
241
- /** Durable Objects the worker entry exports → safe to bind. */
242
- durableObjects: DurableObjectSpec[];
243
- /**
244
- * The wrangler `flagship[].binding` name implied by `lunora/flags.ts` when it
245
- * uses the Flagship provider in binding mode — `undefined` for HTTP-mode
246
- * Flagship, a custom OpenFeature provider, or no flags. The binding needs an
247
- * un-mintable `app_id`, so it is reconciled as a hint, not auto-written.
248
- */
249
- flagshipBinding?: string;
250
- /** Schema declares a `.global()` table → needs the `DB` D1 binding. */
251
- needsD1: boolean;
252
- /** Queues declared in `lunora/queues.ts` → reconciled into `queues.producers[]` / `queues.consumers[]`. */
253
- queues: InferredQueue[];
254
- /** Human-readable provenance for each inferred binding / hint, for logging. */
255
- signals: string[];
256
- /** `@lunora/ai` is imported or `env.AI` is used → needs the `ai` Workers AI binding. */
257
- usesAi: boolean;
258
- /** `@lunora/bindings/analytics` is imported → self-describing `analytics_engine_datasets` binding (auto-writeable). */
259
- usesAnalytics: boolean;
260
- /** `@lunora/auth` is imported (sessions may be D1- or `SessionDO`-backed). */
261
- usesAuth: boolean;
262
- /** `@lunora/browser` is imported → self-describing `browser` binding (auto-writeable). */
263
- usesBrowser: boolean;
264
- /** `lunora/flags.ts` declares a feature-flag provider (any OpenFeature provider — Flagship or custom). */
265
- usesFlags: boolean;
266
- /** `@lunora/hyperdrive` is imported (binding needs an un-mintable remote `id`; hint-only). */
267
- usesHyperdrive: boolean;
268
- /** `@lunora/bindings/images` is imported → self-describing `images` binding (auto-writeable). */
269
- usesImages: boolean;
270
- /** `@lunora/bindings/kv` is imported (namespace binding name + id are user-defined; hint-only). */
271
- usesKv: boolean;
272
- /** `@lunora/mail` is imported (Resend API key must be set in `.dev.vars`; no binding). */
273
- usesMail: boolean;
274
- /** `@lunora/payment` is imported (provider secrets must be set in `.dev.vars`; no binding). */
275
- usesPayment: boolean;
276
- /** `ctx.pipelines` is used (binding needs an un-mintable remote pipeline name; hint-only). */
277
- usesPipelines: boolean;
278
- /** `@lunora/scheduler` is imported. */
279
- usesScheduler: boolean;
280
- /** `@lunora/storage` is imported (R2 bucket binding name is user-defined). */
281
- usesStorage: boolean;
282
- /** Workflows declared in `lunora/workflows.ts` (exported or not — see {@link InferredWorkflow.exported}). */
283
- workflows: InferredWorkflow[];
284
- }
285
- interface InferOptions {
286
- projectRoot: string;
287
- /** Directories (relative to root) to scan. Defaults to `lunora` + `src`. */
288
- scanDirs?: ReadonlyArray<string>;
289
- /** Lunora source directory holding `schema.ts`. Defaults to `lunora`. */
290
- schemaDir?: string;
291
- }
292
450
  /**
293
- * Scan a Lunora project and report which Cloudflare bindings its code implies.
294
- * Read-only: performs no writes. Binding provisioning is driven by the worker
295
- * entry's Durable Object exports plus the schema's D1 need; capability imports
296
- * surface as hints.
297
- */
298
- declare const inferLunoraBindings: (options: InferOptions) => Promise<InferredBindings>;
299
- /**
300
- * Derive the list of `@lunora/*` package names that are actively used by a
301
- * project, based on its already-resolved {@link InferredBindings}.
302
- *
303
- * This is the canonical bridge between binding inference and the package-aware
304
- * `.dev.vars.example` scaffolding in `scaffold-dev-variables.ts`. The result is
305
- * a stable, predictable slice of {@link CAPABILITY_SOURCES} source values,
306
- * filtered to the flags that are `true` in `bindings` — in CAPABILITY_SOURCES
307
- * declaration order.
308
- */
309
- declare const packageNamesFromBindings: (bindings: InferredBindings) => string[];
451
+ * Escape a runtime string for safe literal interpolation into a `RegExp`
452
+ * source. The one canonical implementation — `@lunora/config`'s own
453
+ * `infer-bindings.ts` (type-only-export detection) and `@lunora/cli`'s `env`
454
+ * command both need this and used to carry their own (functionally
455
+ * identical) copy; grammar this fundamental gets one owner like everything
456
+ * else in this module.
457
+ */
458
+ declare const escapeRegExp: (value: string) => string;
459
+ /**
460
+ * Surgically upsert a single `KEY="value"` line in raw `.dev.vars` content,
461
+ * leaving every comment, blank line, and untouched entry verbatim. Rebuilding
462
+ * the whole file from the parsed entry map (an earlier approach) silently
463
+ * dropped all `# …` comments and blank lines — including the documentation the
464
+ * registry installer and scaffolder write — and re-quoted lines the user never
465
+ * touched. If the key already has a line it is replaced in place; otherwise the
466
+ * new line is appended with a single trailing newline. Always quotes the value
467
+ * to preserve a whitespace round-trip; callers (`env set`, `env generate --set`,
468
+ * `deploy`'s minted-secret disclosure) reject newline/`"`/`\` up front so the
469
+ * verbatim quote is safe.
470
+ *
471
+ * Duplicate lines for the key are collapsed down to exactly one. The shared
472
+ * read path is last-wins — dotenv overwrites the key as it walks the file — so
473
+ * replacing only the first match would leave a later, untouched duplicate
474
+ * still winning at read time, i.e. a `set` that silently didn't take effect.
475
+ */
476
+ declare const upsertDevVariableLine: (content: string, key: string, value: string) => string;
477
+ /**
478
+ * Surgically remove every `.dev.vars` line defining `key` (and its own line
479
+ * terminator), preserving all other lines, comments, and blanks verbatim.
480
+ * Backs `lunora env unset`.
481
+ */
482
+ declare const removeDevVariableLine: (content: string, key: string) => string;
483
+ /** The default deploy target — today's behavior for every project. */
484
+ declare const DEFAULT_DEPLOY_TARGET = "cloudflare";
485
+ /** The ids a caller may select, for error messages and `--target` help text. */
486
+ declare const deployTargetIds: () => ReadonlyArray<string>;
487
+ /**
488
+ * Resolve a target name to its driver.
489
+ * @throws when `target` names no registered driver — never falls back to the default.
490
+ */
491
+ declare const resolveDeployDriver: (target?: string) => DeployDriver;
492
+ /**
493
+ * Whether `target`'s driver ships a `toolchain` — i.e. whether any tool can
494
+ * actually build, serve or deploy it, as opposed to merely generate for it.
495
+ *
496
+ * One predicate rather than one per caller: the CLI (`deploy`, `dev`) and the
497
+ * Vite plugin (`build`, `dev`) must refuse the same set, and a second copy
498
+ * fails silently — the tool that forgot runs the WRONG pipeline for the target
499
+ * instead of rejecting it.
500
+ * @param target A target id already resolved by `resolveTargetOrThrow`. Unknown
501
+ * ids answer `false` rather than throwing, so this is safe to ask about
502
+ * arbitrary input.
503
+ * @returns `true` when the target can be run.
504
+ */
505
+ declare const isRunnableTarget: (target: string) => boolean;
506
+ /**
507
+ * The subset of {@link deployTargetIds} that {@link isRunnableTarget} accepts,
508
+ * in the same (alphabetical) order.
509
+ * @returns every target id a tool can build, serve or deploy — used to name the
510
+ * alternatives when one is refused.
511
+ */
512
+ declare const runnableTargetIds: () => ReadonlyArray<string>;
310
513
  /** Directory holding per-checkout Lunora state (gitignored by convention). */
311
514
  declare const LINKED_PROJECT_DIR = ".lunora";
312
515
  /** The canonical link filename, relative to the project root. */
313
516
  declare const LINKED_PROJECT_FILE: string;
314
517
  /**
315
- * The link record persisted to `.lunora/project.json`. Every field is optional
316
- * so a partially-populated link (e.g. a worker name with no URL yet) still
317
- * round-trips. `linkedAt` is an ISO-8601 stamp written at link time, purely
318
- * informational.
319
- */
518
+ * The link record persisted to `.lunora/project.json`. Every field is optional
519
+ * so a partially-populated link (e.g. a worker name with no URL yet) still
520
+ * round-trips. `linkedAt` is an ISO-8601 stamp written at link time, purely
521
+ * informational.
522
+ */
320
523
  interface LinkedProject {
321
524
  /** Cloudflare account id the worker lives under, when known. */
322
525
  account?: string;
323
- /** Cloudflare environment name (`wrangler … --env &lt;env>`), when scoped. */
526
+ /** Cloudflare environment name (`wrangler … --env <env>`), when scoped. */
324
527
  env?: string;
325
528
  /** ISO-8601 timestamp recorded when the link was written. */
326
529
  linkedAt?: string;
@@ -330,34 +533,67 @@ interface LinkedProject {
330
533
  workerUrl?: string;
331
534
  }
332
535
  /**
333
- * Read the link record from `.lunora/project.json`, or `undefined` when there
334
- * is no usable link. Best-effort: a missing file, parse error, or unexpected
335
- * shape all collapse to `undefined`.
336
- */
536
+ * Read the link record from `.lunora/project.json`, or `undefined` when there
537
+ * is no usable link. Best-effort: a missing file, parse error, or unexpected
538
+ * shape all collapse to `undefined`.
539
+ */
337
540
  declare const readLinkedProject: (projectRoot: string) => LinkedProject | undefined;
338
541
  /**
339
- * Write the link record to `.lunora/project.json`, creating the `.lunora/`
340
- * directory when absent. Only defined fields are persisted (so an empty value
341
- * never clobbers a known one). Returns the absolute path written.
342
- */
542
+ * Write the link record to `.lunora/project.json`, creating the `.lunora/`
543
+ * directory when absent. Only defined fields are persisted (so an empty value
544
+ * never clobbers a known one). Returns the absolute path written.
545
+ */
343
546
  declare const writeLinkedProject: (projectRoot: string, link: LinkedProject) => string;
344
547
  /**
345
- * Shared formatter for the structured log events the Lunora runtime emits to the
346
- * worker's `console` during development. Both the CLI `dev` command and the Vite
347
- * plugin pipe worker output through `formatLunoraEvent` so a developer sees
348
- * attributed, readable lines instead of raw JSON.
349
- *
350
- * The runtime emits two event shapes, each a single `console` line tagged
351
- * `source: "lunora"` (see `@lunora/do`'s `request-log.ts`): a `type: "log"`
352
- * event per `ctx.log.*` call, and a `type: "request"` event per RPC dispatch
353
- * (opt-in for successful calls, always for errors).
354
- *
355
- * This module is intentionally dependency-free and colour-free: it returns the
356
- * severity plus a plain display string, leaving ANSI/level colouring to each
357
- * caller (the CLI routes through its `pail` logger; the Vite plugin dims inline).
358
- * Any line that is not a lunora event returns `undefined`, signalling the caller
359
- * to pass it through unchanged.
360
- */
548
+ * The generated and derived paths a linter or formatter should skip, in
549
+ * gitignore syntax relative to the project root.
550
+ *
551
+ * Committed-on-purpose entries are in here too, deliberately: being tracked by
552
+ * git says nothing about whether a human should be asked to reformat it.
553
+ */
554
+ declare const LUNORA_IGNORED_PATHS: ReadonlyArray<string>;
555
+ /** A linter or formatter this module knows how to configure. */
556
+ type LintTool = "biome" | "eslint" | "oxlint" | "prettier";
557
+ /**
558
+ * What happened to one tool's configuration.
559
+ *
560
+ * `"manual"` is not a failure — it means the change is correct but not safe to
561
+ * make automatically, and {@link LintIgnoreOutcome.snippet} carries what to
562
+ * paste. `"failed"` IS a failure, but only of this step: the command that
563
+ * triggered it had already done its real work, so it is reported rather than
564
+ * thrown.
565
+ */
566
+ type LintIgnoreStatus = "created" | "failed" | "manual" | "unchanged" | "updated";
567
+ interface LintIgnoreOutcome {
568
+ /** For `"failed"`: why the writer could not run. The command itself still succeeded. */
569
+ message?: string;
570
+ /** Config file that was written, or the one the user must edit for `"manual"`. */
571
+ path: string;
572
+ /** For `"manual"`: the exact text to add. */
573
+ snippet?: string;
574
+ status: LintIgnoreStatus;
575
+ tool: LintTool;
576
+ }
577
+ /**
578
+ * Which linters/formatters this project already uses, by declared dependency or
579
+ * config file on disk.
580
+ *
581
+ * Both signals matter: a dependency without a config is a tool about to be
582
+ * configured, and a config without a dependency is a tool installed globally or
583
+ * hoisted from a monorepo root. Missing either one means silently skipping a
584
+ * tool the project genuinely runs.
585
+ */
586
+ declare const detectLintTools: (projectRoot: string) => LintTool[];
587
+ /**
588
+ * Add {@link LUNORA_IGNORED_PATHS} to each tool's configuration.
589
+ *
590
+ * Idempotent: every writer appends only what is missing, so re-running after a
591
+ * `lunora add` neither duplicates entries nor disturbs a project's own rules.
592
+ * @param projectRoot The project to configure.
593
+ * @param tools Which tools to configure — normally {@link detectLintTools}'s result, or the user's selection at `init`.
594
+ * @returns one outcome per tool, in the order given.
595
+ */
596
+ declare const applyLintIgnores: (projectRoot: string, tools: ReadonlyArray<LintTool>) => LintIgnoreOutcome[];
361
597
  /** Severity a formatted line should be surfaced at, mapped onto the three logger channels. */
362
598
  type LunoraLineLevel = "error" | "info" | "warn";
363
599
  /** A formatted lunora event: the channel to surface it on, the display text, and which event produced it. */
@@ -372,11 +608,11 @@ interface LunoraFormattedLine {
372
608
  /** Stable `source` tag every lunora console event carries. Mirrors `REQUEST_LOG_EVENT_SOURCE` in `@lunora/do`. */
373
609
  declare const LUNORA_EVENT_SOURCE = "lunora";
374
610
  /**
375
- * Parse a single worker-output line and, when it is a lunora structured event,
376
- * return its severity plus display text. Returns `undefined` for anything else —
377
- * non-JSON lines, JSON that isn't a lunora event, or an unrecognised event type
378
- * — so the caller passes the original line through untouched. Pure and total.
379
- */
611
+ * Parse a single worker-output line and, when it is a lunora structured event,
612
+ * return its severity plus display text. Returns `undefined` for anything else —
613
+ * non-JSON lines, JSON that isn't a lunora event, or an unrecognised event type
614
+ * — so the caller passes the original line through untouched. Pure and total.
615
+ */
380
616
  declare const formatLunoraEvent: (line: string) => LunoraFormattedLine | undefined;
381
617
  declare class LunoraReporter {
382
618
  #private;
@@ -384,33 +620,75 @@ declare class LunoraReporter {
384
620
  setStderr(stderr: NodeJS.WriteStream): void;
385
621
  log(meta: unknown): void;
386
622
  }
623
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
624
+ /** True when `manager` is on PATH — probed by running `<manager> --version`. Injectable for tests. */
625
+ type PackageManagerProbe = (manager: PackageManager) => boolean;
626
+ /**
627
+ * The package managers actually installed on this machine, in preference order
628
+ * ({@link INSTALL_PREFERENCE} — pnpm > bun > yarn > npm). The first entry is the
629
+ * recommended default for the install prompt; the whole list is what the user
630
+ * picks from. Empty when none are found.
631
+ */
632
+ declare const detectInstalledManagers: (probe?: PackageManagerProbe) => PackageManager[];
633
+ /** The argv that installs a project's dependencies with `manager` (`<manager> install`). */
634
+ declare const installArgsFor: (manager: PackageManager) => {
635
+ args: string[];
636
+ command: string;
637
+ };
638
+ /**
639
+ * The argv that adds `packages` as new dependencies with `manager` — the
640
+ * counterpart to {@link installArgsFor}, which reinstalls what package.json
641
+ * already declares rather than adding something new to it.
642
+ *
643
+ * npm's dev flag is `--save-dev` (its own docs lead with the long form); bun's
644
+ * is the lowercase `-d` (uppercase `-D` is not one of its recognized flags);
645
+ * pnpm and yarn both take `-D`.
646
+ */
647
+ declare const addArgsFor: (manager: PackageManager, packages: ReadonlyArray<string>, options?: {
648
+ dev?: boolean;
649
+ }) => {
650
+ args: string[];
651
+ command: string;
652
+ };
653
+ /**
654
+ * Resolve the package manager to drive for the project rooted at (or above)
655
+ * `startDirectory`. Every step is a real signal — Lunora never blindly assumes a
656
+ * particular manager, and there is no hardcoded fallback:
657
+ *
658
+ * 1. `@visulima/package` — the nearest lock file (`pnpm-lock.yaml`, `yarn.lock`,
659
+ * `package-lock.json`, `bun.lockb`) or the `packageManager` field of the nearest
660
+ * `package.json`.
661
+ * 2. The manager that launched this CLI, read from `npm_config_user_agent` (so
662
+ * `pnpm dlx lunora …` / `npx lunora …` resolve to the right manager).
663
+ * 3. The first package manager actually installed on this machine
664
+ * ({@link detectInstalledManagers}).
665
+ *
666
+ * Throws when none of those resolve — i.e. there is no lock file / `packageManager`
667
+ * field, the CLI wasn't launched by a known manager, and none is on `PATH`.
668
+ * Surfacing that is better than silently guessing a manager that can't run.
669
+ */
670
+ declare const detectPackageManager: (startDirectory: string) => PackageManager;
671
+ /** Map a package manager to the argv pair that runs an installed CLI. */
672
+ declare const execArgsFor: (manager: PackageManager, command: string, args: ReadonlyArray<string>) => {
673
+ args: string[];
674
+ command: string;
675
+ };
387
676
  /**
388
- * Per-package secret-requirements registry for `.dev.vars` scaffolding.
389
- *
390
- * Each entry maps a `@lunora/*` package name → the secrets it requires at
391
- * runtime, expressed as `{ key, description, docsUrl }` records. The scaffolder
392
- * in {@link ./scaffold-dev-variables} reads this registry to emit package-aware
393
- * `.dev.vars.example` entries (placeholders + inline doc-pointer comments).
394
- *
395
- * ## Adding a new add-on
396
- *
397
- * When a new `@lunora/*` package requires runtime secrets, add one entry to
398
- * {@link PACKAGE_SECRETS_REGISTRY} keyed by its exact npm package name. Each
399
- * `SecretEntry` in the array needs:
400
- *
401
- * - `key` — the env-var name the package reads from `env` (e.g. `RESEND_API_KEY`).
402
- * - `description` — one line describing the secret and how to obtain it.
403
- * - `docsUrl` — a stable URL to the package/provider docs.
404
- *
405
- * The registry lives in `@lunora/config` so that add-ons themselves never need
406
- * to depend on it (no circular coupling). Add-ons document their secrets in
407
- * their own READMEs; the registry duplicates that knowledge in a machine-readable
408
- * form that the scaffolder can consume.
409
- *
410
- * **Never write a real secret value** in this file — `placeholderValue` entries
411
- * are the only allowed values (they must pass `isPlaceholderValue` from
412
- * scaffold-dev-variables).
413
- */
677
+ * The argv that runs a project SCRIPT with `manager` — the counterpart to
678
+ * {@link execArgsFor}, which runs a BINARY.
679
+ *
680
+ * All four managers accept `<manager> run <script>`, so this is uniform. It is a
681
+ * separate function because reaching for `execArgsFor(manager, "run", [script])`
682
+ * silently produces something else entirely: `pnpm exec run <script>` fails with
683
+ * `Command "run" not found`, and `npx -- run <script>` resolves the registry
684
+ * PACKAGE named `run` and executes it.
685
+ */
686
+ declare const runScriptArgsFor: (manager: PackageManager, script: string) => {
687
+ args: string[];
688
+ command: string;
689
+ };
690
+ /** The shell command that runs a project script with `manager` (`pnpm dev`, `npm run dev`, …). */
691
+ declare const runScriptCommand: (manager: PackageManager, script: string) => string;
414
692
  /** A single secret variable required by a package. */
415
693
  interface SecretEntry {
416
694
  /** One sentence describing what this secret is and how to obtain it. */
@@ -420,96 +698,171 @@ interface SecretEntry {
420
698
  /** The env-var key as it appears in `.dev.vars`, e.g. `AUTH_SECRET`. */
421
699
  key: string;
422
700
  /**
423
- * The placeholder value written into `.dev.vars.example`.
424
- * Must pass `isPlaceholderValue` from scaffold-dev-variables so the
425
- * scaffolder regenerates it when generating `.dev.vars`. Use angle-bracket
426
- * conventions or a recognised marker like `replace-with-openssl-rand-hex-32`.
427
- * Non-secret env-vars (e.g. `AUTH_URL`) may carry a real default value.
428
- */
701
+ * The placeholder value written into `.dev.vars.example`.
702
+ * Must pass `isPlaceholderValue` from scaffold-dev-variables so the
703
+ * scaffolder regenerates it when generating `.dev.vars`. Use angle-bracket
704
+ * conventions or a recognised marker like `replace-with-openssl-rand-hex-32`.
705
+ * Non-secret env-vars (e.g. `AUTH_URL`) may carry a real default value.
706
+ */
429
707
  placeholderValue: string;
430
708
  }
431
709
  /**
432
- * Secrets every Lunora project needs regardless of which capability packages are
433
- * installed — scaffolded into `.dev.vars` always. `LUNORA_ADMIN_TOKEN` is the
434
- * bearer the local Studio uses to call the worker's admin endpoints (the data
435
- * browser, schema edits) in dev; the worker reads the SAME `.dev.vars` value via
436
- * its admin gate, so both agree and the Studio authenticates without a prompt.
437
- * Without it, every `/_lunora/admin/*` call is `ADMIN_FORBIDDEN` (403).
438
- */
439
-
440
- /**
441
- * The canonical registry of per-package secret requirements.
442
- *
443
- * Keys are exact npm package names (e.g. `"@lunora/auth"`). Values are
444
- * non-empty arrays of {@link SecretEntry} — one entry per required secret key.
445
- *
446
- * The scaffolder in `scaffold-dev-variables.ts` calls
447
- * {@link secretsForPackages} to resolve the applicable entries from this map
448
- * given the set of detected capability package names.
449
- */
710
+ * The canonical registry of per-package secret requirements.
711
+ *
712
+ * Keys are exact npm package names (e.g. `"@lunora/auth"`). Values are
713
+ * non-empty arrays of {@link SecretEntry} — one entry per required secret key.
714
+ *
715
+ * The scaffolder in `scaffold-dev-variables.ts` calls
716
+ * {@link secretsForPackages} to resolve the applicable entries from this map
717
+ * given the set of detected capability package names.
718
+ */
450
719
  declare const PACKAGE_SECRETS_REGISTRY: Readonly<Record<string, ReadonlyArray<SecretEntry>>>;
451
- /**
452
- * Collect all secret entries required by the given set of package names. The
453
- * order follows the order of `packageNames` (stable, predictable output), and
454
- * within each package the entries are returned in registry declaration order.
455
- *
456
- * Only packages present in {@link PACKAGE_SECRETS_REGISTRY} contribute entries;
457
- * unknown package names are silently ignored — this makes the call site resilient
458
- * to future capability flags whose packages have no secrets.
459
- */
460
720
  declare const secretsForPackages: (packageNames: ReadonlyArray<string>) => SecretEntry[];
461
- /** The canonical project-config filename probed at the project root. */
462
- declare const LUNORA_CONFIG_FILE = "lunora.json";
463
- /**
464
- * The parsed `remote` preference from `lunora.json`:
465
- *
466
- * - `true` / `false` — the boolean form: enable or explicitly disable remote dev.
467
- * - `undefined` — no usable preference (file absent, key absent, or malformed).
468
- *
469
- * The object form (scoping which binding kinds go remote) is reserved for a
470
- * future increment; for now an object value is treated as "enabled" (truthy
471
- * presence) so forward-written configs still turn remote on.
472
- */
473
- type RemotePreference = boolean | undefined;
474
- /** The structural slice of `lunora.json` Lunora reads. */
475
- interface LunoraProjectConfig {
476
- remote?: unknown;
721
+ /**
722
+ * The two channels this needs from a caller's logger. Deliberately structural
723
+ * and this small: the CLI's `Logger`, Vite's `server.config.logger` shim, and a
724
+ * two-line test double all satisfy it without any of them being imported here.
725
+ */
726
+ interface HookLogger {
727
+ error: (message: string) => void;
728
+ info: (message: string) => void;
729
+ }
730
+ /** What a spawner is handed. A superset-accepting spawner (the CLI's) satisfies this. */
731
+ interface HookSpawnDescriptor {
732
+ args: ReadonlyArray<string>;
733
+ command: string;
734
+ cwd?: string;
735
+ /**
736
+ * Route the child's stdout to the parent's stderr (fd 2). Set whenever the
737
+ * caller's own stdout is a machine-readable stream — `--format json`, or
738
+ * `lunora dev`'s NDJSON — since a `postcodegen` script that prints anything
739
+ * would otherwise interleave with and corrupt it.
740
+ */
741
+ stdoutToStderr?: boolean;
742
+ }
743
+ /** Run a child process and resolve with its exit code. Injectable so tests need no subprocess. */
744
+ type HookSpawner = (descriptor: HookSpawnDescriptor) => Promise<{
745
+ code: number;
746
+ }>;
747
+ interface PostCodegenHookResult {
748
+ /**
749
+ * The hook's failure message, when it ran and did not succeed.
750
+ *
751
+ * Already logged by the time a caller sees it — read it only to decide
752
+ * whether to ABORT, and to carry into a structured result. Logging it again
753
+ * double-reports.
754
+ */
755
+ error?: string;
756
+ /** True when a `postcodegen` script existed and was invoked. */
757
+ ran: boolean;
477
758
  }
478
759
  /**
479
- * Interpret a raw `remote` value into a tri-state preference. A boolean passes
480
- * through; an object is treated as enabled (the documented-but-not-yet-honored
481
- * scoping form is still an opt-in); anything else (string, number, null) is no
482
- * preference.
483
- */
760
+ * Run the project's `postcodegen` script, if it declares one.
761
+ *
762
+ * A failure is reported here and returned rather than thrown, so every caller
763
+ * gets identical reporting and is left with only the decision that actually
764
+ * differs between them: whether it blocks. `deploy` and `prepare` must abort —
765
+ * the whole point is not shipping output the project considers unfinished —
766
+ * while a dev watch loop must not, since the next edit is the chance to fix it.
767
+ *
768
+ * Reporting used to be the caller's job, and all three copied the same
769
+ * `logger.error(result.error)` line. That made "was this surfaced?" a convention
770
+ * rather than a guarantee, and a fourth caller had no way to know it owed one.
771
+ * @returns whether the hook ran, and its (already-logged) error message when it failed.
772
+ */
773
+ declare const runPostCodegenHook: (options: {
774
+ cwd: string;
775
+ logger: HookLogger;
776
+ spawner?: HookSpawner;
777
+ /** See {@link HookSpawnDescriptor.stdoutToStderr}. */
778
+ stdoutToStderr?: boolean;
779
+ }) => Promise<PostCodegenHookResult>;
780
+ /**
781
+ * The parsed `remote` preference from `lunora.config.*`:
782
+ *
783
+ * - `true` / `false` — the boolean form: enable or explicitly disable remote dev.
784
+ * - `undefined` — no usable preference (file absent, key absent, or malformed).
785
+ *
786
+ * The object form (scoping which binding kinds go remote) is reserved for a
787
+ * future increment; for now an object value is treated as "enabled" (truthy
788
+ * presence) so forward-written configs still turn remote on.
789
+ */
790
+ type RemotePreference = boolean | undefined;
791
+ /**
792
+ * Interpret a raw `remote` value into a tri-state preference. A boolean passes
793
+ * through; an object is treated as enabled (the documented-but-not-yet-honored
794
+ * scoping form is still an opt-in); anything else (string, number, null) is no
795
+ * preference.
796
+ */
484
797
  declare const interpretRemote: (value: unknown) => RemotePreference;
485
798
  /**
486
- * Read the project's `remote` preference from `lunora.json`, or `undefined` when
487
- * there's no usable preference. Best-effort: never throws — a missing file,
488
- * parse error, or unexpected shape all collapse to `undefined` so the caller
489
- * falls through to the env/flag layers.
490
- */
799
+ * Read the project's `remote` preference, or `undefined` when there is no
800
+ * usable one — the caller then falls through to the env/flag layers.
801
+ */
491
802
  declare const readProjectRemotePreference: (projectRoot: string) => RemotePreference;
492
803
  /**
493
- * Whether we can interactively prompt — stdin must be a TTY. In CI / piped
494
- * contexts this is false, and callers should fall back to a non-interactive
495
- * default (skip, or require an explicit `--yes`) rather than hang on a read.
496
- */
804
+ * Read the project's deploy `target` from `lunora.config.*`, or `undefined` when
805
+ * absent.
806
+ *
807
+ * Delegates to `@lunora/codegen` rather than re-reading the file here.
808
+ * `runCodegen` has to resolve the same key without this package (config
809
+ * depends on codegen, not the reverse), so the parser lives there and this is a
810
+ * re-export — two readers of one key is exactly the kind of copy that drifts.
811
+ */
812
+ declare const readProjectTarget: (projectRoot: string) => string | undefined;
813
+ /**
814
+ * Resolve the deploy target for a command: an explicit `--target` wins, then
815
+ * `lunora.config.*`, then the registry default.
816
+ *
817
+ * **This is the canonical resolution point, and the reason it is one place.**
818
+ * Codegen tailors the emitted `ctx.*` surface to a target while deploy picks
819
+ * the driver that ships it. Resolve those separately and they can disagree,
820
+ * producing an app that builds cleanly and fails at runtime with nothing in the
821
+ * build to explain it. Every caller — CLI commands, the Vite plugin,
822
+ * `runCodegen`'s own fallback — resolves through here or through
823
+ * {@link resolveTargetOrThrow}, so there is exactly one precedence order to
824
+ * reason about.
825
+ */
826
+ declare const resolveProjectTarget: (projectRoot: string, explicit?: string) => string;
827
+ /**
828
+ * Resolve the deploy target as {@link resolveProjectTarget} does, then reject
829
+ * one that no registered driver serves.
830
+ *
831
+ * Lives here rather than in `@lunora/cli` because nothing about it is
832
+ * CLI-shaped and the Vite plugin needs the same guard — a validator only the
833
+ * CLI could reach left `vite build` emitting the default surface for a
834
+ * mis-declared target, silently.
835
+ *
836
+ * Callers that go on to resolve a driver get this for free; the ones that never
837
+ * look a driver up — codegen, the Vite plugin — need it, because otherwise
838
+ * nothing in their path ever rejects the name.
839
+ * @param projectRoot Directory containing `lunora.config.*`.
840
+ * @param explicit A caller-supplied target, if any.
841
+ * @throws when the resolved target names no registered driver.
842
+ * @returns the resolved, registered target id.
843
+ */
844
+ declare const resolveTargetOrThrow: (projectRoot: string, explicit?: string) => string;
845
+ /**
846
+ * Whether we can interactively prompt — stdin must be a TTY. In CI / piped
847
+ * contexts this is false, and callers should fall back to a non-interactive
848
+ * default (skip, or require an explicit `--yes`) rather than hang on a read.
849
+ */
497
850
  declare const isInteractive: () => boolean;
498
851
  /**
499
- * Ask a yes/no question on stdin. With `defaultYes`, an empty answer (just
500
- * Enter) counts as yes and the prompt should read `[Y/n]`; otherwise empty is
501
- * no (`[y/N]`). Shared by the CLI (`reset`, `dev`) and the Vite dev server.
502
- */
852
+ * Ask a yes/no question on stdin. With `defaultYes`, an empty answer (just
853
+ * Enter) counts as yes and the prompt should read `[Y/n]`; otherwise empty is
854
+ * no (`[y/N]`). Shared by the CLI (`reset`, `dev`) and the Vite dev server.
855
+ */
503
856
  declare const promptYesNo: (prompt: string, options?: {
504
857
  defaultYes?: boolean;
505
858
  }) => Promise<boolean>;
506
859
  /**
507
- * Build a default-yes `confirm(message)` for the scaffolders' `ensureDevVariables`:
508
- * an interactive `[Y/n]` prompt (optionally prefixed, e.g. `"[lunora] "`) when
509
- * stdin is a TTY, or an immediate `false` otherwise — so CI declines silently
510
- * instead of blocking. Keeps the "non-interactive ⇒ decline" policy in one place
511
- * rather than re-stated at every call site.
512
- */
860
+ * Build a default-yes `confirm(message)` for the scaffolders' `ensureDevVariables`:
861
+ * an interactive `[Y/n]` prompt (optionally prefixed, e.g. `"[lunora] "`) when
862
+ * stdin is a TTY, or an immediate `false` otherwise — so CI declines silently
863
+ * instead of blocking. Keeps the "non-interactive ⇒ decline" policy in one place
864
+ * rather than re-stated at every call site.
865
+ */
513
866
  declare const createConfirm: (prefix?: string) => ((message: string) => Promise<boolean>);
514
867
  /** One choice in a {@link promptSelect} list. `value` is returned; `label` (and optional `description`) are shown. */
515
868
  interface SelectOption<T extends string> {
@@ -518,272 +871,73 @@ interface SelectOption<T extends string> {
518
871
  value: T;
519
872
  }
520
873
  /**
521
- * Ask the user to pick one option from a numbered list on stdin. Accepts the
522
- * 1-based number or the option's `value`/`label` typed verbatim; an empty answer
523
- * (just Enter) takes `settings.default`. In a non-interactive context (CI /
524
- * piped — no TTY) it never reads and returns `settings.default` (or `undefined`),
525
- * mirroring {@link createConfirm}'s "non-interactive ⇒ fall back" policy so
526
- * automation never blocks.
527
- */
874
+ * Ask the user to pick one option from a numbered list on stdin. Accepts the
875
+ * 1-based number or the option's `value`/`label` typed verbatim; an empty answer
876
+ * (just Enter) takes `settings.default`. In a non-interactive context (CI /
877
+ * piped — no TTY) it never reads and returns `settings.default` (or `undefined`),
878
+ * mirroring {@link createConfirm}'s "non-interactive ⇒ fall back" policy so
879
+ * automation never blocks.
880
+ */
528
881
  declare const promptSelect: <T extends string>(message: string, options: ReadonlyArray<SelectOption<T>>, settings?: {
529
882
  default?: T;
530
883
  }) => Promise<T | undefined>;
884
+ /**
885
+ * Ask a free-text question on stdin, returning the trimmed answer. An empty
886
+ * answer (just Enter) takes `settings.default`. In a non-interactive context
887
+ * (CI / piped — no TTY) it never reads and returns `settings.default` (or
888
+ * `undefined`), mirroring {@link promptSelect}'s "non-interactive ⇒ fall back"
889
+ * policy so automation never blocks.
890
+ */
891
+ declare const promptText: (message: string, settings?: {
892
+ default?: string;
893
+ }) => Promise<string | undefined>;
531
894
  /** One choice in a {@link promptMultiSelect} list. Identical shape to {@link SelectOption}; `value` is returned when picked. */
532
895
  type MultiSelectOption<T extends string> = SelectOption<T>;
533
896
  /**
534
- * Ask the user to pick zero or more options from a numbered list on stdin.
535
- * Accepts a comma- or space-separated list of 1-based numbers and/or option
536
- * `value`/`label`s typed verbatim; an empty answer (just Enter) takes
537
- * `settings.defaults`. In a non-interactive context (CI / piped — no TTY) it
538
- * never reads and returns `settings.defaults ?? []`, mirroring {@link promptSelect}'s
539
- * "non-interactive ⇒ fall back" policy so automation never blocks. Unknown
540
- * tokens are ignored; the returned list is de-duplicated and preserves option
541
- * order.
542
- */
897
+ * Ask the user to pick zero or more options from a numbered list on stdin.
898
+ * Accepts a comma- or space-separated list of 1-based numbers and/or option
899
+ * `value`/`label`s typed verbatim; an empty answer (just Enter) takes
900
+ * `settings.defaults`. In a non-interactive context (CI / piped — no TTY) it
901
+ * never reads and returns `settings.defaults ?? []`, mirroring {@link promptSelect}'s
902
+ * "non-interactive ⇒ fall back" policy so automation never blocks. Unknown
903
+ * tokens are ignored; the returned list is de-duplicated and preserves option
904
+ * order.
905
+ */
543
906
  declare const promptMultiSelect: <T extends string>(message: string, options: ReadonlyArray<MultiSelectOption<T>>, settings?: {
544
907
  defaults?: ReadonlyArray<T>;
545
908
  }) => Promise<T[]>;
546
- /**
547
- * A container/workflow that is declared (so codegen emits its class) but the
548
- * worker entry never re-exports — the one wiring step the generators can't always
549
- * do for the developer. wrangler rejects a `class_name` the deployed worker
550
- * doesn't export, so a deploy fails late on this; surfacing it as structured data
551
- * lets the Vite plugin raise it in the dev error overlay (not just the console)
552
- * the moment the gap appears. The human-readable form is also folded into
553
- * {@link ReconcileBindingsResult.warnings}.
554
- */
555
- interface ExportGap {
556
- /** Generated class wrangler needs exported, e.g. `OrderPipelineWorkflow`. */
557
- className: string;
558
- /** The `lunora/{containers,workflows}.ts` export name, e.g. `orderPipeline`. */
559
- exportName: string;
560
- /** Which declaration is unexported. */
561
- kind: "container" | "workflow";
562
- /** The `_generated/{module}` to re-export from, e.g. `workflows`. */
563
- module: "containers" | "workflows";
564
- }
565
- interface ReconcileBindingsResult {
566
- /** Short labels for each binding written (e.g. `"SCHEDULER/SchedulerDO"`). */
567
- added: string[];
568
- /** `true` when `wrangler.jsonc` was rewritten. */
569
- changed: boolean;
570
- /**
571
- * Declared containers/workflows the worker entry doesn't re-export — the
572
- * structured form of the corresponding `warnings` entries, for the dev error
573
- * overlay. Empty when every declaration is wired.
574
- */
575
- exportGaps: ExportGap[];
576
- /** Reason reconciliation was skipped, for logging. */
577
- reason?: string;
578
- /** Non-fatal hints for capabilities that cannot be auto-provisioned. */
579
- warnings: string[];
580
- /** Resolved wrangler path, or `undefined` when none was found. */
581
- wranglerPath?: string;
582
- }
583
- /**
584
- * Reconcile inferred Durable Object / D1 bindings into `wrangler.jsonc`.
585
- *
586
- * Writes only when something is missing; returns `changed: false` when the
587
- * config already satisfies the inferred needs.
588
- */
589
- declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings) => ReconcileBindingsResult;
590
- /**
591
- * The wrangler config sections Lunora can safely flip to remote mode in dev,
592
- * each with the human label used in logs and the structural `shape` the entry
593
- * lives in.
594
- *
595
- * `"array"` is a top-level array of binding objects (`d1_databases`,
596
- * `kv_namespaces`, `r2_buckets`, `vectorize`, `services`). `"producers"` is
597
- * `queues.producers[]` — consumers are NOT remoted (their schema has no `remote`
598
- * field) and the edit path is two levels deep. `"object"` is a single binding
599
- * object, not an array (`ai`), whose edit path targets the section key directly.
600
- *
601
- * Every kind here was confirmed against `wrangler/config-schema.json`: the
602
- * entry's schema declares a `remote` property. Deliberately omits
603
- * `durable_objects` (no CF remote-DO mode; shards stay local) and sections whose
604
- * schema has no `remote` field (`hyperdrive`, `analytics_engine_datasets`,
605
- * `secrets_store_secrets`, queue consumers, …). Widening further is a one-line
606
- * table edit.
607
- */
608
- declare const REMOTE_ELIGIBLE_KEYS: {
609
- readonly ai: {
610
- readonly label: "AI";
611
- readonly shape: "object";
612
- };
613
- readonly d1_databases: {
614
- readonly label: "D1";
615
- readonly shape: "array";
616
- };
617
- readonly kv_namespaces: {
618
- readonly label: "KV";
619
- readonly shape: "array";
620
- };
621
- readonly queues: {
622
- readonly label: "Queue";
623
- readonly shape: "producers";
624
- };
625
- readonly r2_buckets: {
626
- readonly label: "R2";
627
- readonly shape: "array";
628
- };
629
- readonly services: {
630
- readonly label: "Service";
631
- readonly shape: "array";
632
- };
633
- readonly vectorize: {
634
- readonly label: "Vectorize";
635
- readonly shape: "array";
636
- };
637
- };
638
- type RemoteEligibleKey = keyof typeof REMOTE_ELIGIBLE_KEYS;
639
- /** One binding object as it appears in any eligible section. */
640
- interface BindingEntry {
641
- binding?: string;
642
- remote?: boolean;
643
- }
644
- /** One binding entry we mark remote, with enough provenance to log + edit it. */
645
- interface RemoteBindingPlan {
646
- /** The binding name as declared in the config (e.g. `"DB"`, `"FILES"`). */
647
- binding: string;
648
- /** Short kind label for logging (`"D1"`, `"KV"`, `"R2"`, `"Vectorize"`, …). */
649
- kind: string;
650
- /**
651
- * The jsonc edit path within {@link RemoteBindingPlan.section}, relative to
652
- * the section key: `[index]` for an `"array"` section, `["producers", index]`
653
- * for a queue producer, or `[]` for the single-object `ai` section. The
654
- * materializer prepends the section key and appends `"remote"`.
655
- */
656
- path: ReadonlyArray<number | string>;
657
- /** The wrangler config key the entry lives under. */
658
- section: RemoteEligibleKey;
659
- }
660
- /** The structural slice of a wrangler config the remote planner reads. */
661
- interface RemoteWranglerShape {
662
- ai?: BindingEntry | null;
663
- d1_databases?: ReadonlyArray<BindingEntry | null | undefined>;
664
- kv_namespaces?: ReadonlyArray<BindingEntry | null | undefined>;
665
- queues?: {
666
- producers?: ReadonlyArray<BindingEntry | null | undefined>;
667
- } | null;
668
- r2_buckets?: ReadonlyArray<BindingEntry | null | undefined>;
669
- services?: ReadonlyArray<BindingEntry | null | undefined>;
670
- vectorize?: ReadonlyArray<BindingEntry | null | undefined>;
671
- }
672
- /**
673
- * Inspect a parsed wrangler config and list every eligible binding that should
674
- * be flipped to remote mode. Pure — no file-system access, no mutation. An
675
- * entry already carrying `"remote": true` is still reported (so logging is
676
- * complete) but the materializer's edit is a harmless no-op for it.
677
- */
678
- declare const planRemoteBindings: (parsed: RemoteWranglerShape) => RemoteBindingPlan[];
679
- /**
680
- * Inject `"remote": true` onto each planned binding in the config `text`,
681
- * comment-preservingly via jsonc edits. Pure string→string; the edits target
682
- * disjoint entries so applying them sequentially is safe. The edit path is
683
- * `[section, ...plan.path, "remote"]`, which resolves to the array element, the
684
- * `queues.producers[i]` entry, or the single `ai` object as the plan demands.
685
- */
686
- declare const injectRemoteFlags: (text: string, plans: ReadonlyArray<RemoteBindingPlan>) => string;
687
- interface MaterializeOptions {
688
- /** When `false`, the call is a no-op (returns `enabled: false`). */
689
- enabled: boolean;
690
- projectRoot: string;
691
- }
692
- interface MaterializeResult {
693
- /**
694
- * Removes the generated temp config file. Always present and always safe to
695
- * call: it is idempotent, a no-op when nothing was written (disabled /
696
- * fall-through cases), and never throws if the path is already gone. The dev
697
- * command calls this on every exit path (normal, signal, error).
698
- */
699
- cleanup: () => void;
700
- /**
701
- * Absolute path to the generated temp config to pass to
702
- * `wrangler dev --config`. `undefined` when remote mode is disabled, no
703
- * wrangler config was found, it failed to parse, or it declared no eligible
704
- * binding (nothing to remote — run plain local dev).
705
- */
706
- configPath?: string;
707
- /** Whether remote mode was requested at all. */
708
- enabled: boolean;
709
- /** Why no temp config was produced, for logging (only set when none was). */
710
- reason?: string;
711
- /** The bindings flipped to remote, for the dev banner. */
712
- remoteBindings: RemoteBindingPlan[];
713
- }
714
- /**
715
- * Produce a temporary wrangler config with `"remote": true` on every eligible
716
- * binding, so `lunora dev` can run `wrangler dev --config &lt;temp>` against the
717
- * deployed D1/KV/R2 without touching the user's file.
718
- *
719
- * The temp file is written as a sibling of the source `wrangler.jsonc` (in the
720
- * project root), NOT an OS temp dir: wrangler resolves a config's relative paths
721
- * (`main`, `assets`, `migrations_dir`, …) against the **config file's own
722
- * directory**, so a temp config in `/tmp` would make wrangler look for
723
- * `/tmp/src/server.ts` and fail to start the worker. Keeping it beside the real
724
- * config preserves those relative paths. Returns `configPath: undefined` (with a
725
- * `reason`) for every fall-through case so the caller degrades to plain local
726
- * dev instead of failing.
727
- */
728
- declare const materializeRemoteWranglerConfig: (options: MaterializeOptions) => MaterializeResult;
729
- /**
730
- * Parse a `LUNORA_REMOTE` env value into the on/off decision. Truthy when set to
731
- * `"1"` or `"true"` (case-insensitive); anything else — unset, `"0"`, `"false"`,
732
- * empty — is off. Mirrors the `"1" | "true"` convention used across the runtime.
733
- */
734
- declare const isRemoteEnvEnabled: (value: string | undefined) => boolean;
735
- /** The three inputs that can switch remote-binding dev on, in precedence order. */
736
- interface RemoteEnableInputs {
737
- /**
738
- * The `remote` preference from `lunora.json` (the lowest-priority signal).
739
- * `undefined` means "no project preference"; an explicit `false` here loses
740
- * to neither the flag nor the env when those are absent — it just stays off.
741
- */
742
- configPreference?: boolean;
743
- /** The raw `LUNORA_REMOTE` env value (parsed with {@link isRemoteEnvEnabled}). */
744
- envValue?: string;
745
- /** The explicit `--remote` CLI flag — `true` when passed, `undefined`/`false` otherwise. */
746
- flag?: boolean;
747
- }
748
- /**
749
- * Resolve whether remote-binding dev is on, with a clear precedence:
750
- *
751
- * 1. an explicit `--remote` flag (highest — a deliberate per-invocation choice),
752
- * 2. then `LUNORA_REMOTE` in the environment,
753
- * 3. then the `remote` key in `lunora.json` (lowest — a project default).
754
- *
755
- * The flag and env are one-directional (they can only turn remote *on*); only
756
- * the config preference carries a meaningful `false`, and it applies solely when
757
- * neither stronger signal is present. So a project that sets `"remote": false`
758
- * is still overridable per-run by `--remote` or `LUNORA_REMOTE=1`.
759
- */
760
- declare const resolveRemoteEnabled: (inputs: RemoteEnableInputs) => boolean;
761
909
  /** Core (always-scaffolded) secrets followed by the package-specific ones for the detected capabilities. */
762
910
  declare const requiredSecrets: (packageNames: ReadonlyArray<string>) => SecretEntry[];
763
911
  /**
764
- * Whether an (already-unquoted) value looks like a fill-me-in placeholder —
765
- * empty, angle-bracketed, or containing a known marker — rather than a real
766
- * value. Used both when scaffolding (which values to regenerate) and by
767
- * `lunora env doctor` (which set values are still unfilled).
768
- */
912
+ * Whether an (already-unquoted) value looks like a fill-me-in placeholder —
913
+ * empty, angle-bracketed, or containing a known marker — rather than a real
914
+ * value. Used both when scaffolding (which values to regenerate) and by
915
+ * `lunora env doctor` (which set values are still unfilled).
916
+ */
769
917
  declare const isPlaceholderValue: (value: string) => boolean;
770
918
  /**
771
- * True for a secret-looking key whose value Lunora can mint locally (a random
772
- * 32-byte hex, like `openssl rand -hex 32`) — e.g. `AUTH_SECRET`,
773
- * `LUNORA_ADMIN_TOKEN`, `STORAGE_SIGNING_SECRET`. False for provider-issued keys
774
- * ({@link PROVIDER_SECRET_KEYS}) and any non-secret key.
775
- */
919
+ * True for a secret key the registry says Lunora can mint locally (a random
920
+ * 32-byte hex, like `openssl rand -hex 32`) — `AUTH_SECRET`,
921
+ * `LUNORA_ADMIN_TOKEN`, `STORAGE_SIGNING_SECRET`, …
922
+ *
923
+ * Registry membership, NOT the key's name shape, is the rule. A secret-looking
924
+ * key nothing in the registry declares (`OPENAI_API_KEY`,
925
+ * `GITHUB_CLIENT_SECRET`, a project's own `*_TOKEN`) is provider-issued as far
926
+ * as Lunora knows, so minting for it would write a value the provider rejects at
927
+ * runtime — over the developer's own half-filled `.dev.vars` — and hide the gap
928
+ * from `lunora env doctor`, whose job is to report it as unfilled.
929
+ */
776
930
  declare const isMintableSecretKey: (key: string) => boolean;
777
931
  /** Mint a fresh strong secret value — 64 hex chars (32 bytes), like `openssl rand -hex 32`. */
778
932
  declare const generateSecretValue: (randomHex?: (bytes: number) => string) => string;
779
933
  /**
780
- * The outcome of planning a scaffold — a discriminated union so the orchestrator
781
- * never has to re-derive whether `content` is present.
782
- *
783
- * `exists`: `.dev.vars` is already there; nothing to do.
784
- * `no-example`: nothing to scaffold from (stay silent — the project may not use secrets).
785
- * `generate`: write `content`, a copy of the example with secret-looking placeholders replaced by fresh random hex (`generatedKeys` lists which).
786
- */
934
+ * The outcome of planning a scaffold — a discriminated union so the orchestrator
935
+ * never has to re-derive whether `content` is present.
936
+ *
937
+ * `exists`: `.dev.vars` is already there; nothing to do.
938
+ * `no-example`: nothing to scaffold from (stay silent — the project may not use secrets).
939
+ * `generate`: write `content`, a copy of the example with secret-looking placeholders replaced by fresh random hex (`generatedKeys` lists which).
940
+ */
787
941
  type ScaffoldPlan = {
788
942
  content: string;
789
943
  generatedKeys: string[];
@@ -796,7 +950,8 @@ type ScaffoldPlan = {
796
950
  /** Decide whether (and what) to scaffold. Pure — given the current state of the two files. */
797
951
  declare const planDevVariablesScaffold: (input: {
798
952
  devVarsExists: boolean;
799
- exampleContent: string | undefined; /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
953
+ exampleContent: string | undefined;
954
+ /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
800
955
  randomHex?: (bytes: number) => string;
801
956
  }) => ScaffoldPlan;
802
957
  interface AugmentPlan {
@@ -808,22 +963,23 @@ interface AugmentPlan {
808
963
  missingKeys: string[];
809
964
  }
810
965
  /**
811
- * Plan how to top up an existing `.dev.vars` from the example: every example key
812
- * not already present becomes an appended line (secret placeholders filled with
813
- * fresh random hex, other values copied). Pure — no I/O. Empty `missingKeys`
814
- * means the file is already complete.
815
- */
966
+ * Plan how to top up an existing `.dev.vars` from the example: every example key
967
+ * not already present becomes an appended line (secret placeholders filled with
968
+ * fresh random hex, other values copied). Pure — no I/O. Empty `missingKeys`
969
+ * means the file is already complete.
970
+ */
816
971
  declare const planDevVariablesAugment: (input: {
817
972
  exampleContent: string;
818
- existingContent: string; /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
973
+ existingContent: string;
974
+ /** Injectable for deterministic tests; defaults to `crypto.randomBytes`. */
819
975
  randomHex?: (bytes: number) => string;
820
976
  }) => AugmentPlan;
821
977
  interface EnsureDevVariablesDeps {
822
978
  /**
823
- * Ask the user to confirm generating the file. Return `true` to generate.
824
- * Consumers pass a TTY-aware prompt; in non-interactive contexts they should
825
- * resolve `false` (we then report `"declined"` and the caller can hint).
826
- */
979
+ * Ask the user to confirm generating the file. Return `true` to generate.
980
+ * Consumers pass a TTY-aware prompt; in non-interactive contexts they should
981
+ * resolve `false` (we then report `"declined"` and the caller can hint).
982
+ */
827
983
  confirm: (message: string) => Promise<boolean>;
828
984
  cwd: string;
829
985
  /** Emit a human-facing line (success / hint). */
@@ -842,41 +998,51 @@ interface EnsureDevVariablesResult {
842
998
  status: EnsureDevVariablesStatus;
843
999
  }
844
1000
  /**
845
- * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
846
- *
847
- * - file missing → offer to generate it (secret placeholders auto-filled);
848
- * - file present but missing keys the example lists → offer to append them;
849
- * - file present and complete → nothing to do.
850
- *
851
- * Prompts via `confirm` (skipped when `yes`); never overwrites existing values.
852
- * Returns what happened so the caller can tailor any follow-up. Shared by
853
- * `lunora dev` and the `@lunora/vite` dev server. All side effects funnel
854
- * through `confirm`/`info`/`randomHex`.
855
- */
1001
+ * Atomically (over)write a `.dev.vars`-shaped file, owner-only. For a file
1002
+ * holding secrets a torn write would destroy every other local value alongside
1003
+ * the new one, and the new value itself has no other recoverable copy if it
1004
+ * was never disclosed anywhere else (Cloudflare secrets are write-only).
1005
+ * Exported so a caller writing to a `.dev.vars`-shaped path outside this
1006
+ * module (`lunora deploy`'s minted-secret disclosure) reuses this instead of
1007
+ * hand-rolling another copy of the pattern.
1008
+ */
1009
+ declare const writeDevVariablesFileAtomically: (path: string, content: string) => void;
1010
+ /**
1011
+ * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
1012
+ *
1013
+ * - file missing → offer to generate it (secret placeholders auto-filled);
1014
+ * - file present but missing keys the example lists → offer to append them;
1015
+ * - file present and complete → nothing to do.
1016
+ *
1017
+ * Prompts via `confirm` (skipped when `yes`); never overwrites existing values.
1018
+ * Returns what happened so the caller can tailor any follow-up. Shared by
1019
+ * `lunora dev` and the `@lunora/vite` dev server. All side effects funnel
1020
+ * through `confirm`/`info`/`randomHex`.
1021
+ */
856
1022
  declare const ensureDevVariables: (deps: EnsureDevVariablesDeps) => Promise<EnsureDevVariablesResult>;
857
1023
  /**
858
- * Build the text that should be merged into `.dev.vars.example` for the given
859
- * set of package names. Only entries whose key is not already present in
860
- * `existingKeys` are included (additive / idempotent). Returns an empty string
861
- * when there is nothing to add.
862
- *
863
- * The output is grouped by package with a blank-line separator so the file
864
- * reads cleanly when multiple packages each contribute several keys.
865
- *
866
- * **Safety invariant:** this function never writes a real secret — every value
867
- * in the output is the entry's `placeholderValue`.
868
- */
1024
+ * Build the text that should be merged into `.dev.vars.example` for the given
1025
+ * set of package names. Only entries whose key is not already present in
1026
+ * `existingKeys` are included (additive / idempotent). Returns an empty string
1027
+ * when there is nothing to add.
1028
+ *
1029
+ * The output is grouped by package with a blank-line separator so the file
1030
+ * reads cleanly when multiple packages each contribute several keys.
1031
+ *
1032
+ * **Safety invariant:** this function never writes a real secret — every value
1033
+ * in the output is the entry's `placeholderValue`.
1034
+ */
869
1035
  declare const buildPackageSecretsBlock: (packageNames: ReadonlyArray<string>, existingKeys: ReadonlySet<string>) => string;
870
1036
  /**
871
- * Write (or update) `.dev.vars.example` so that it contains the secrets
872
- * required by `packageNames`. Existing lines are never removed or rewritten;
873
- * new entries are appended (with a blank-line separator after existing content).
874
- *
875
- * Idempotent: re-running with the same `packageNames` does not duplicate keys
876
- * already in the file. Returns the list of keys that were actually appended.
877
- *
878
- * **Safety invariant:** only placeholder values are written — no real secrets.
879
- */
1037
+ * Write (or update) `.dev.vars.example` so that it contains the secrets
1038
+ * required by `packageNames`. Existing lines are never removed or rewritten;
1039
+ * new entries are appended (with a blank-line separator after existing content).
1040
+ *
1041
+ * Idempotent: re-running with the same `packageNames` does not duplicate keys
1042
+ * already in the file. Returns the list of keys that were actually appended.
1043
+ *
1044
+ * **Safety invariant:** only placeholder values are written — no real secrets.
1045
+ */
880
1046
  declare const ensureDevVariablesExample: (cwd: string, packageNames: ReadonlyArray<string>) => string[];
881
1047
  interface DevSecretsFillPlan {
882
1048
  /** {@link CORE_SECRETS} keys appended because they were absent (each generated). */
@@ -887,19 +1053,21 @@ interface DevSecretsFillPlan {
887
1053
  filledKeys: string[];
888
1054
  }
889
1055
  /**
890
- * Plan the in-place generation of dev secrets for a `.dev.vars`. First, every
891
- * line whose KEY looks like a secret (`*_SECRET`, `*_TOKEN`, `*_KEY`,
892
- * `*_PASSWORD`) and whose value is empty or a placeholder gets a freshly
893
- * generated value — so a `lunora add`-scaffolded `.dev.vars` (which writes each
894
- * secret blank) becomes usable on `lunora dev` / `vite dev` without the user
895
- * running `openssl` by hand. Second, any {@link CORE_SECRETS} key absent from
896
- * the file is appended (generated) — notably `LUNORA_ADMIN_TOKEN`, which the
897
- * local Studio needs to call the worker's admin gate in dev (without it the
898
- * Studio shows its login gate).
899
- *
900
- * Pure (given `randomHex`): real (non-placeholder) values are never touched, and
901
- * comments + non-secret entries are preserved verbatim.
902
- */
1056
+ * Plan the in-place generation of dev secrets for a `.dev.vars`. First, every
1057
+ * line whose key is {@link isMintableSecretKey mintable} — registry membership,
1058
+ * NOT the key's name shape — and whose value is empty or a placeholder gets a
1059
+ * freshly generated value, so a `lunora add`-scaffolded `.dev.vars` (which
1060
+ * writes each secret blank) becomes usable on `lunora dev` / `vite dev` without
1061
+ * the user running `openssl` by hand. A provider-issued key (`OPENAI_API_KEY`,
1062
+ * `STRIPE_SECRET_KEY`) is left verbatim so `lunora env doctor` can still report
1063
+ * it as unfilled. Second, any {@link CORE_SECRETS} key absent from
1064
+ * the file is appended (generated) — notably `LUNORA_ADMIN_TOKEN`, which the
1065
+ * local Studio needs to call the worker's admin gate in dev (without it the
1066
+ * Studio shows its login gate).
1067
+ *
1068
+ * Pure (given `randomHex`): real (non-placeholder) values are never touched, and
1069
+ * comments + non-secret entries are preserved verbatim.
1070
+ */
903
1071
  declare const planDevSecretsFill: (input: {
904
1072
  existingContent: string;
905
1073
  randomHex?: (bytes: number) => string;
@@ -913,25 +1081,35 @@ interface FillDevSecretsResult {
913
1081
  status: "created" | "filled" | "unchanged";
914
1082
  }
915
1083
  /**
916
- * Generate any missing/empty dev secrets in the project's `.dev.vars`, in place.
917
- *
918
- * Complements {@link ensureDevVariables} (which scaffolds `.dev.vars` from
919
- * `.dev.vars.example`). A `lunora add`-scaffolded project writes secrets blank
920
- * straight into `.dev.vars` (no example) and never includes `LUNORA_ADMIN_TOKEN`
921
- * — so the worker boots with empty secrets and the Studio shows its login gate.
922
- * This fills those gaps at dev startup, so both `lunora dev` and the
923
- * `@lunora/vite` dev server give a working project with zero manual `openssl`.
924
- *
925
- * Never overwrites a real (non-placeholder) value. The write is atomic + owner-
926
- * only (temp + rename, `mode: 0o600`), matching the other `.dev.vars` writers.
927
- */
1084
+ * Generate any missing/empty dev secrets in the project's `.dev.vars`, in place.
1085
+ *
1086
+ * Complements {@link ensureDevVariables} (which scaffolds `.dev.vars` from
1087
+ * `.dev.vars.example`). A `lunora add`-scaffolded project writes secrets blank
1088
+ * straight into `.dev.vars` (no example) and never includes `LUNORA_ADMIN_TOKEN`
1089
+ * — so the worker boots with empty secrets and the Studio shows its login gate.
1090
+ * This fills those gaps at dev startup, so both `lunora dev` and the
1091
+ * `@lunora/vite` dev server give a working project with zero manual `openssl`.
1092
+ *
1093
+ * Never overwrites a real (non-placeholder) value. The write is atomic + owner-
1094
+ * only (temp + rename, `mode: 0o600`), matching the other `.dev.vars` writers.
1095
+ */
928
1096
  declare const fillDevSecrets: (deps: {
929
1097
  cwd: string;
930
1098
  info?: (message: string) => void;
931
1099
  randomHex?: (bytes: number) => string;
932
1100
  }) => FillDevSecretsResult;
1101
+ /** Storage backends `.global()` accepts. A closed union, so it is never free text. */
1102
+ type GlobalBackend = "d1" | "hyperdrive";
933
1103
  /** Add a new table to `defineSchema({ ... })`. */
934
1104
  interface AddTableEdit {
1105
+ /**
1106
+ * Mark the table `.global()`. Pass `{}` for the D1 default, or a backend for
1107
+ * an external store — `lunora introspect` uses `{ backend: "hyperdrive" }`
1108
+ * because the rows live in the database it read.
1109
+ */
1110
+ readonly global?: {
1111
+ readonly backend?: GlobalBackend;
1112
+ };
935
1113
  readonly kind: "addTable";
936
1114
  readonly table: string;
937
1115
  }
@@ -941,11 +1119,11 @@ interface AddOptionalColumnEdit {
941
1119
  readonly kind: "addOptionalColumn";
942
1120
  readonly table: string;
943
1121
  /**
944
- * Inner validator expression text WITHOUT the `v.optional(...)` wrapper, e.g.
945
- * `v.string()`. Always wrapped in `v.optional(...)` on apply, because only
946
- * optional columns are additive-safe (a required column needs a backfill
947
- * migration).
948
- */
1122
+ * Inner validator expression text WITHOUT the `v.optional(...)` wrapper, e.g.
1123
+ * `v.string()`. Always wrapped in `v.optional(...)` on apply, because only
1124
+ * optional columns are additive-safe (a required column needs a backfill
1125
+ * migration).
1126
+ */
949
1127
  readonly validator: string;
950
1128
  }
951
1129
  /** Add a secondary index to an existing table. */
@@ -959,9 +1137,9 @@ interface AddIndexEdit {
959
1137
  /** Additive edits — the only requests {@link applyAdditiveEdit} applies. */
960
1138
  type AdditiveEdit = AddIndexEdit | AddOptionalColumnEdit | AddTableEdit;
961
1139
  /**
962
- * Destructive edits — never applied directly; routed to the migration handoff
963
- * (plan 024 Item 5). Carried as data so the editor can describe the request.
964
- */
1140
+ * Destructive edits — never applied directly; routed to the migration handoff
1141
+ * (plan 024 Item 5). Carried as data so the editor can describe the request.
1142
+ */
965
1143
  interface DestructiveEdit {
966
1144
  readonly column?: string;
967
1145
  readonly kind: "changeColumnType" | "dropColumn" | "dropTable" | "makeRequired" | "renameColumn";
@@ -972,9 +1150,9 @@ interface DestructiveEdit {
972
1150
  /** Any edit the editor can request. */
973
1151
  type SchemaEdit = AdditiveEdit | DestructiveEdit;
974
1152
  /**
975
- * Classify an edit request. Additive edits ({@link AdditiveEdit}) apply
976
- * directly; everything else changes stored data and is destructive.
977
- */
1153
+ * Classify an edit request. Additive edits ({@link AdditiveEdit}) apply
1154
+ * directly; everything else changes stored data and is destructive.
1155
+ */
978
1156
  declare const classifyEdit: (edit: SchemaEdit) => "additive" | "destructive";
979
1157
  /** Failure reasons an additive edit can report. */
980
1158
  type ApplyFailureReason = "aliased-define-schema" | "destructive" | "duplicate-column" | "duplicate-index" | "duplicate-table" | "invalid-identifier" | "invalid-validator" | "no-define-schema" | "non-object-argument" | "unknown-table";
@@ -987,10 +1165,10 @@ type ApplyEditResult = {
987
1165
  text: string;
988
1166
  };
989
1167
  /**
990
- * Apply an **additive** edit to a schema source string, preserving formatting.
991
- * Destructive edits are refused with `{ ok: false, reason: "destructive" }`;
992
- * route them through the migration handoff (plan 024 Item 5).
993
- */
1168
+ * Apply an **additive** edit to a schema source string, preserving formatting.
1169
+ * Destructive edits are refused with `{ ok: false, reason: "destructive" }`;
1170
+ * route them through the migration handoff (plan 024 Item 5).
1171
+ */
994
1172
  declare const applyAdditiveEdit: (source: string, edit: SchemaEdit) => ApplyEditResult;
995
1173
  /** A single declared column: its name and the raw validator expression text. */
996
1174
  interface SchemaColumn {
@@ -1029,40 +1207,15 @@ type ParseSchemaResult = {
1029
1207
  tables: ReadonlyArray<SchemaTable>;
1030
1208
  };
1031
1209
  /**
1032
- * Every `CallExpression` at or below a node. `getDescendantsOfKind` excludes the
1033
- * node itself, but a table initializer often is the outermost call in the
1034
- * `defineTable(...).global().index(...)` chain, so include it explicitly.
1035
- */
1036
-
1037
- /**
1038
- * Parse the tables (with typed columns + indexes) out of a `lunora/schema.ts`
1039
- * source string. Returns a tagged result so callers can render a helpful
1040
- * message per failure mode without throwing.
1041
- */
1210
+ * Parse the tables (with typed columns + indexes) out of a `lunora/schema.ts`
1211
+ * source string. Returns a tagged result so callers can render a helpful
1212
+ * message per failure mode without throwing.
1213
+ */
1042
1214
  declare const parseSchema: (source: string) => ParseSchemaResult;
1043
- interface SchemaInfo {
1044
- /** Whether the lunora schema declares any `.global()` table. */
1045
- hasGlobalTable: boolean;
1046
- /** Names of vector indexes declared via `.vectorize()` / `defineVectorIndex()`. */
1047
- vectorIndexNames?: ReadonlyArray<string>;
1048
- }
1049
- interface DiscoverSchemaInfoResult {
1050
- /** Parse error message, when the schema exists but could not be analyzed. */
1051
- error?: string;
1052
- /** Schema facts, or `undefined` when no `schema.ts` exists or parsing failed. */
1053
- info: SchemaInfo | undefined;
1054
- }
1055
- /**
1056
- * Discover {@link SchemaInfo} for a project. Returns `{ info: undefined }` when
1057
- * the project declares no `schema.ts` (not an error), or `{ info: undefined,
1058
- * error }` when a present schema could not be parsed — callers decide whether a
1059
- * parse failure is a warning (validator) or simply ignorable (inference).
1060
- */
1061
- declare const discoverSchemaInfo: (projectRoot: string, schemaDirectory: string) => DiscoverSchemaInfoResult;
1062
1215
  /**
1063
- * A badge: the short colored label that prefixes a line. `bg`/`fg` are hex so the
1064
- * same value drives both colorize's `bgHex().hex()` and the tui `&lt;Text>` props.
1065
- */
1216
+ * A badge: the short colored label that prefixes a line. `bg`/`fg` are hex so the
1217
+ * same value drives both colorize's `bgHex().hex()` and the tui `<Text>` props.
1218
+ */
1066
1219
  interface BadgeSpec {
1067
1220
  bg: `#${string}`;
1068
1221
  fg: `#${string}`;
@@ -1078,24 +1231,24 @@ type BadgeName = LevelBadgeName | StepBadgeName;
1078
1231
  /** The ordered step-phase names, used to register custom pail log types. */
1079
1232
  declare const STEP_BADGE_NAMES: ReadonlyArray<StepBadgeName>;
1080
1233
  /**
1081
- * Every badge, keyed by name. Levels get their conventional colors (red/amber/
1082
- * green/blue/grey); step phases follow create-astro's green→purple→cyan rhythm.
1083
- */
1234
+ * Every badge, keyed by name. Levels get their conventional colors (red/amber/
1235
+ * green/blue/grey); step phases follow create-astro's green→purple→cyan rhythm.
1236
+ */
1084
1237
  declare const BADGES: Record<BadgeName, BadgeSpec>;
1085
1238
  /**
1086
- * Luna, the mascot: the folklore rabbit-in-the-moon — a bunny tucked inside the
1087
- * moon disc. Pure ASCII so it renders the same everywhere (including piped logs).
1088
- * The CLI signs off the `init` flow with it, the way create-astro closes with
1089
- * Houston.
1090
- */
1239
+ * Luna, the mascot: the folklore rabbit-in-the-moon — a bunny tucked inside the
1240
+ * moon disc. Pure ASCII so it renders the same everywhere (including piped logs).
1241
+ * The CLI signs off the `init` flow with it, the way create-astro closes with
1242
+ * Houston.
1243
+ */
1091
1244
  declare const LUNA_NAME = "Luna";
1092
1245
  declare const LUNA_SIGNOFF = "Safe travels, voyager.";
1093
1246
  declare const LUNA_BUNNY: string;
1094
1247
  /**
1095
- * {@link LUNA_BUNNY} with its leading newline stripped, ready to render inline
1096
- * (beside the name + sign-off). Both render paths — the tui mascot frame and the
1097
- * pail off-TTY fallback — use this so neither re-implements the strip.
1098
- */
1248
+ * {@link LUNA_BUNNY} with its leading newline stripped, ready to render inline
1249
+ * (beside the name + sign-off). Both render paths — the tui mascot frame and the
1250
+ * pail off-TTY fallback — use this so neither re-implements the strip.
1251
+ */
1099
1252
  declare const LUNA_ART: string;
1100
1253
  /** The colored part of a badge — the word with one space of padding each side. */
1101
1254
  declare const padBadge: (text: string) => string;
@@ -1109,214 +1262,4 @@ declare const badgeWidth: (_spec: BadgeSpec) => number;
1109
1262
  declare const paintBadge: (spec: BadgeSpec) => string;
1110
1263
  /** Dim continuation text (a step's chosen answer, shown under the question). */
1111
1264
  declare const paintAnswer: (text: string) => string;
1112
- /** Candidate wrangler config filenames, in the order every consumer probes them. */
1113
- declare const WRANGLER_FILES: readonly ["wrangler.jsonc", "wrangler.json"];
1114
- /** Locate the project's wrangler config, or `undefined` when none exists. */
1115
- declare const findWranglerFile: (projectRoot: string) => string | undefined;
1116
- interface ReadWranglerResult<T> {
1117
- /** Parsed config, or `undefined` when the file was not valid JSONC. */
1118
- parsed: T | undefined;
1119
- /** Raw file text — needed for comment-preserving `modify`/`applyEdits`. */
1120
- text: string;
1121
- }
1122
- /**
1123
- * Read and JSONC-parse a wrangler config file. Returns the raw `text` (for
1124
- * structural edits) alongside `parsed`, which is `undefined` when the file is
1125
- * not valid JSONC or does not parse to an object. Allows trailing commas, as
1126
- * wrangler does.
1127
- */
1128
- declare const readWranglerJsonc: <T = unknown>(wranglerPath: string) => ReadWranglerResult<T>;
1129
- declare const REQUIRED_COMPATIBILITY_DATE: string;
1130
- declare const REQUIRED_FLAG: string;
1131
- interface WranglerDurableObjectBinding {
1132
- class_name?: string;
1133
- name?: string;
1134
- }
1135
- /**
1136
- * A `tail_consumers` entry: a Worker that receives this Worker's tail events
1137
- * (logs, exceptions, fetch metadata) for forwarding to an external sink. See
1138
- * `withTailConsumer` for the wiring helper.
1139
- */
1140
- interface TailConsumer {
1141
- /** Optional Cloudflare environment of the consumer Worker. */
1142
- environment?: string;
1143
- /** Name of the Worker that consumes tail events. */
1144
- service?: string;
1145
- }
1146
- /** A wrangler `containers[]` entry (parsed from untrusted JSONC). */
1147
- interface WranglerContainerEntry {
1148
- class_name?: string;
1149
- image?: string;
1150
- instance_type?: string | {
1151
- disk_mb?: number;
1152
- memory_mib?: number;
1153
- vcpu?: number;
1154
- };
1155
- max_instances?: number;
1156
- }
1157
- /**
1158
- * A wrangler `workflows[]` entry (parsed from untrusted JSONC). Unlike
1159
- * containers, workflows are NOT Durable Objects — the entry stands alone (no
1160
- * `durable_objects` binding, no migration class).
1161
- */
1162
- interface WranglerWorkflowEntry {
1163
- binding?: string;
1164
- class_name?: string;
1165
- name?: string;
1166
- }
1167
- /** A wrangler `queues.producers[]` entry — a `Queue` binding sending to `queue`. */
1168
- interface WranglerQueueProducer {
1169
- binding?: string;
1170
- delivery_delay?: number;
1171
- queue?: string;
1172
- }
1173
- /** A wrangler `queues.consumers[]` entry — push (worker) or `type: "http_pull"`. */
1174
- interface WranglerQueueConsumer {
1175
- dead_letter_queue?: string;
1176
- max_batch_size?: number;
1177
- max_batch_timeout?: number;
1178
- max_retries?: number;
1179
- queue?: string;
1180
- retry_delay?: number;
1181
- type?: string;
1182
- }
1183
- interface WranglerConfig {
1184
- analytics_engine_datasets?: ReadonlyArray<{
1185
- binding?: string;
1186
- dataset?: string;
1187
- } | null | undefined>;
1188
- assets?: {
1189
- binding?: string;
1190
- directory?: string;
1191
- html_handling?: string;
1192
- not_found_handling?: string;
1193
- };
1194
- browser?: {
1195
- binding?: string;
1196
- };
1197
- compatibility_date?: string;
1198
- compatibility_flags?: ReadonlyArray<string>;
1199
- containers?: ReadonlyArray<WranglerContainerEntry | null | undefined>;
1200
- d1_databases?: ReadonlyArray<{
1201
- binding?: string;
1202
- }>;
1203
- dispatch_namespaces?: ReadonlyArray<{
1204
- binding?: string;
1205
- namespace?: string;
1206
- outbound?: unknown;
1207
- } | null | undefined>;
1208
- durable_objects?: {
1209
- bindings?: ReadonlyArray<WranglerDurableObjectBinding>;
1210
- };
1211
- flagship?: ReadonlyArray<{
1212
- app_id?: string;
1213
- binding?: string;
1214
- } | null | undefined>;
1215
- hyperdrive?: ReadonlyArray<{
1216
- binding?: string;
1217
- id?: string;
1218
- localConnectionString?: string;
1219
- } | null | undefined>;
1220
- images?: {
1221
- binding?: string;
1222
- };
1223
- kv_namespaces?: ReadonlyArray<{
1224
- binding?: string;
1225
- id?: string;
1226
- } | null | undefined>;
1227
- logpush?: boolean;
1228
- migrations?: ReadonlyArray<{
1229
- new_classes?: ReadonlyArray<string>;
1230
- new_sqlite_classes?: ReadonlyArray<string>;
1231
- } | null | undefined>;
1232
- mtls_certificates?: ReadonlyArray<{
1233
- binding?: string;
1234
- certificate_id?: string;
1235
- } | null | undefined>;
1236
- observability?: {
1237
- enabled?: boolean;
1238
- head_sampling_rate?: number;
1239
- logs?: {
1240
- enabled?: boolean;
1241
- head_sampling_rate?: number;
1242
- };
1243
- };
1244
- pipelines?: ReadonlyArray<{
1245
- binding?: string;
1246
- pipeline?: string;
1247
- } | null | undefined>;
1248
- placement?: {
1249
- mode?: string;
1250
- };
1251
- queues?: {
1252
- consumers?: ReadonlyArray<WranglerQueueConsumer | null | undefined>;
1253
- producers?: ReadonlyArray<WranglerQueueProducer | null | undefined>;
1254
- };
1255
- r2_buckets?: ReadonlyArray<{
1256
- binding?: string;
1257
- }>;
1258
- secrets_store_secrets?: ReadonlyArray<{
1259
- binding?: string;
1260
- secret_name?: string;
1261
- store_id?: string;
1262
- } | null | undefined>;
1263
- send_email?: ReadonlyArray<{
1264
- allowed_destination_addresses?: ReadonlyArray<string>;
1265
- destination_address?: string;
1266
- name?: string;
1267
- } | null | undefined>;
1268
- services?: ReadonlyArray<{
1269
- binding?: string;
1270
- entrypoint?: string;
1271
- environment?: string;
1272
- service?: string;
1273
- } | null | undefined>;
1274
- tail_consumers?: ReadonlyArray<TailConsumer | null | undefined>;
1275
- vars?: Record<string, unknown>;
1276
- vectorize?: ReadonlyArray<{
1277
- binding?: string;
1278
- index_name?: string;
1279
- } | null | undefined>;
1280
- workflows?: ReadonlyArray<WranglerWorkflowEntry | null | undefined>;
1281
- }
1282
- interface WranglerValidationReport {
1283
- errors: string[];
1284
- valid: boolean;
1285
- warnings: string[];
1286
- }
1287
- /**
1288
- * Return a new `WranglerConfig` with `consumer` present in `tail_consumers`,
1289
- * wiring this Worker to forward its tail events (logs/exceptions) to another
1290
- * Worker that fans them out to an external sink. Pure and idempotent: an
1291
- * existing entry with the same `service` + `environment` is left untouched
1292
- * rather than duplicated, so it is safe to call on every codegen/deploy.
1293
- */
1294
- declare const withTailConsumer: (wrangler: WranglerConfig, consumer: TailConsumer) => WranglerConfig;
1295
- /**
1296
- * Pure validator: given a parsed `WranglerConfig` object and an optional
1297
- * `SchemaInfo`, produce a structured report. Performs no I/O.
1298
- */
1299
- declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, schema?: SchemaInfo) => WranglerValidationReport;
1300
- /**
1301
- * Convenience alias matching the original task-spec signature
1302
- * `validateWrangler(wranglerJson, schema)` returning
1303
- * `{ valid, errors, warnings }`.
1304
- */
1305
- declare const validateWrangler: typeof validateWranglerConfig;
1306
- interface WranglerProjectValidationOptions {
1307
- projectRoot: string;
1308
- schemaDir?: string;
1309
- }
1310
- interface WranglerProjectValidationResult {
1311
- problems: ReadonlyArray<string>;
1312
- report: WranglerValidationReport;
1313
- wranglerPath: string | undefined;
1314
- }
1315
- /**
1316
- * File-system aware variant: reads `wrangler.jsonc`/`wrangler.json` from
1317
- * the given project root, discovers the schema (if any), and delegates to
1318
- * `validateWranglerConfig`. Returns the legacy
1319
- * `{ problems, wranglerPath }` shape plus the structured `report`.
1320
- */
1321
- declare const validateWranglerProject: (options: WranglerProjectValidationOptions) => WranglerProjectValidationResult;
1322
- 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, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DiscoverContainerInfoResult, type DiscoverSchemaInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type ExportGap, type FillDevSecretsResult, 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, fillDevSecrets, findWranglerFile, formatLunoraEvent, generateSecretValue, inferLunoraBindings, injectRemoteFlags, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isRemoteEnvEnabled, materializeRemoteWranglerConfig, packageNamesFromBindings, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, planRemoteBindings, promptMultiSelect, promptSelect, promptYesNo, readLinkedProject, readProjectRemotePreference, readWranglerJsonc, reconcileWranglerBindings, requiredSecrets, resolveRemoteEnabled, secretsForPackages, streamContainerLogs, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer, writeLinkedProject };
1265
+ 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, CODEGEN_ENV, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_BINDINGS_FILE, 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, type HookLogger, type HookSpawnDescriptor, type HookSpawner, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_EVENT_SOURCE, LUNORA_IGNORED_PATHS, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LintIgnoreOutcome, type LintIgnoreStatus, type LintTool, type LunoraFormattedLine, type LunoraLineLevel, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type PackageManager, type PackageManagerProbe, type ParseSchemaResult, type PostCodegenHookResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, addArgsFor, applyAdditiveEdit, applyLintIgnores, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, detectInstalledManagers, detectLintTools, detectPackageManager, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, escapeRegExp, execArgsFor, fillDevSecrets, formatLunoraEvent, generateSecretValue, installArgsFor, interpretRemote, isCodegenDisabled, isDevServerReady, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, isRunnableTarget, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, projectUsesUmbrella, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, removeDevVariableLine, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, runPostCodegenHook, runScriptArgsFor, runScriptCommand, runnableTargetIds, secretsForPackages, streamContainerLogs, updateDevServerState, upsertDevVariableLine, writeDevServerState, writeDevVariablesFileAtomically, writeLinkedProject };