@lunora/config 1.0.0-alpha.60 → 1.0.0-alpha.62

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 (22) hide show
  1. package/dist/index.d.mts +4 -0
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.mjs +7 -7
  4. package/dist/packem_shared/{POLICY_SCAFFOLD_ENDPOINT-8bNMHGiu.mjs → POLICY_SCAFFOLD_ENDPOINT-CvTZpaZF.mjs} +5 -9
  5. package/dist/packem_shared/{REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs → REMOTE_ELIGIBLE_KEYS-C-F_Flgy.mjs} +1 -6
  6. package/dist/packem_shared/{REQUIRED_COMPATIBILITY_DATE-DYYxY_If.mjs → REQUIRED_COMPATIBILITY_DATE-CoKkA1uk.mjs} +4 -3
  7. package/dist/packem_shared/{SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs → SCHEMA_EDIT_ENDPOINT-Bt2SNkpV.mjs} +4 -8
  8. package/dist/packem_shared/{SEED_ENDPOINT-DVCjaGO-.mjs → SEED_ENDPOINT-7izz_Jao.mjs} +1 -1
  9. package/dist/packem_shared/{buildPackageSecretsBlock-BDXX0mKG.mjs → buildPackageSecretsBlock-B-dWb-Sa.mjs} +1 -1
  10. package/dist/packem_shared/{collectWranglerSecretVariables-Red5H-4V.mjs → collectWranglerSecretVariables-p4ezBYQ4.mjs} +1 -1
  11. package/dist/packem_shared/{inferLunoraBindings-ByMwG-gH.mjs → inferLunoraBindings-BadOK1UZ.mjs} +15 -1
  12. package/dist/packem_shared/jsonc-edit-SbcD7MAe.mjs +9 -0
  13. package/dist/packem_shared/parseDevVariable-B0FEvfHC.mjs +21 -0
  14. package/dist/packem_shared/{reconcileWranglerBindings-Cruwwx1M.mjs → reconcileWranglerBindings-BhQN61qp.mjs} +15 -7
  15. package/dist/packem_shared/{reconcileWranglerCompatibilityDate-CYY0rVnJ.mjs → reconcileWranglerCompatibilityDate-D5Bqjk4P.mjs} +1 -1
  16. package/dist/packem_shared/{serveJsonHandler-B4OLTGLS.mjs → serveJsonHandler-Cl2Wqkv8.mjs} +2 -2
  17. package/dist/packem_shared/write-atomic-B2REqapi.mjs +14 -0
  18. package/dist/studio-host/index.d.mts +12 -2
  19. package/dist/studio-host/index.d.ts +12 -2
  20. package/dist/studio-host/index.mjs +5 -5
  21. package/package.json +6 -6
  22. package/dist/packem_shared/parseDevVariable-CJiq2IwE.mjs +0 -30
package/dist/index.d.mts CHANGED
@@ -409,6 +409,10 @@ interface InferredBindings {
409
409
  usesScheduler: boolean;
410
410
  /** `@lunora/storage` is imported (R2 bucket binding name is user-defined). */
411
411
  usesStorage: boolean;
412
+ /** `@lunora/x402/charge` is imported — the charge rail settles USDC to a recipient address (a public `[vars]` entry, user-named; hint-only). */
413
+ usesX402Charge: boolean;
414
+ /** `@lunora/x402/pay` is imported — the agent-wallet pay rail signs from a Secrets Store binding paired with a spend policy (ActionCtx-only; hint-only). */
415
+ usesX402Pay: boolean;
412
416
  /** Workflows declared in `lunora/workflows.ts` (exported or not — see {@link InferredWorkflow.exported}). */
413
417
  workflows: InferredWorkflow[];
414
418
  }
package/dist/index.d.ts CHANGED
@@ -409,6 +409,10 @@ interface InferredBindings {
409
409
  usesScheduler: boolean;
410
410
  /** `@lunora/storage` is imported (R2 bucket binding name is user-defined). */
411
411
  usesStorage: boolean;
412
+ /** `@lunora/x402/charge` is imported — the charge rail settles USDC to a recipient address (a public `[vars]` entry, user-named; hint-only). */
413
+ usesX402Charge: boolean;
414
+ /** `@lunora/x402/pay` is imported — the agent-wallet pay rail signs from a Secrets Store binding paired with a spend policy (ActionCtx-only; hint-only). */
415
+ usesX402Pay: boolean;
412
416
  /** Workflows declared in `lunora/workflows.ts` (exported or not — see {@link InferredWorkflow.exported}). */
413
417
  workflows: InferredWorkflow[];
414
418
  }
package/dist/index.mjs CHANGED
@@ -5,17 +5,17 @@ export { streamContainerLogs } from './packem_shared/streamContainerLogs-BZ4cOZw
5
5
  export { detectFramework, readProjectDependencyNames } from './packem_shared/detectFramework-U08038Yp.mjs';
6
6
  export { DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, claimDevServerState, clearDevServerState, isProcessAlive, isRecordedProcessCurrent, readDevServerState, readLiveDevServerState, updateDevServerState, writeDevServerState } from './packem_shared/DEV_DAEMON_ENV-FIEtW7Fz.mjs';
7
7
  export { DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, parseDevVariableEntries } from './packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
8
- export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-ByMwG-gH.mjs';
8
+ export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-BadOK1UZ.mjs';
9
9
  export { LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, readLinkedProject, writeLinkedProject } from './packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs';
10
10
  export { LUNORA_EVENT_SOURCE, formatLunoraEvent } from './packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs';
11
11
  export { default as LunoraReporter } from './packem_shared/LunoraReporter-Ci-bDCK9.mjs';
12
12
  export { PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './packem_shared/PACKAGE_SECRETS_REGISTRY-BeRSvrl5.mjs';
13
13
  export { LUNORA_CONFIG_FILE, interpretRemote, readProjectRemotePreference } from './packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs';
14
14
  export { createConfirm, isInteractive, promptMultiSelect, promptSelect, promptYesNo } from './packem_shared/createConfirm-fvpdgJ9s.mjs';
15
- export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-Cruwwx1M.mjs';
16
- export { reconcileWranglerCompatibilityDate } from './packem_shared/reconcileWranglerCompatibilityDate-CYY0rVnJ.mjs';
17
- export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs';
18
- export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-BDXX0mKG.mjs';
15
+ export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-BhQN61qp.mjs';
16
+ export { reconcileWranglerCompatibilityDate } from './packem_shared/reconcileWranglerCompatibilityDate-D5Bqjk4P.mjs';
17
+ export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-C-F_Flgy.mjs';
18
+ export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-B-dWb-Sa.mjs';
19
19
  export { applyAdditiveEdit, classifyEdit } from './packem_shared/applyAdditiveEdit-C-snTFEV.mjs';
20
20
  export { parseSchema } from './packem_shared/parseSchema-DSeyktvG.mjs';
21
21
  export { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './packem_shared/classifyPolicyEdit-BHeAqF8P.mjs';
@@ -23,5 +23,5 @@ export { discoverSchemaInfo } from './packem_shared/discoverSchemaInfo-BB-CKlTK.
23
23
  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';
24
24
  export { discoverWorkflowInfo } from './packem_shared/discoverWorkflowInfo-CedvR0mn.mjs';
25
25
  export { WRANGLER_FILES, findWranglerFile, readWranglerJsonc } from './packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs';
26
- export { collectWranglerSecretVariables, scanWranglerVariablesForSecrets } from './packem_shared/collectWranglerSecretVariables-Red5H-4V.mjs';
27
- export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-DYYxY_If.mjs';
26
+ export { collectWranglerSecretVariables, scanWranglerVariablesForSecrets } from './packem_shared/collectWranglerSecretVariables-p4ezBYQ4.mjs';
27
+ export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-CoKkA1uk.mjs';
@@ -1,8 +1,9 @@
1
- import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join, isAbsolute, relative } from 'node:path';
3
3
  import { runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
4
4
  import { LunoraError } from '@lunora/errors';
5
5
  import { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './classifyPolicyEdit-BHeAqF8P.mjs';
6
+ import { w as writeFileAtomic } from './write-atomic-B2REqapi.mjs';
6
7
 
7
8
  const POLICY_SCAFFOLD_ENDPOINT = "/__lunora/policy-scaffold";
8
9
  const statusForFailure = (reason) => {
@@ -14,11 +15,6 @@ const statusForFailure = (reason) => {
14
15
  }
15
16
  return reason === "destructive" ? 409 : 422;
16
17
  };
17
- const writeAtomic = (path, text) => {
18
- const temporaryPath = `${path}.lunora-tmp`;
19
- writeFileSync(temporaryPath, text, "utf8");
20
- renameSync(temporaryPath, path);
21
- };
22
18
  const runCodegenForResponse = (request, okBody) => {
23
19
  let diagnostics = [];
24
20
  try {
@@ -53,7 +49,7 @@ const handleScaffoldPolicy = (request, edit) => {
53
49
  if (existsSync(targetPath)) {
54
50
  return { body: { error: "file-exists", fileName: result.fileName, ok: false }, status: 409 };
55
51
  }
56
- writeAtomic(targetPath, result.source);
52
+ writeFileAtomic(targetPath, result.source);
57
53
  return runCodegenForResponse(request, { fileName: result.fileName });
58
54
  };
59
55
  const resolveProcedureFile = (projectRoot, lunoraDirectory, filePath) => {
@@ -77,7 +73,7 @@ const handleWireRls = (request, edit) => {
77
73
  if (!wired.ok) {
78
74
  return { body: { error: wired.reason, ok: false }, status: statusForFailure(wired.reason) };
79
75
  }
80
- writeAtomic(procedurePath, wired.text);
76
+ writeFileAtomic(procedurePath, wired.text);
81
77
  return runCodegenForResponse(request, { exportName: edit.exportName });
82
78
  };
83
79
  const handlePolicyScaffoldRequest = (request) => {
@@ -85,7 +81,7 @@ const handlePolicyScaffoldRequest = (request) => {
85
81
  return { body: { error: "method-not-allowed", ok: false }, status: 405 };
86
82
  }
87
83
  const { body } = request;
88
- if (body === void 0 || typeof body !== "object" || typeof body.kind !== "string") {
84
+ if (body === void 0 || body === null || typeof body !== "object" || typeof body.kind !== "string") {
89
85
  return { body: { error: "invalid-edit", ok: false }, status: 400 };
90
86
  }
91
87
  const edit = body;
@@ -1,9 +1,8 @@
1
1
  import { writeFileSync, rmSync } from 'node:fs';
2
- import { modify, applyEdits } from 'jsonc-parser';
2
+ import { a as applyModify } from './jsonc-edit-SbcD7MAe.mjs';
3
3
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
4
4
  import { join } from 'node:path';
5
5
 
6
- const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
7
6
  const REMOTE_ELIGIBLE_KEYS = {
8
7
  ai: { label: "AI", shape: "object" },
9
8
  d1_databases: { label: "D1", shape: "array" },
@@ -38,10 +37,6 @@ const planSection = (section, parsed) => {
38
37
  return entry === null || entry === void 0 ? [] : [{ binding: entryName(entry, section), kind: label, path: [], section }];
39
38
  };
40
39
  const planRemoteBindings = (parsed) => REMOTE_ELIGIBLE_KEY_LIST.flatMap((section) => planSection(section, parsed));
41
- const applyModify = (text, path, value) => {
42
- const edits = modify(text, [...path], value, FORMATTING);
43
- return edits.length > 0 ? applyEdits(text, edits) : text;
44
- };
45
40
  const injectRemoteFlags = (text, plans) => {
46
41
  let next = text;
47
42
  for (const plan of plans) {
@@ -76,6 +76,7 @@ const validateContainerEntry = (entry, label, checks) => {
76
76
  warnings.push(`${label} ("${entry.class_name}") declares no max_instances — set a cap so a traffic spike can't fan out unbounded container spend`);
77
77
  }
78
78
  };
79
+ const objectBindingEntries = (value) => Array.isArray(value) ? value.filter((entry) => entry !== null && typeof entry === "object") : [];
79
80
  const validateContainers = (wrangler, errors, warnings) => {
80
81
  if (wrangler.containers === void 0) {
81
82
  return;
@@ -88,7 +89,7 @@ const validateContainers = (wrangler, errors, warnings) => {
88
89
  if (entries.length === 0) {
89
90
  return;
90
91
  }
91
- const boundClasses = new Set((wrangler.durable_objects?.bindings ?? []).map((binding) => binding.class_name));
92
+ const boundClasses = new Set(objectBindingEntries(wrangler.durable_objects?.bindings).map((binding) => binding.class_name));
92
93
  const migrations = wrangler.migrations ?? [];
93
94
  const sqliteClasses = new Set(migrations.flatMap((migration) => [...migration?.new_sqlite_classes ?? []]));
94
95
  const nonSqliteClasses = new Set(migrations.flatMap((migration) => [...migration?.new_classes ?? []]));
@@ -494,7 +495,7 @@ const validateWranglerConfig = (wrangler, schema) => {
494
495
  errors.push("wrangler config is not a valid object");
495
496
  return { errors, valid: false, warnings };
496
497
  }
497
- const durableObjectBindings = wrangler.durable_objects?.bindings ?? [];
498
+ const durableObjectBindings = objectBindingEntries(wrangler.durable_objects?.bindings);
498
499
  const shardBinding = durableObjectBindings.find((binding) => binding.name === "SHARD" && binding.class_name === "ShardDO");
499
500
  if (!shardBinding) {
500
501
  errors.push(
@@ -514,7 +515,7 @@ const validateWranglerConfig = (wrangler, schema) => {
514
515
  errors.push(`cache.enabled requires compatibility_date >= "${WORKERS_CACHE_MIN_DATE}" (got "${compatibilityDate || "<missing>"}")`);
515
516
  }
516
517
  if (schema?.hasGlobalTable) {
517
- const d1Bindings = wrangler.d1_databases ?? [];
518
+ const d1Bindings = objectBindingEntries(wrangler.d1_databases);
518
519
  const databaseBinding = d1Bindings.find((binding) => binding.binding === "DB");
519
520
  if (!databaseBinding) {
520
521
  errors.push(
@@ -1,7 +1,8 @@
1
- import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
3
3
  import { classifyEdit, applyAdditiveEdit } from './applyAdditiveEdit-C-snTFEV.mjs';
4
4
  import { parseSchema } from './parseSchema-DSeyktvG.mjs';
5
+ import { w as writeFileAtomic } from './write-atomic-B2REqapi.mjs';
5
6
  import { join } from 'node:path';
6
7
 
7
8
  const SCHEMA_EDIT_ENDPOINT = "/__lunora/schema-edit";
@@ -36,11 +37,6 @@ const readSchema = (schemaPath) => {
36
37
  }
37
38
  return { tables: parsed.tables };
38
39
  };
39
- const writeSchemaAtomic = (schemaPath, text) => {
40
- const temporaryPath = `${schemaPath}.lunora-tmp`;
41
- writeFileSync(temporaryPath, text, "utf8");
42
- renameSync(temporaryPath, schemaPath);
43
- };
44
40
  const needsMigrationResponse = (edit) => {
45
41
  return {
46
42
  body: {
@@ -54,7 +50,7 @@ const needsMigrationResponse = (edit) => {
54
50
  };
55
51
  const handlePost = (request, schemaPath) => {
56
52
  const edit = request.body;
57
- if (edit === void 0 || typeof edit !== "object" || typeof edit.kind !== "string") {
53
+ if (edit === void 0 || edit === null || typeof edit !== "object" || typeof edit.kind !== "string") {
58
54
  return { body: { error: "invalid-edit", ok: false }, status: 400 };
59
55
  }
60
56
  if (classifyEdit(edit) === "destructive") {
@@ -67,7 +63,7 @@ const handlePost = (request, schemaPath) => {
67
63
  if (!applied.ok) {
68
64
  return { body: { error: applied.reason, ok: false }, status: statusForFailure(applied.reason) };
69
65
  }
70
- writeSchemaAtomic(schemaPath, applied.text);
66
+ writeFileAtomic(schemaPath, applied.text);
71
67
  let diagnostics = [];
72
68
  try {
73
69
  runCodegen({ lunoraDirectory: request.schemaDirectory ?? "lunora", projectRoot: request.projectRoot });
@@ -26,7 +26,7 @@ const handleSeedRequest = (request) => {
26
26
  return { body: { error: "method-not-allowed", ok: false }, status: 405 };
27
27
  }
28
28
  const { body } = request;
29
- if (body === void 0 || typeof body !== "object") {
29
+ if (body === void 0 || body === null || typeof body !== "object") {
30
30
  return { body: { error: "invalid-request", ok: false }, status: 400 };
31
31
  }
32
32
  const { count, existingIds, seed, table } = body;
@@ -46,11 +46,11 @@ const isPlaceholderValue = (value) => {
46
46
  };
47
47
  const isPlaceholder = (rawValue) => isPlaceholderValue(unquoteDevVariable(rawValue.trim()));
48
48
  const defaultRandomHex = (bytes) => randomBytes(bytes).toString("hex");
49
- const generatedSecretFor = (key, rawValue, randomHex) => SECRET_KEY.test(key) && isPlaceholder(rawValue) ? randomHex(SECRET_BYTES) : void 0;
50
49
  const PROVIDER_SECRET_KEYS = new Set(
51
50
  [...CORE_SECRETS, ...Object.values(PACKAGE_SECRETS_REGISTRY).flat()].filter((entry) => SECRET_KEY.test(entry.key) && entry.placeholderValue.startsWith("<")).map((entry) => entry.key)
52
51
  );
53
52
  const isMintableSecretKey = (key) => SECRET_KEY.test(key) && !PROVIDER_SECRET_KEYS.has(key);
53
+ const generatedSecretFor = (key, rawValue, randomHex) => isMintableSecretKey(key) && isPlaceholder(rawValue) ? randomHex(SECRET_BYTES) : void 0;
54
54
  const generateSecretValue = (randomHex = defaultRandomHex) => randomHex(SECRET_BYTES);
55
55
  const planDevVariablesScaffold = (input) => {
56
56
  if (input.devVarsExists) {
@@ -1,6 +1,6 @@
1
1
  import { relative } from 'node:path';
2
2
  import { secretKindOf, redact } from '@lunora/codegen';
3
- import { isPlaceholderValue } from './buildPackageSecretsBlock-BDXX0mKG.mjs';
3
+ import { isPlaceholderValue } from './buildPackageSecretsBlock-B-dWb-Sa.mjs';
4
4
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
5
5
 
6
6
  const MIN_SECRET_NAMED_VALUE_LENGTH = 8;
@@ -69,7 +69,13 @@ const CAPABILITY_SOURCES = {
69
69
  // for the hint message.
70
70
  usesPipelines: { pattern: CTX_PIPELINES_PATTERN, source: "@lunora/bindings/pipelines" },
71
71
  usesScheduler: { pattern: /\bfrom\s+["']@lunora\/scheduler["']/, source: "@lunora/scheduler" },
72
- usesStorage: { pattern: /\bfrom\s+["']@lunora\/storage["']/, source: "@lunora/storage" }
72
+ usesStorage: { pattern: /\bfrom\s+["']@lunora\/storage["']/, source: "@lunora/storage" },
73
+ // x402 rails are opt-in add-on subpaths (not part of the `lunorash` umbrella),
74
+ // so they key off the exact `@lunora/x402/{charge,pay}` specifiers. Neither
75
+ // implies a `.dev.vars` secret: the charge recipient is a user-named `[vars]`
76
+ // entry and the pay wallet key is a Secrets Store binding — both hint-only.
77
+ usesX402Charge: { pattern: /\bfrom\s+["']@lunora\/x402\/charge["']/, source: "@lunora/x402/charge" },
78
+ usesX402Pay: { pattern: /\bfrom\s+["']@lunora\/x402\/pay["']/, source: "@lunora/x402/pay" }
73
79
  };
74
80
  const CAPABILITY_FLAGS = Object.keys(CAPABILITY_SOURCES);
75
81
  const PAYMENT_PROVIDER_SECRETS = "STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET (Stripe) or POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET (Polar)";
@@ -291,6 +297,14 @@ const describeCapabilitySignals = (capabilities, exported) => {
291
297
  [
292
298
  capabilities.usesPipelines,
293
299
  "hint: ctx.pipelines is used; run 'wrangler pipelines create <name>' and add a 'pipelines' binding ({ binding, pipeline }) — the pipeline resource can't be auto-provisioned"
300
+ ],
301
+ [
302
+ capabilities.usesX402Charge,
303
+ "hint: @lunora/x402/charge is imported; set the recipient wallet address as a [vars] entry (the var name is yours to choose) and pass it to the charge config — the x402 facilitator settles USDC to that address"
304
+ ],
305
+ [
306
+ capabilities.usesX402Pay,
307
+ "hint: @lunora/x402/pay is imported (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding for the agent wallet key (name it to match signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars, so the key can't be auto-provisioned"
294
308
  ]
295
309
  ];
296
310
  return rules.filter(([active]) => active).map(([, signal]) => signal);
@@ -0,0 +1,9 @@
1
+ import { modify, applyEdits } from 'jsonc-parser';
2
+
3
+ const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
4
+ const applyModify = (text, path, value) => {
5
+ const edits = modify(text, [...path], value, FORMATTING);
6
+ return edits.length > 0 ? applyEdits(text, edits) : text;
7
+ };
8
+
9
+ export { FORMATTING as F, applyModify as a };
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parseDevVariableEntries } from './DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
4
+
5
+ const parseDevVariable = (contents, key) => {
6
+ const entry = parseDevVariableEntries(contents).find((candidate) => candidate.key === key);
7
+ return entry === void 0 || entry.value === "" ? void 0 : entry.value;
8
+ };
9
+ const resolveAdminToken = (root) => {
10
+ const fromEnv = process.env["LUNORA_ADMIN_TOKEN"];
11
+ if (typeof fromEnv === "string" && fromEnv !== "") {
12
+ return fromEnv;
13
+ }
14
+ try {
15
+ return parseDevVariable(readFileSync(join(root, ".dev.vars"), "utf8"), "LUNORA_ADMIN_TOKEN");
16
+ } catch {
17
+ return void 0;
18
+ }
19
+ };
20
+
21
+ export { parseDevVariable, resolveAdminToken };
@@ -1,9 +1,8 @@
1
1
  import { writeFileSync } from 'node:fs';
2
2
  import { containerBuildTag } from '@lunora/container';
3
- import { modify, applyEdits } from 'jsonc-parser';
3
+ import { a as applyModify } from './jsonc-edit-SbcD7MAe.mjs';
4
4
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
5
5
 
6
- const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
7
6
  const D1_PLACEHOLDER_ID = "<replace-with-d1-create-id>";
8
7
  const collectExportGaps = (inferred) => {
9
8
  const gaps = [];
@@ -41,6 +40,19 @@ const collectHintBindingWarnings = (inferred, parsed) => {
41
40
  ];
42
41
  return rules.filter(([active]) => active).map(([, warning]) => warning);
43
42
  };
43
+ const collectX402Warnings = (inferred) => {
44
+ const rules = [
45
+ [
46
+ inferred.usesX402Charge,
47
+ "@lunora/x402/charge is used; set the recipient wallet address as a [vars] entry (the var name is your choice) and pass it to the charge config — the x402 facilitator settles USDC to that address."
48
+ ],
49
+ [
50
+ inferred.usesX402Pay,
51
+ "@lunora/x402/pay is used (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding holding the agent wallet key (binding name == signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars."
52
+ ]
53
+ ];
54
+ return rules.filter(([active]) => active).map(([, warning]) => warning);
55
+ };
44
56
  const collectWarnings = (inferred, parsed) => {
45
57
  const exported = new Set(inferred.durableObjects.map((object) => object.className));
46
58
  const warnings = [];
@@ -81,13 +93,9 @@ const collectWarnings = (inferred, parsed) => {
81
93
  "@lunora/payment is used; set the provider secrets in .dev.vars — STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET (Stripe) or POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET (Polar)."
82
94
  );
83
95
  }
84
- warnings.push(...collectHintBindingWarnings(inferred, parsed));
96
+ warnings.push(...collectX402Warnings(inferred), ...collectHintBindingWarnings(inferred, parsed));
85
97
  return warnings;
86
98
  };
87
- const applyModify = (text, path, value) => {
88
- const edits = modify(text, [...path], value, FORMATTING);
89
- return edits.length > 0 ? applyEdits(text, edits) : text;
90
- };
91
99
  const nextMigrationTag = (migrations) => {
92
100
  const used = new Set(migrations.map((migration) => migration.tag));
93
101
  let index = 1;
@@ -1,9 +1,9 @@
1
1
  import { writeFileSync } from 'node:fs';
2
2
  import { modify, applyEdits } from 'jsonc-parser';
3
+ import { F as FORMATTING } from './jsonc-edit-SbcD7MAe.mjs';
3
4
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
4
5
 
5
6
  const WORKERS_CACHE_MIN_DATE = "2026-05-01";
6
- const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
7
7
  const reconcileWranglerCompatibilityDate = (projectRoot) => {
8
8
  const wranglerPath = findWranglerFile(projectRoot);
9
9
  if (!wranglerPath) {
@@ -57,7 +57,7 @@ const csrfRejectionReason = (request) => {
57
57
  }
58
58
  return void 0;
59
59
  };
60
- const serveJsonHandler = (request, response, handle, projectRoot) => {
60
+ const serveJsonHandler = (request, response, handle, projectRoot, schemaDirectory) => {
61
61
  const run = async () => {
62
62
  try {
63
63
  const rejection = csrfRejectionReason(request);
@@ -73,7 +73,7 @@ const serveJsonHandler = (request, response, handle, projectRoot) => {
73
73
  respondJson(response, 400, { error: "invalid-json", ok: false });
74
74
  return;
75
75
  }
76
- const result = handle({ body: parsed, method: request.method ?? "POST", projectRoot });
76
+ const result = handle({ body: parsed, method: request.method ?? "POST", projectRoot, schemaDirectory });
77
77
  respondJson(response, result.status, result.body);
78
78
  } catch (error) {
79
79
  respondJson(response, 500, { error: error instanceof Error ? error.message : String(error), ok: false });
@@ -0,0 +1,14 @@
1
+ import { writeFileSync, renameSync, rmSync } from 'node:fs';
2
+
3
+ const writeFileAtomic = (path, text) => {
4
+ const temporaryPath = `${path}.lunora-tmp`;
5
+ try {
6
+ writeFileSync(temporaryPath, text, "utf8");
7
+ renameSync(temporaryPath, path);
8
+ } catch (error) {
9
+ rmSync(temporaryPath, { force: true });
10
+ throw error;
11
+ }
12
+ };
13
+
14
+ export { writeFileAtomic as w };
@@ -1,6 +1,12 @@
1
1
  import { D as DestructivePolicyEdit, S as ScaffoldPolicyEdit, W as WireRlsEdit } from "../packem_shared/policy-scaffold.d-DCmwn7zQ.mjs";
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
- /** Parse a single `KEY=value` (optionally quoted) out of a `.dev.vars` body. */
3
+ /**
4
+ * Parse a single `KEY=value` (optionally quoted) out of a `.dev.vars` body,
5
+ * returning `undefined` when the key is absent or its value is empty. A thin
6
+ * wrapper over {@link parseDevVariableEntries} — the single owner of the
7
+ * `.dev.vars` line grammar — so the key-validation, comment-skip, and quote-strip
8
+ * rules can't drift from every other reader/writer of the file.
9
+ */
4
10
  declare const parseDevVariable: (contents: string, key: string) => string | undefined;
5
11
  /**
6
12
  * Resolve the worker's admin token so the studio can auto-authenticate in
@@ -254,6 +260,10 @@ type LocalEndpointHandler = (request: LocalEndpointRequest) => LocalEndpointResp
254
260
  * schema); every other method has its body read + JSON-parsed first. The
255
261
  * handler's `{ status, body }` is serialised back as JSON. A malformed body is a
256
262
  * `400`; an unexpected throw (e.g. the body-size guard) is a `500`.
263
+ *
264
+ * `schemaDirectory` (when the host configures a custom `schemaDir`) is forwarded
265
+ * to the handler so the schema/seed/policy endpoints target the right directory
266
+ * instead of always defaulting to `"lunora"`.
257
267
  */
258
- declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string) => void;
268
+ declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string, schemaDirectory?: string) => void;
259
269
  export { type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp };
@@ -1,6 +1,12 @@
1
1
  import { D as DestructivePolicyEdit, S as ScaffoldPolicyEdit, W as WireRlsEdit } from "../packem_shared/policy-scaffold.d-DCmwn7zQ.js";
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
- /** Parse a single `KEY=value` (optionally quoted) out of a `.dev.vars` body. */
3
+ /**
4
+ * Parse a single `KEY=value` (optionally quoted) out of a `.dev.vars` body,
5
+ * returning `undefined` when the key is absent or its value is empty. A thin
6
+ * wrapper over {@link parseDevVariableEntries} — the single owner of the
7
+ * `.dev.vars` line grammar — so the key-validation, comment-skip, and quote-strip
8
+ * rules can't drift from every other reader/writer of the file.
9
+ */
4
10
  declare const parseDevVariable: (contents: string, key: string) => string | undefined;
5
11
  /**
6
12
  * Resolve the worker's admin token so the studio can auto-authenticate in
@@ -254,6 +260,10 @@ type LocalEndpointHandler = (request: LocalEndpointRequest) => LocalEndpointResp
254
260
  * schema); every other method has its body read + JSON-parsed first. The
255
261
  * handler's `{ status, body }` is serialised back as JSON. A malformed body is a
256
262
  * `400`; an unexpected throw (e.g. the body-size guard) is a `500`.
263
+ *
264
+ * `schemaDirectory` (when the host configures a custom `schemaDir`) is forwarded
265
+ * to the handler so the schema/seed/policy endpoints target the right directory
266
+ * instead of always defaulting to `"lunora"`.
257
267
  */
258
- declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string) => void;
268
+ declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string, schemaDirectory?: string) => void;
259
269
  export { type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp };
@@ -1,7 +1,7 @@
1
- export { parseDevVariable, resolveAdminToken } from '../packem_shared/parseDevVariable-CJiq2IwE.mjs';
1
+ export { parseDevVariable, resolveAdminToken } from '../packem_shared/parseDevVariable-B0FEvfHC.mjs';
2
2
  export { assetContentType, isStandaloneModulePath, loadStudioAssets, readStandaloneAsset, resolveStandaloneDirectory, studioAssetsStamp } from '../packem_shared/assetContentType-ByZjeV3D.mjs';
3
- export { POLICY_SCAFFOLD_ENDPOINT, handlePolicyScaffoldRequest } from '../packem_shared/POLICY_SCAFFOLD_ENDPOINT-8bNMHGiu.mjs';
3
+ export { POLICY_SCAFFOLD_ENDPOINT, handlePolicyScaffoldRequest } from '../packem_shared/POLICY_SCAFFOLD_ENDPOINT-CvTZpaZF.mjs';
4
4
  export { default as renderStudioHtml } from '../packem_shared/renderStudioHtml-449Ysn75.mjs';
5
- export { SCHEMA_EDIT_ENDPOINT, handleSchemaEditRequest } from '../packem_shared/SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs';
6
- export { SEED_ENDPOINT, handleSeedRequest } from '../packem_shared/SEED_ENDPOINT-DVCjaGO-.mjs';
7
- export { serveJsonHandler } from '../packem_shared/serveJsonHandler-B4OLTGLS.mjs';
5
+ export { SCHEMA_EDIT_ENDPOINT, handleSchemaEditRequest } from '../packem_shared/SCHEMA_EDIT_ENDPOINT-Bt2SNkpV.mjs';
6
+ export { SEED_ENDPOINT, handleSeedRequest } from '../packem_shared/SEED_ENDPOINT-7izz_Jao.mjs';
7
+ export { serveJsonHandler } from '../packem_shared/serveJsonHandler-Cl2Wqkv8.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/config",
3
- "version": "1.0.0-alpha.60",
3
+ "version": "1.0.0-alpha.62",
4
4
  "description": "Internal shared CLI + Vite config layer for Lunora: wrangler.jsonc validation, binding inference, and .dev.vars scaffolding",
5
5
  "keywords": [
6
6
  "bindings",
@@ -50,10 +50,10 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@lunora/codegen": "1.0.0-alpha.38",
54
- "@lunora/container": "1.0.0-alpha.8",
55
- "@lunora/errors": "1.0.0-alpha.3",
56
- "@lunora/seed": "1.0.0-alpha.20",
53
+ "@lunora/codegen": "1.0.0-alpha.40",
54
+ "@lunora/container": "1.0.0-alpha.9",
55
+ "@lunora/errors": "1.0.0-alpha.4",
56
+ "@lunora/seed": "1.0.0-alpha.22",
57
57
  "@visulima/colorize": "2.0.0",
58
58
  "@visulima/find-ai-runner": "1.0.0",
59
59
  "dockerode": "^5.0.1",
@@ -62,7 +62,7 @@
62
62
  "ts-morph": "^28.0.0"
63
63
  },
64
64
  "peerDependencies": {
65
- "@lunora/studio": "1.0.0-alpha.45"
65
+ "@lunora/studio": "1.0.0-alpha.47"
66
66
  },
67
67
  "peerDependenciesMeta": {
68
68
  "@lunora/studio": {
@@ -1,30 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- const LINE_BREAK = /\r?\n/u;
5
- const parseDevVariable = (contents, key) => {
6
- for (const raw of contents.split(LINE_BREAK)) {
7
- const line = raw.trim();
8
- const eq = line.indexOf("=");
9
- if (line === "" || line.startsWith("#") || eq === -1 || line.slice(0, eq).trim() !== key) {
10
- continue;
11
- }
12
- const value = line.slice(eq + 1).trim();
13
- const unquoted = value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'") ? value.slice(1, -1) : value;
14
- return unquoted === "" ? void 0 : unquoted;
15
- }
16
- return void 0;
17
- };
18
- const resolveAdminToken = (root) => {
19
- const fromEnv = process.env["LUNORA_ADMIN_TOKEN"];
20
- if (typeof fromEnv === "string" && fromEnv !== "") {
21
- return fromEnv;
22
- }
23
- try {
24
- return parseDevVariable(readFileSync(join(root, ".dev.vars"), "utf8"), "LUNORA_ADMIN_TOKEN");
25
- } catch {
26
- return void 0;
27
- }
28
- };
29
-
30
- export { parseDevVariable, resolveAdminToken };