@lunora/config 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (23) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +240 -6
  3. package/dist/index.d.ts +240 -6
  4. package/dist/index.mjs +13 -10
  5. package/dist/packem_shared/ACCENT-DW1XJn8i.mjs +40 -0
  6. package/dist/packem_shared/LunoraReporter-Ci-bDCK9.mjs +70 -0
  7. package/dist/packem_shared/{PACKAGE_SECRETS_REGISTRY-CySy5vR_.mjs → PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs} +9 -1
  8. package/dist/packem_shared/{injectRemoteFlags-C-WZAKLY.mjs → REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs} +1 -1
  9. package/dist/packem_shared/{REQUIRED_COMPATIBILITY_DATE-Dd1suoit.mjs → REQUIRED_COMPATIBILITY_DATE-USQQ7NfE.mjs} +80 -2
  10. package/dist/packem_shared/{buildPackageSecretsBlock-S74dgmwy.mjs → buildPackageSecretsBlock-DWDKHViT.mjs} +60 -4
  11. package/dist/packem_shared/{discoverSchemaInfo-DWtypqpP.mjs → discoverSchemaInfo-BB-CKlTK.mjs} +1 -1
  12. package/dist/packem_shared/{inferLunoraBindings-0W3eRdIP.mjs → inferLunoraBindings-b9SJwb2s.mjs} +45 -18
  13. package/dist/packem_shared/{reconcileWranglerBindings-ByJk3yLU.mjs → reconcileWranglerBindings-27af-Qvt.mjs} +56 -4
  14. package/dist/packem_shared/streamContainerLogs-BZ4cOZwH.mjs +157 -0
  15. package/dist/studio-host/index.mjs +4 -4
  16. package/package.json +8 -6
  17. /package/dist/packem_shared/{interpretRemote-CtcIcB5-.mjs → LUNORA_CONFIG_FILE-CtcIcB5-.mjs} +0 -0
  18. /package/dist/packem_shared/{formatLunoraEvent-D2fDeGB6.mjs → LUNORA_EVENT_SOURCE-D2fDeGB6.mjs} +0 -0
  19. /package/dist/packem_shared/{handlePolicyScaffoldRequest-CiC2IGKx.mjs → POLICY_SCAFFOLD_ENDPOINT-CiC2IGKx.mjs} +0 -0
  20. /package/dist/packem_shared/{handleSchemaEditRequest-Df-Wrix-.mjs → SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs} +0 -0
  21. /package/dist/packem_shared/{handleSeedRequest-DVCjaGO-.mjs → SEED_ENDPOINT-DVCjaGO-.mjs} +0 -0
  22. /package/dist/packem_shared/{findWranglerFile-DwSuC-Kn.mjs → WRANGLER_FILES-DwSuC-Kn.mjs} +0 -0
  23. /package/dist/packem_shared/{studioAssetsStamp-Csk5RS4E.mjs → loadStudioAssets-Csk5RS4E.mjs} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { ContainerIR, WorkflowIR } from '@lunora/codegen';
1
+ import { ContainerIR, WorkflowIR, QueueIR } from '@lunora/codegen';
2
2
  export type { ContainerIR, WorkflowIR } from '@lunora/codegen';
3
+ import { Writable } from 'node:stream';
3
4
  import 'ts-morph';
4
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";
5
6
  /**
@@ -65,6 +66,77 @@ interface DiscoverContainerInfoResult {
65
66
  * (inference).
66
67
  */
67
68
  declare const discoverContainerInfo: (projectRoot: string, schemaDirectory: string) => DiscoverContainerInfoResult;
69
+ /** Severity a container output line is surfaced at: `stderr` → `error`, `stdout` → `info`. */
70
+ type ContainerLogLevel = "error" | "info";
71
+ /** One declared container to follow, identified by the names codegen lifts from `defineContainer`. */
72
+ interface ContainerLogSource {
73
+ /** Generated Durable Object class name, e.g. `TranscoderContainer`. Wrangler's dev image is `cloudflare-dev/<lowercased>`. */
74
+ className: string;
75
+ /** The `lunora/containers.ts` export name, e.g. `transcoder` — used as the display tag. */
76
+ exportName: string;
77
+ }
78
+ /** A single line of container output handed back to the caller. */
79
+ interface ContainerLogLine {
80
+ /** `"error"` for the container's stderr, `"info"` for its stdout. */
81
+ level: ContainerLogLevel;
82
+ /** The container's export name (`transcoder`), for tagging the line. */
83
+ name: string;
84
+ /** One output line, with the trailing newline (and any `\r`) stripped. */
85
+ text: string;
86
+ }
87
+ interface ContainerLogStreamOptions {
88
+ /** The declared containers to follow. An empty list yields an inert handle. */
89
+ containers: ReadonlyArray<ContainerLogSource>;
90
+ /** Injected Docker client — defaults to a real lazily-imported `dockerode` instance. Tests pass a stub. */
91
+ docker?: DockerLike;
92
+ /** Called once per container output line. */
93
+ onLine: (line: ContainerLogLine) => void;
94
+ /** Called once when the Docker engine can't be reached (re-armed after it recovers). Defaults to silent. */
95
+ onUnavailable?: (message: string) => void;
96
+ /** Poll interval override, in ms. */
97
+ pollIntervalMs?: number;
98
+ }
99
+ /** Handle controlling a running log stream. */
100
+ interface ContainerLogStreamHandle {
101
+ /** Stop polling and tear down every attached log stream. Idempotent. */
102
+ close: () => void;
103
+ }
104
+ /** The minimal structural slice of a `dockerode` log stream this module consumes. */
105
+ interface DockerLogStream {
106
+ destroy: () => void;
107
+ on: (event: "data" | "end" | "error", listener: (chunk?: Buffer) => void) => void;
108
+ }
109
+ /** The minimal structural slice of a `dockerode` instance this module consumes. */
110
+ interface DockerLike {
111
+ getContainer: (id: string) => {
112
+ logs: (options: {
113
+ follow: true;
114
+ stderr: true;
115
+ stdout: true;
116
+ tail: "all";
117
+ timestamps: false;
118
+ }) => Promise<DockerLogStream>;
119
+ };
120
+ listContainers: (options: {
121
+ filters: {
122
+ status: ["running"];
123
+ };
124
+ }) => Promise<{
125
+ Id: string;
126
+ Image: string;
127
+ }[]>;
128
+ modem: {
129
+ demuxStream: (stream: DockerLogStream, stdout: Writable, stderr: Writable) => void;
130
+ };
131
+ }
132
+ /**
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
+ */
139
+ declare const streamContainerLogs: (options: ContainerLogStreamOptions) => ContainerLogStreamHandle;
68
140
  /**
69
141
  * The meta-frameworks Lunora can compose with, plus `"none"` for a standalone
70
142
  * SPA / SSR-less project (the current default). Mirrors PLAN4 §2.4.
@@ -156,6 +228,13 @@ interface InferredContainer extends ContainerIR {
156
228
  interface InferredWorkflow extends WorkflowIR {
157
229
  exported: boolean;
158
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;
159
238
  interface InferredBindings {
160
239
  /** Containers declared in `lunora/containers.ts` (exported or not — see {@link InferredContainer.exported}). */
161
240
  containers: InferredContainer[];
@@ -163,11 +242,13 @@ interface InferredBindings {
163
242
  durableObjects: DurableObjectSpec[];
164
243
  /** Schema declares a `.global()` table → needs the `DB` D1 binding. */
165
244
  needsD1: boolean;
245
+ /** Queues declared in `lunora/queues.ts` → reconciled into `queues.producers[]` / `queues.consumers[]`. */
246
+ queues: InferredQueue[];
166
247
  /** Human-readable provenance for each inferred binding / hint, for logging. */
167
248
  signals: string[];
168
249
  /** `@lunora/ai` is imported or `env.AI` is used → needs the `ai` Workers AI binding. */
169
250
  usesAi: boolean;
170
- /** `@lunora/analytics` is imported → self-describing `analytics_engine_datasets` binding (auto-writeable). */
251
+ /** `@lunora/bindings/analytics` is imported → self-describing `analytics_engine_datasets` binding (auto-writeable). */
171
252
  usesAnalytics: boolean;
172
253
  /** `@lunora/auth` is imported (sessions may be D1- or `SessionDO`-backed). */
173
254
  usesAuth: boolean;
@@ -175,15 +256,15 @@ interface InferredBindings {
175
256
  usesBrowser: boolean;
176
257
  /** `@lunora/hyperdrive` is imported (binding needs an un-mintable remote `id`; hint-only). */
177
258
  usesHyperdrive: boolean;
178
- /** `@lunora/images` is imported → self-describing `images` binding (auto-writeable). */
259
+ /** `@lunora/bindings/images` is imported → self-describing `images` binding (auto-writeable). */
179
260
  usesImages: boolean;
180
- /** `@lunora/kv` is imported (namespace binding name + id are user-defined; hint-only). */
261
+ /** `@lunora/bindings/kv` is imported (namespace binding name + id are user-defined; hint-only). */
181
262
  usesKv: boolean;
182
263
  /** `@lunora/mail` is imported (Resend API key must be set in `.dev.vars`; no binding). */
183
264
  usesMail: boolean;
184
265
  /** `@lunora/payment` is imported (provider secrets must be set in `.dev.vars`; no binding). */
185
266
  usesPayment: boolean;
186
- /** `@lunora/pipelines` is imported (binding needs an un-mintable remote pipeline name; hint-only). */
267
+ /** `ctx.pipelines` is used (binding needs an un-mintable remote pipeline name; hint-only). */
187
268
  usesPipelines: boolean;
188
269
  /** `@lunora/scheduler` is imported. */
189
270
  usesScheduler: boolean;
@@ -288,6 +369,12 @@ declare const LUNORA_EVENT_SOURCE = "lunora";
288
369
  * — so the caller passes the original line through untouched. Pure and total.
289
370
  */
290
371
  declare const formatLunoraEvent: (line: string) => LunoraFormattedLine | undefined;
372
+ declare class LunoraReporter {
373
+ #private;
374
+ setStdout(stdout: NodeJS.WriteStream): void;
375
+ setStderr(stderr: NodeJS.WriteStream): void;
376
+ log(meta: unknown): void;
377
+ }
291
378
  /**
292
379
  * Per-package secret-requirements registry for `.dev.vars` scaffolding.
293
380
  *
@@ -332,6 +419,15 @@ interface SecretEntry {
332
419
  */
333
420
  placeholderValue: string;
334
421
  }
422
+ /**
423
+ * Secrets every Lunora project needs regardless of which capability packages are
424
+ * installed — scaffolded into `.dev.vars` always. `LUNORA_ADMIN_TOKEN` is the
425
+ * bearer the local Studio uses to call the worker's admin endpoints (the data
426
+ * browser, schema edits) in dev; the worker reads the SAME `.dev.vars` value via
427
+ * its admin gate, so both agree and the Studio authenticates without a prompt.
428
+ * Without it, every `/_lunora/admin/*` call is `ADMIN_FORBIDDEN` (403).
429
+ */
430
+
335
431
  /**
336
432
  * The canonical registry of per-package secret requirements.
337
433
  *
@@ -653,6 +749,8 @@ interface RemoteEnableInputs {
653
749
  * is still overridable per-run by `--remote` or `LUNORA_REMOTE=1`.
654
750
  */
655
751
  declare const resolveRemoteEnabled: (inputs: RemoteEnableInputs) => boolean;
752
+ /** Core (always-scaffolded) secrets followed by the package-specific ones for the detected capabilities. */
753
+ declare const requiredSecrets: (packageNames: ReadonlyArray<string>) => SecretEntry[];
656
754
  /**
657
755
  * Whether an (already-unquoted) value looks like a fill-me-in placeholder —
658
756
  * empty, angle-bracketed, or containing a known marker — rather than a real
@@ -661,6 +759,15 @@ declare const resolveRemoteEnabled: (inputs: RemoteEnableInputs) => boolean;
661
759
  */
662
760
  declare const isPlaceholderValue: (value: string) => boolean;
663
761
  /**
762
+ * True for a secret-looking key whose value Lunora can mint locally (a random
763
+ * 32-byte hex, like `openssl rand -hex 32`) — e.g. `AUTH_SECRET`,
764
+ * `LUNORA_ADMIN_TOKEN`, `STORAGE_SIGNING_SECRET`. False for provider-issued keys
765
+ * ({@link PROVIDER_SECRET_KEYS}) and any non-secret key.
766
+ */
767
+ declare const isMintableSecretKey: (key: string) => boolean;
768
+ /** Mint a fresh strong secret value — 64 hex chars (32 bytes), like `openssl rand -hex 32`. */
769
+ declare const generateSecretValue: (randomHex?: (bytes: number) => string) => string;
770
+ /**
664
771
  * The outcome of planning a scaffold — a discriminated union so the orchestrator
665
772
  * never has to re-derive whether `content` is present.
666
773
  *
@@ -762,6 +869,58 @@ declare const buildPackageSecretsBlock: (packageNames: ReadonlyArray<string>, ex
762
869
  * **Safety invariant:** only placeholder values are written — no real secrets.
763
870
  */
764
871
  declare const ensureDevVariablesExample: (cwd: string, packageNames: ReadonlyArray<string>) => string[];
872
+ interface DevSecretsFillPlan {
873
+ /** {@link CORE_SECRETS} keys appended because they were absent (each generated). */
874
+ addedKeys: string[];
875
+ /** The full new file content to write. */
876
+ content: string;
877
+ /** Existing empty/placeholder secret-keyed entries filled with fresh values. */
878
+ filledKeys: string[];
879
+ }
880
+ /**
881
+ * Plan the in-place generation of dev secrets for a `.dev.vars`. First, every
882
+ * line whose KEY looks like a secret (`*_SECRET`, `*_TOKEN`, `*_KEY`,
883
+ * `*_PASSWORD`) and whose value is empty or a placeholder gets a freshly
884
+ * generated value — so a `lunora add`-scaffolded `.dev.vars` (which writes each
885
+ * secret blank) becomes usable on `lunora dev` / `vite dev` without the user
886
+ * running `openssl` by hand. Second, any {@link CORE_SECRETS} key absent from
887
+ * the file is appended (generated) — notably `LUNORA_ADMIN_TOKEN`, which the
888
+ * local Studio needs to call the worker's admin gate in dev (without it the
889
+ * Studio shows its login gate).
890
+ *
891
+ * Pure (given `randomHex`): real (non-placeholder) values are never touched, and
892
+ * comments + non-secret entries are preserved verbatim.
893
+ */
894
+ declare const planDevSecretsFill: (input: {
895
+ existingContent: string;
896
+ randomHex?: (bytes: number) => string;
897
+ }) => DevSecretsFillPlan;
898
+ interface FillDevSecretsResult {
899
+ /** Core secret keys appended (generated) because they were missing. */
900
+ addedKeys: string[];
901
+ /** Existing empty/placeholder secrets filled with generated values. */
902
+ filledKeys: string[];
903
+ /** `created` = no `.dev.vars` existed; `filled` = topped up an existing one; `unchanged` = nothing to do. */
904
+ status: "created" | "filled" | "unchanged";
905
+ }
906
+ /**
907
+ * Generate any missing/empty dev secrets in the project's `.dev.vars`, in place.
908
+ *
909
+ * Complements {@link ensureDevVariables} (which scaffolds `.dev.vars` from
910
+ * `.dev.vars.example`). A `lunora add`-scaffolded project writes secrets blank
911
+ * straight into `.dev.vars` (no example) and never includes `LUNORA_ADMIN_TOKEN`
912
+ * — so the worker boots with empty secrets and the Studio shows its login gate.
913
+ * This fills those gaps at dev startup, so both `lunora dev` and the
914
+ * `@lunora/vite` dev server give a working project with zero manual `openssl`.
915
+ *
916
+ * Never overwrites a real (non-placeholder) value. The write is atomic + owner-
917
+ * only (temp + rename, `mode: 0o600`), matching the other `.dev.vars` writers.
918
+ */
919
+ declare const fillDevSecrets: (deps: {
920
+ cwd: string;
921
+ info?: (message: string) => void;
922
+ randomHex?: (bytes: number) => string;
923
+ }) => FillDevSecretsResult;
765
924
  /** Add a new table to `defineSchema({ ... })`. */
766
925
  interface AddTableEdit {
767
926
  readonly kind: "addTable";
@@ -891,6 +1050,56 @@ interface DiscoverSchemaInfoResult {
891
1050
  * parse failure is a warning (validator) or simply ignorable (inference).
892
1051
  */
893
1052
  declare const discoverSchemaInfo: (projectRoot: string, schemaDirectory: string) => DiscoverSchemaInfoResult;
1053
+ /**
1054
+ * A badge: the short colored label that prefixes a line. `bg`/`fg` are hex so the
1055
+ * same value drives both colorize's `bgHex().hex()` and the tui `&lt;Text>` props.
1056
+ */
1057
+ interface BadgeSpec {
1058
+ bg: `#${string}`;
1059
+ fg: `#${string}`;
1060
+ text: string;
1061
+ }
1062
+ /** Lunora purple — the accent shared with the CLI prompt frames. */
1063
+ declare const ACCENT: `#${string}`;
1064
+ /** Standard log-level badge names (the restyled base output). */
1065
+ type LevelBadgeName = "debug" | "error" | "info" | "success" | "warn";
1066
+ /** Step-phase badge names (the create-astro-style flow transcript). */
1067
+ type StepBadgeName = "add" | "deps" | "dir" | "git" | "lunora" | "next" | "tmpl";
1068
+ type BadgeName = LevelBadgeName | StepBadgeName;
1069
+ /** The ordered step-phase names, used to register custom pail log types. */
1070
+ declare const STEP_BADGE_NAMES: ReadonlyArray<StepBadgeName>;
1071
+ /**
1072
+ * Every badge, keyed by name. Levels get their conventional colors (red/amber/
1073
+ * green/blue/grey); step phases follow create-astro's green→purple→cyan rhythm.
1074
+ */
1075
+ declare const BADGES: Record<BadgeName, BadgeSpec>;
1076
+ /**
1077
+ * Luna, the mascot: the folklore rabbit-in-the-moon — a bunny tucked inside the
1078
+ * moon disc. Pure ASCII so it renders the same everywhere (including piped logs).
1079
+ * The CLI signs off the `init` flow with it, the way create-astro closes with
1080
+ * Houston.
1081
+ */
1082
+ declare const LUNA_NAME = "Luna";
1083
+ declare const LUNA_SIGNOFF = "Safe travels, voyager.";
1084
+ declare const LUNA_BUNNY: string;
1085
+ /**
1086
+ * {@link LUNA_BUNNY} with its leading newline stripped, ready to render inline
1087
+ * (beside the name + sign-off). Both render paths — the tui mascot frame and the
1088
+ * pail off-TTY fallback — use this so neither re-implements the strip.
1089
+ */
1090
+ declare const LUNA_ART: string;
1091
+ /** The colored part of a badge — the word with one space of padding each side. */
1092
+ declare const padBadge: (text: string) => string;
1093
+ /** Leading spaces that right-align a badge's box within the gutter. */
1094
+ declare const badgeLead: (text: string) => string;
1095
+ /** Total columns a rendered badge column occupies (lead + box), constant across badges. */
1096
+ declare const BADGE_COLUMN_WIDTH: number;
1097
+ /** Columns a rendered badge occupies — the gutter-aligned column width. */
1098
+ declare const badgeWidth: (_spec: BadgeSpec) => number;
1099
+ /** Paint a badge as an ANSI string (the non-tui path): right-aligning spaces + the colored box. */
1100
+ declare const paintBadge: (spec: BadgeSpec) => string;
1101
+ /** Dim continuation text (a step's chosen answer, shown under the question). */
1102
+ declare const paintAnswer: (text: string) => string;
894
1103
  /** Candidate wrangler config filenames, in the order every consumer probes them. */
895
1104
  declare const WRANGLER_FILES: readonly ["wrangler.jsonc", "wrangler.json"];
896
1105
  /** Locate the project's wrangler config, or `undefined` when none exists. */
@@ -946,6 +1155,22 @@ interface WranglerWorkflowEntry {
946
1155
  class_name?: string;
947
1156
  name?: string;
948
1157
  }
1158
+ /** A wrangler `queues.producers[]` entry — a `Queue` binding sending to `queue`. */
1159
+ interface WranglerQueueProducer {
1160
+ binding?: string;
1161
+ delivery_delay?: number;
1162
+ queue?: string;
1163
+ }
1164
+ /** A wrangler `queues.consumers[]` entry — push (worker) or `type: "http_pull"`. */
1165
+ interface WranglerQueueConsumer {
1166
+ dead_letter_queue?: string;
1167
+ max_batch_size?: number;
1168
+ max_batch_timeout?: number;
1169
+ max_retries?: number;
1170
+ queue?: string;
1171
+ retry_delay?: number;
1172
+ type?: string;
1173
+ }
949
1174
  interface WranglerConfig {
950
1175
  analytics_engine_datasets?: ReadonlyArray<{
951
1176
  binding?: string;
@@ -1010,9 +1235,18 @@ interface WranglerConfig {
1010
1235
  placement?: {
1011
1236
  mode?: string;
1012
1237
  };
1238
+ queues?: {
1239
+ consumers?: ReadonlyArray<WranglerQueueConsumer | null | undefined>;
1240
+ producers?: ReadonlyArray<WranglerQueueProducer | null | undefined>;
1241
+ };
1013
1242
  r2_buckets?: ReadonlyArray<{
1014
1243
  binding?: string;
1015
1244
  }>;
1245
+ secrets_store_secrets?: ReadonlyArray<{
1246
+ binding?: string;
1247
+ secret_name?: string;
1248
+ store_id?: string;
1249
+ } | null | undefined>;
1016
1250
  send_email?: ReadonlyArray<{
1017
1251
  allowed_destination_addresses?: ReadonlyArray<string>;
1018
1252
  destination_address?: string;
@@ -1072,4 +1306,4 @@ interface WranglerProjectValidationResult {
1072
1306
  * `{ problems, wranglerPath }` shape plus the structured `report`.
1073
1307
  */
1074
1308
  declare const validateWranglerProject: (options: WranglerProjectValidationOptions) => WranglerProjectValidationResult;
1075
- export { 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, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DestructiveEdit, type DetectedFramework, type DiscoverContainerInfoResult, type DiscoverSchemaInfoResult, type DiscoverWorkflowInfoResult, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type ExportGap, type FrameworkClass, type FrameworkDetection, type InferOptions, type InferredBindings, type InferredContainer, type InferredWorkflow, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, 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, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaInfo, type SchemaTable, type SecretEntry, type SelectOption, type TailConsumer, WRANGLER_FILES, type WranglerConfig, type WranglerContainerEntry, type WranglerProjectValidationOptions, type WranglerProjectValidationResult, type WranglerValidationReport, type WranglerWorkflowEntry, applyAdditiveEdit, buildPackageSecretsBlock, claimAgentRulesHint, classifyEdit, createConfirm, detectAgentRules, detectFramework, discoverContainerInfo, discoverSchemaInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, findWranglerFile, formatLunoraEvent, inferLunoraBindings, injectRemoteFlags, interpretRemote, isInteractive, isPlaceholderValue, isRemoteEnvEnabled, materializeRemoteWranglerConfig, packageNamesFromBindings, parseDevVariableEntries, parseSchema, planDevVariablesAugment, planDevVariablesScaffold, planRemoteBindings, promptMultiSelect, promptSelect, promptYesNo, readLinkedProject, readProjectRemotePreference, readWranglerJsonc, reconcileWranglerBindings, resolveRemoteEnabled, secretsForPackages, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer, writeLinkedProject };
1309
+ 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 };
package/dist/index.mjs CHANGED
@@ -1,20 +1,23 @@
1
1
  export { AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, LUNORA_SKILL_NAMES, ROOT_SKILL_NAME, claimAgentRulesHint, detectAgentRules } from './packem_shared/AGENT_RULES_DIR-lcgC08aE.mjs';
2
2
  export { discoverContainerInfo } from './packem_shared/discoverContainerInfo-BXFs6Wav.mjs';
3
+ export { streamContainerLogs } from './packem_shared/streamContainerLogs-BZ4cOZwH.mjs';
3
4
  export { detectFramework } from './packem_shared/detectFramework-Br-BcPBq.mjs';
4
5
  export { DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, parseDevVariableEntries } from './packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
5
- export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-0W3eRdIP.mjs';
6
+ export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-b9SJwb2s.mjs';
6
7
  export { LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, readLinkedProject, writeLinkedProject } from './packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs';
7
- export { LUNORA_EVENT_SOURCE, formatLunoraEvent } from './packem_shared/formatLunoraEvent-D2fDeGB6.mjs';
8
- export { PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './packem_shared/PACKAGE_SECRETS_REGISTRY-CySy5vR_.mjs';
9
- export { LUNORA_CONFIG_FILE, interpretRemote, readProjectRemotePreference } from './packem_shared/interpretRemote-CtcIcB5-.mjs';
8
+ export { LUNORA_EVENT_SOURCE, formatLunoraEvent } from './packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs';
9
+ export { default as LunoraReporter } from './packem_shared/LunoraReporter-Ci-bDCK9.mjs';
10
+ export { PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './packem_shared/PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs';
11
+ export { LUNORA_CONFIG_FILE, interpretRemote, readProjectRemotePreference } from './packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs';
10
12
  export { createConfirm, isInteractive, promptMultiSelect, promptSelect, promptYesNo } from './packem_shared/createConfirm-fvpdgJ9s.mjs';
11
- export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-ByJk3yLU.mjs';
12
- export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/injectRemoteFlags-C-WZAKLY.mjs';
13
- export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, isPlaceholderValue, planDevVariablesAugment, planDevVariablesScaffold } from './packem_shared/buildPackageSecretsBlock-S74dgmwy.mjs';
13
+ export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-27af-Qvt.mjs';
14
+ export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs';
15
+ export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-DWDKHViT.mjs';
14
16
  export { applyAdditiveEdit, classifyEdit } from './packem_shared/applyAdditiveEdit-C-snTFEV.mjs';
15
17
  export { parseSchema } from './packem_shared/parseSchema-DSeyktvG.mjs';
16
18
  export { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './packem_shared/classifyPolicyEdit-BHeAqF8P.mjs';
17
- export { discoverSchemaInfo } from './packem_shared/discoverSchemaInfo-DWtypqpP.mjs';
19
+ export { discoverSchemaInfo } from './packem_shared/discoverSchemaInfo-BB-CKlTK.mjs';
20
+ export { ACCENT, BADGES, BADGE_COLUMN_WIDTH, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, STEP_BADGE_NAMES, badgeLead, badgeWidth, padBadge, paintAnswer, paintBadge } from './packem_shared/ACCENT-DW1XJn8i.mjs';
18
21
  export { discoverWorkflowInfo } from './packem_shared/discoverWorkflowInfo-CedvR0mn.mjs';
19
- export { WRANGLER_FILES, findWranglerFile, readWranglerJsonc } from './packem_shared/findWranglerFile-DwSuC-Kn.mjs';
20
- export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-Dd1suoit.mjs';
22
+ export { WRANGLER_FILES, findWranglerFile, readWranglerJsonc } from './packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs';
23
+ export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-USQQ7NfE.mjs';
@@ -0,0 +1,40 @@
1
+ import colorize from '@visulima/colorize';
2
+
3
+ const ACCENT = "#a855f7";
4
+ const INK = "#0b0b0b";
5
+ const STEP_BADGE_NAMES = ["lunora", "dir", "tmpl", "add", "deps", "git", "next"];
6
+ const BADGES = {
7
+ add: { bg: ACCENT, fg: INK, text: "add" },
8
+ debug: { bg: "#6b7280", fg: "#f3f4f6", text: "debug" },
9
+ deps: { bg: ACCENT, fg: INK, text: "deps" },
10
+ dir: { bg: ACCENT, fg: INK, text: "dir" },
11
+ error: { bg: "#ef4444", fg: INK, text: "error" },
12
+ git: { bg: "#f59e0b", fg: INK, text: "git" },
13
+ info: { bg: "#3b82f6", fg: INK, text: "info" },
14
+ lunora: { bg: "#22c55e", fg: INK, text: "lunora" },
15
+ next: { bg: "#06b6d4", fg: INK, text: "next" },
16
+ success: { bg: "#22c55e", fg: INK, text: "ok" },
17
+ tmpl: { bg: ACCENT, fg: INK, text: "tmpl" },
18
+ warn: { bg: "#f59e0b", fg: INK, text: "warn" }
19
+ };
20
+ const LUNA_NAME = "Luna";
21
+ const LUNA_SIGNOFF = "Safe travels, voyager.";
22
+ const LUNA_BUNNY = String.raw`
23
+ .-"""""-.
24
+ / (\(\ \
25
+ | ( -.-) |
26
+ \ o(")(") /
27
+ '-._____.-'`;
28
+ const LUNA_ART = LUNA_BUNNY.startsWith("\n") ? LUNA_BUNNY.slice(1) : LUNA_BUNNY;
29
+ const BADGE_GUTTER = 6;
30
+ const padBadge = (text) => ` ${text} `;
31
+ const badgeLead = (text) => " ".repeat(Math.max(0, BADGE_GUTTER - text.length));
32
+ const BADGE_COLUMN_WIDTH = BADGE_GUTTER + 2;
33
+ const badgeWidth = (_spec) => BADGE_COLUMN_WIDTH;
34
+ const paintBadge = (spec) => (
35
+ // eslint-disable-next-line import/no-named-as-default-member -- see file header.
36
+ badgeLead(spec.text) + colorize.bgHex(spec.bg).hex(spec.fg).bold(padBadge(spec.text))
37
+ );
38
+ const paintAnswer = (text) => colorize.dim(text);
39
+
40
+ export { ACCENT, BADGES, BADGE_COLUMN_WIDTH, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, STEP_BADGE_NAMES, badgeLead, badgeWidth, padBadge, paintAnswer, paintBadge };
@@ -0,0 +1,70 @@
1
+ import { paintBadge, badgeWidth, BADGES } from './ACCENT-DW1XJn8i.mjs';
2
+
3
+ const NAME_ALIASES = {
4
+ informational: "info",
5
+ warning: "warn"
6
+ };
7
+ const STDERR_LEVELS = /* @__PURE__ */ new Set(["alert", "critical", "emergency", "error", "warn", "warning"]);
8
+ const resolveBadge = (name) => {
9
+ if (name in BADGES) {
10
+ return BADGES[name];
11
+ }
12
+ const alias = NAME_ALIASES[name];
13
+ return alias === void 0 ? void 0 : BADGES[alias];
14
+ };
15
+ const renderContextArgument = (value) => {
16
+ if (value instanceof Error) {
17
+ return `
18
+ ${value.stack ?? value.message}`;
19
+ }
20
+ if (typeof value === "object" && value !== null) {
21
+ try {
22
+ return ` ${JSON.stringify(value)}`;
23
+ } catch {
24
+ return " [unserializable]";
25
+ }
26
+ }
27
+ return ` ${String(value)}`;
28
+ };
29
+ const composeMessage = (meta) => {
30
+ const base = typeof meta.message === "string" ? meta.message : String(meta.message);
31
+ const context = meta.context ?? [];
32
+ return context.length === 0 ? base : base + context.map((value) => renderContextArgument(value)).join("");
33
+ };
34
+ class LunoraReporter {
35
+ #stdout = process.stdout;
36
+ #stderr = process.stderr;
37
+ setStdout(stdout) {
38
+ this.#stdout = stdout;
39
+ }
40
+ setStderr(stderr) {
41
+ this.#stderr = stderr;
42
+ }
43
+ // Note: pail's other reporter setters (setLoggerTypes / setInteractiveManager /
44
+ // setIsInteractive) are optional and intentionally omitted — this reporter keeps
45
+ // no type or interactive state; it only needs the two stream setters above.
46
+ log(meta) {
47
+ const data = meta;
48
+ const badge = resolveBadge(data.type.name);
49
+ const stream = STDERR_LEVELS.has(data.type.level ?? data.type.name) ? this.#stderr : this.#stdout;
50
+ stream.write(LunoraReporter.#render(badge, composeMessage(data)));
51
+ }
52
+ /**
53
+ * Frame `text` under `badge`: `&lt;badge> &lt;first line>`, with any further lines
54
+ * indented to align beneath the message. Without a badge (unknown type) the
55
+ * message is written plain.
56
+ */
57
+ static #render(badge, text) {
58
+ if (badge === void 0) {
59
+ return `${text}
60
+ `;
61
+ }
62
+ const [first = "", ...rest] = text.split("\n");
63
+ const indent = " ".repeat(badgeWidth() + 1);
64
+ const lines = [`${paintBadge(badge)} ${first}`, ...rest.map((line) => `${indent}${line}`)];
65
+ return `${lines.join("\n")}
66
+ `;
67
+ }
68
+ }
69
+
70
+ export { LunoraReporter as default };
@@ -1,3 +1,11 @@
1
+ const CORE_SECRETS = [
2
+ {
3
+ description: "Bearer token the local Lunora Studio uses to call the worker's admin endpoints (data browser, schema edits) in dev. Generate with: openssl rand -hex 32",
4
+ docsUrl: "https://lunora.sh/docs/packages/studio",
5
+ key: "LUNORA_ADMIN_TOKEN",
6
+ placeholderValue: "replace-with-openssl-rand-hex-32"
7
+ }
8
+ ];
1
9
  const PACKAGE_SECRETS_REGISTRY = {
2
10
  "@lunora/auth": [
3
11
  {
@@ -59,4 +67,4 @@ const secretsForPackages = (packageNames) => {
59
67
  return result;
60
68
  };
61
69
 
62
- export { PACKAGE_SECRETS_REGISTRY, secretsForPackages };
70
+ export { CORE_SECRETS, PACKAGE_SECRETS_REGISTRY, secretsForPackages };
@@ -1,6 +1,6 @@
1
1
  import { writeFileSync, rmSync } from 'node:fs';
2
2
  import { modify, applyEdits } from 'jsonc-parser';
3
- import { findWranglerFile, readWranglerJsonc } from './findWranglerFile-DwSuC-Kn.mjs';
3
+ import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
4
4
  import { join } from 'node:path';
5
5
 
6
6
  const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
- import { discoverSchemaInfo } from './discoverSchemaInfo-DWtypqpP.mjs';
4
- import { findWranglerFile, readWranglerJsonc } from './findWranglerFile-DwSuC-Kn.mjs';
3
+ import { discoverSchemaInfo } from './discoverSchemaInfo-BB-CKlTK.mjs';
4
+ import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
5
5
 
6
6
  const REQUIRED_COMPATIBILITY_DATE = "2026-04-07";
7
7
  const REQUIRED_FLAG = "web_socket_auto_reply_to_close";
@@ -127,6 +127,82 @@ const validateWorkflows = (wrangler, errors) => {
127
127
  }
128
128
  }
129
129
  };
130
+ const validateQueueProducers = (producers, errors) => {
131
+ if (producers === void 0) {
132
+ return;
133
+ }
134
+ if (!Array.isArray(producers)) {
135
+ errors.push("queues.producers must be an array of { binding, queue } entries");
136
+ return;
137
+ }
138
+ const entries = producers;
139
+ for (const [index, entry] of entries.entries()) {
140
+ const label = `queues.producers[${String(index)}]`;
141
+ if (!entry || typeof entry !== "object") {
142
+ errors.push(`${label} must be a { binding, queue } object`);
143
+ continue;
144
+ }
145
+ if (typeof entry.binding !== "string" || entry.binding.length === 0) {
146
+ errors.push(`${label} must have a non-empty "binding" naming the Queue producer (e.g. QUEUE_EMAIL)`);
147
+ }
148
+ if (typeof entry.queue !== "string" || entry.queue.length === 0) {
149
+ errors.push(`${label} must have a non-empty "queue" naming the deployed queue`);
150
+ }
151
+ }
152
+ };
153
+ const validateQueueConsumers = (consumers, errors) => {
154
+ if (consumers === void 0) {
155
+ return;
156
+ }
157
+ if (!Array.isArray(consumers)) {
158
+ errors.push("queues.consumers must be an array of { queue } entries");
159
+ return;
160
+ }
161
+ const entries = consumers;
162
+ for (const [index, entry] of entries.entries()) {
163
+ const label = `queues.consumers[${String(index)}]`;
164
+ if (!entry || typeof entry !== "object") {
165
+ errors.push(`${label} must be a { queue } object`);
166
+ continue;
167
+ }
168
+ if (typeof entry.queue !== "string" || entry.queue.length === 0) {
169
+ errors.push(`${label} must have a non-empty "queue" naming the consumed queue`);
170
+ }
171
+ }
172
+ };
173
+ const validateQueues = (wrangler, errors) => {
174
+ if (wrangler.queues === void 0) {
175
+ return;
176
+ }
177
+ if (typeof wrangler.queues !== "object" || Array.isArray(wrangler.queues)) {
178
+ errors.push("queues must be a { producers, consumers } object");
179
+ return;
180
+ }
181
+ validateQueueProducers(wrangler.queues.producers, errors);
182
+ validateQueueConsumers(wrangler.queues.consumers, errors);
183
+ };
184
+ const validateSecretsStore = (wrangler, errors) => {
185
+ if (wrangler.secrets_store_secrets === void 0) {
186
+ return;
187
+ }
188
+ if (!Array.isArray(wrangler.secrets_store_secrets)) {
189
+ errors.push("secrets_store_secrets must be an array of { binding, store_id, secret_name } entries");
190
+ return;
191
+ }
192
+ const entries = wrangler.secrets_store_secrets;
193
+ for (const [index, entry] of entries.entries()) {
194
+ const label = `secrets_store_secrets[${String(index)}]`;
195
+ if (!entry || typeof entry !== "object") {
196
+ errors.push(`${label} must be a { binding, store_id, secret_name } object`);
197
+ continue;
198
+ }
199
+ for (const field of ["binding", "store_id", "secret_name"]) {
200
+ if (typeof entry[field] !== "string" || entry[field].length === 0) {
201
+ errors.push(`${label} must have a non-empty "${field}"`);
202
+ }
203
+ }
204
+ }
205
+ };
130
206
  const isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
131
207
  const asBindingEntries = (value) => value;
132
208
  const HINT_BINDING_RULES = [
@@ -398,6 +474,8 @@ const validateWranglerConfig = (wrangler, schema) => {
398
474
  validateTailConsumers(wrangler, errors);
399
475
  validateContainers(wrangler, errors, warnings);
400
476
  validateWorkflows(wrangler, errors);
477
+ validateQueues(wrangler, errors);
478
+ validateSecretsStore(wrangler, errors);
401
479
  for (const rule of HINT_BINDING_RULES) {
402
480
  validateHintBinding(wrangler, rule, errors, warnings);
403
481
  }