@lunora/cli 1.0.0-alpha.68 → 1.0.0-alpha.69

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.
package/dist/index.d.mts CHANGED
@@ -483,8 +483,12 @@ interface DevCommandOptions {
483
483
  ensureExample?: typeof ensureDevVarsExample;
484
484
  /** Injection seam for tests — defaults to the real empty-secret/admin-token filler. */
485
485
  fillSecrets?: typeof fillDevSecrets;
486
+ /** Injection seam for tests — defaults to the real free-port probe ({@link findAvailablePort}). */
487
+ findFreePort?: (preferred: number) => Promise<number>;
486
488
  /** Dev flavor override (tests / callers that already detected it) — defaults to {@link detectDevFlavor}. */
487
489
  flavor?: DevFlavor;
490
+ /** Injection seam for tests — defaults to the real IPv6-loopback probe ({@link hasIpv6Loopback}). */
491
+ hasIpv6Loopback?: () => boolean;
488
492
  logger: Logger;
489
493
  /** Injection seam for tests — defaults to the real remote-config materializer. */
490
494
  materializeRemote?: typeof materializeRemoteWranglerConfig;
@@ -531,6 +535,12 @@ interface DevCommandPlan {
531
535
  * regardless.
532
536
  */
533
537
  frameworkHint?: string;
538
+ /**
539
+ * True when `wrangler dev` was given `--ip 127.0.0.1` because the host has no
540
+ * IPv6 loopback (`::1`) — surfaced so the dev loop can note the rebind.
541
+ * Always `false` for the vite flavor (the plugin owns its own bind).
542
+ */
543
+ ipv4LoopbackForced: boolean;
534
544
  /** The remote-binding decision: which D1/KV/R2 bindings hit the deployed worker. */
535
545
  remote: DevRemotePlan;
536
546
  studioEnabled: boolean;
package/dist/index.d.ts CHANGED
@@ -483,8 +483,12 @@ interface DevCommandOptions {
483
483
  ensureExample?: typeof ensureDevVarsExample;
484
484
  /** Injection seam for tests — defaults to the real empty-secret/admin-token filler. */
485
485
  fillSecrets?: typeof fillDevSecrets;
486
+ /** Injection seam for tests — defaults to the real free-port probe ({@link findAvailablePort}). */
487
+ findFreePort?: (preferred: number) => Promise<number>;
486
488
  /** Dev flavor override (tests / callers that already detected it) — defaults to {@link detectDevFlavor}. */
487
489
  flavor?: DevFlavor;
490
+ /** Injection seam for tests — defaults to the real IPv6-loopback probe ({@link hasIpv6Loopback}). */
491
+ hasIpv6Loopback?: () => boolean;
488
492
  logger: Logger;
489
493
  /** Injection seam for tests — defaults to the real remote-config materializer. */
490
494
  materializeRemote?: typeof materializeRemoteWranglerConfig;
@@ -531,6 +535,12 @@ interface DevCommandPlan {
531
535
  * regardless.
532
536
  */
533
537
  frameworkHint?: string;
538
+ /**
539
+ * True when `wrangler dev` was given `--ip 127.0.0.1` because the host has no
540
+ * IPv6 loopback (`::1`) — surfaced so the dev loop can note the rebind.
541
+ * Always `false` for the vite flavor (the plugin owns its own bind).
542
+ */
543
+ ipv4LoopbackForced: boolean;
534
544
  /** The remote-binding decision: which D1/KV/R2 bindings hit the deployed worker. */
535
545
  remote: DevRemotePlan;
536
546
  studioEnabled: boolean;
@@ -1,6 +1,6 @@
1
1
  import { readLinkedProject } from '@lunora/config';
2
2
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
3
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
3
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
4
4
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
5
5
 
6
6
  const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
@@ -2,7 +2,7 @@ import { mkdtempSync, rmSync, existsSync, readdirSync, statSync } from 'node:fs'
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join, relative } from 'node:path';
4
4
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
5
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
6
6
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
7
7
 
8
8
  const walk = (root) => {
@@ -4,7 +4,7 @@ import { runCodegen } from '@lunora/codegen';
4
4
  import { p as parseApiSpec } from '../packem_shared/api-spec-Bx0iKbxA.mjs';
5
5
  import { a as renderCodegenHint } from '../packem_shared/codegen-error-DJG-ghs_.mjs';
6
6
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
7
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
7
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
8
8
  import { v as validateOutputFormat, i as isJsonFormat, p as printJson, l as loggerForFormat } from '../packem_shared/output-format-B4642rjE.mjs';
9
9
  import { r as runSchemaDriftGate } from '../packem_shared/schema-drift-gate-BtBt0as0.mjs';
10
10
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
@@ -1,5 +1,5 @@
1
1
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
2
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
2
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
3
3
  import { i as isDockerAvailable } from '../packem_shared/docker-hMQ97KSQ.mjs';
4
4
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
2
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
2
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
3
3
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
4
4
 
5
5
  const withEnv = (args, env) => {
@@ -2,9 +2,9 @@ import { writeFileSync, existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, generateSecretValue, DEV_VARS_EXAMPLE_FILE, parseDevVariableEntries, isPlaceholderValue, packageNamesFromBindings, inferLunoraBindings, requiredSecrets, isMintableSecretKey } from '@lunora/config';
4
4
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
- import { d as detectPackageManager, e as execArgsFor } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
5
+ import { d as detectPackageManager, e as execArgsFor } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
6
6
  import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
7
- import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-P2_ZUR-k.mjs';
7
+ import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-Dq_Fkbm-.mjs';
8
8
 
9
9
  const NEWLINE_PRESENT = /[\r\n]/u;
10
10
  const UNREPRESENTABLE_PRESENT = /["\\]/u;
@@ -1,16 +1,17 @@
1
1
  import { spawnSync, spawn } from 'node:child_process';
2
- import { detectAgentRules, claimDevServerState, clearDevServerState, readDevServerState, readLiveDevServerState, readProjectDependencyNames, DEV_HANDOFF_ENV, isRecordedProcessCurrent, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_DAEMON_ENV, updateDevServerState, detectAiAgent, resolveRemoteEnabled, readProjectRemotePreference, detectFramework, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables, isInteractive, DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, fillDevSecrets, claimAgentRulesHint, AGENT_RULES_HINT, discoverContainerInfo, streamContainerLogs, materializeRemoteWranglerConfig, formatLunoraEvent } from '@lunora/config';
2
+ import { detectAgentRules, claimDevServerState, clearDevServerState, readDevServerState, readLiveDevServerState, readProjectDependencyNames, DEV_HANDOFF_ENV, isRecordedProcessCurrent, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_DAEMON_ENV, updateDevServerState, detectAiAgent, resolveRemoteEnabled, readProjectRemotePreference, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables, isInteractive, DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, fillDevSecrets, claimAgentRulesHint, AGENT_RULES_HINT, findWranglerFile, readWranglerJsonc, detectFramework, discoverContainerInfo, streamContainerLogs, materializeRemoteWranglerConfig, formatLunoraEvent } from '@lunora/config';
3
3
  import { p as parseApiSpec } from '../packem_shared/api-spec-Bx0iKbxA.mjs';
4
4
  import { existsSync, watch, readFileSync, statSync, openSync, readSync, closeSync, mkdirSync } from 'node:fs';
5
5
  import { join, dirname } from 'node:path';
6
6
  import { runCodegen } from '@lunora/codegen';
7
7
  import { r as renderCodegenFailure } from '../packem_shared/codegen-error-DJG-ghs_.mjs';
8
8
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
9
- import { d as detectPackageManager, e as execArgsFor, r as runScriptCommand } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
9
+ import { d as detectPackageManager, e as execArgsFor, r as runScriptCommand } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
10
+ import { createServer, connect } from 'node:net';
10
11
  import { forceJsonLogging } from '../packem_shared/createLogger-CIWSHrTL.mjs';
12
+ import { networkInterfaces } from 'node:os';
11
13
  import { spawnShellCompat } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
12
- import { createServer, request } from 'node:http';
13
- import { connect } from 'node:net';
14
+ import { createServer as createServer$1, request } from 'node:http';
14
15
  import { loadStudioAssets, studioAssetsStamp, renderStudioHtml, resolveAdminToken, SCHEMA_EDIT_ENDPOINT, POLICY_SCAFFOLD_ENDPOINT, SEED_ENDPOINT, serveJsonHandler, isStandaloneModulePath, readStandaloneAsset, assetContentType, handleSchemaEditRequest, handlePolicyScaffoldRequest, handleSeedRequest } from '@lunora/config/studio-host';
15
16
  import { c as createTuiConfirm } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
16
17
  import { p as printJson } from '../packem_shared/output-format-B4642rjE.mjs';
@@ -75,6 +76,32 @@ const startCodegenWatch = (options) => {
75
76
  };
76
77
  };
77
78
 
79
+ const DEFAULT_PROBE_ATTEMPTS = 64;
80
+ const LOOPBACK_HOST = "127.0.0.1";
81
+ const MAX_PORT = 65535;
82
+ const isPortFree = (port, host = LOOPBACK_HOST) => new Promise((resolve) => {
83
+ const server = createServer();
84
+ server.once("error", () => {
85
+ resolve(false);
86
+ });
87
+ server.once("listening", () => {
88
+ server.close(() => {
89
+ resolve(true);
90
+ });
91
+ });
92
+ server.listen(port, host);
93
+ });
94
+ const findAvailablePort = async (preferred, host = LOOPBACK_HOST, attempts = DEFAULT_PROBE_ATTEMPTS) => {
95
+ for (let port = preferred; port < preferred + attempts && port <= MAX_PORT; port += 1) {
96
+ if (await isPortFree(port, host)) {
97
+ return port;
98
+ }
99
+ }
100
+ return preferred;
101
+ };
102
+
103
+ const hasIpv6Loopback = (readInterfaces = networkInterfaces) => Object.values(readInterfaces()).some((addresses) => addresses?.some((address) => address.internal && address.address === "::1"));
104
+
78
105
  const PROXY_PREFIX = "/_lunora";
79
106
  const pathnameOf = (url) => {
80
107
  const queryIndex = url.indexOf("?");
@@ -201,7 +228,7 @@ const startStudioServer = async (options) => {
201
228
  return true;
202
229
  };
203
230
  const document = Buffer.from(html);
204
- const server = createServer((request, response) => {
231
+ const server = createServer$1((request, response) => {
205
232
  const pathname = pathnameOf(request.url ?? "/");
206
233
  if (isLoopback && !isLoopbackHost(request.headers.host)) {
207
234
  response.statusCode = 403;
@@ -692,6 +719,29 @@ const resolveRemotePlan = (options, cwd) => {
692
719
  }
693
720
  return { args: ["--config", result.configPath], plan: { bindings, cleanup, enabled: true } };
694
721
  };
722
+ const resolveLoopbackArgs = (cwd, hasLoopback) => {
723
+ const wranglerPath = findWranglerFile(cwd);
724
+ if (wranglerPath !== void 0) {
725
+ const { parsed } = readWranglerJsonc(wranglerPath);
726
+ if (parsed?.dev?.ip !== void 0) {
727
+ return [];
728
+ }
729
+ }
730
+ return hasLoopback() ? [] : ["--ip", "127.0.0.1"];
731
+ };
732
+ const resolveWorkerPort = async (options, cwd) => {
733
+ if (options.workerPort !== void 0) {
734
+ return options.workerPort;
735
+ }
736
+ const wranglerPath = findWranglerFile(cwd);
737
+ if (wranglerPath !== void 0) {
738
+ const { parsed } = readWranglerJsonc(wranglerPath);
739
+ if (typeof parsed?.dev?.port === "number") {
740
+ return parsed.dev.port;
741
+ }
742
+ }
743
+ return (options.findFreePort ?? findAvailablePort)(DEFAULT_WORKER_PORT);
744
+ };
695
745
  const planDevCommand = (options) => {
696
746
  const cwd = options.cwd ?? process.cwd();
697
747
  const manager = detectPackageManager(cwd);
@@ -701,6 +751,7 @@ const planDevCommand = (options) => {
701
751
  return {
702
752
  codegenEnabled: false,
703
753
  flavor,
754
+ ipv4LoopbackForced: false,
704
755
  remote: { bindings: [], cleanup: () => {
705
756
  }, enabled: options.remote === true },
706
757
  studioEnabled: false,
@@ -720,11 +771,13 @@ const planDevCommand = (options) => {
720
771
  const frameworkHint = detection.framework === "none" ? void 0 : `this project uses ${detection.framework} — the worker runs inside Vite there. run \`${runScriptCommand(manager, "dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`;
721
772
  const workerPort = options.workerPort ?? DEFAULT_WORKER_PORT;
722
773
  const remote = resolveRemotePlan(options, cwd);
723
- const exec = execArgsFor(manager, "wrangler", ["dev", "--port", String(workerPort), "--var", "WORKER_ENV:development", ...remote.args]);
774
+ const loopbackArgs = resolveLoopbackArgs(cwd, options.hasIpv6Loopback ?? hasIpv6Loopback);
775
+ const exec = execArgsFor(manager, "wrangler", ["dev", "--port", String(workerPort), ...loopbackArgs, "--var", "WORKER_ENV:development", ...remote.args]);
724
776
  return {
725
777
  codegenEnabled: options.codegen !== false,
726
778
  flavor,
727
779
  frameworkHint,
780
+ ipv4LoopbackForced: loopbackArgs.length > 0,
728
781
  remote: remote.plan,
729
782
  studioEnabled: options.studio !== false,
730
783
  studioPort: options.port ?? DEFAULT_STUDIO_PORT,
@@ -922,8 +975,14 @@ const claimStartRecord = (plan, cwd) => {
922
975
  );
923
976
  return claim.ok ? void 0 : claim.existing;
924
977
  };
978
+ const buildDevPlan = async (options) => {
979
+ const cwd = options.cwd ?? process.cwd();
980
+ const flavor = options.flavor ?? detectDevFlavor(cwd);
981
+ const workerPort = flavor === "wrangler" ? await resolveWorkerPort(options, cwd) : options.workerPort;
982
+ return planDevCommand({ ...options, cwd, flavor, workerPort });
983
+ };
925
984
  const runDevCommand = async (options) => {
926
- const plan = planDevCommand(options);
985
+ const plan = await buildDevPlan(options);
927
986
  const { logger } = options;
928
987
  const cwd = plan.wrangler.cwd ?? process.cwd();
929
988
  const handles = { remoteCleanup: plan.remote.cleanup };
@@ -945,6 +1004,11 @@ const runDevCommand = async (options) => {
945
1004
  logger.info(
946
1005
  plan.flavor === "vite" ? "starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)" : "starting wrangler dev + studio"
947
1006
  );
1007
+ if (plan.ipv4LoopbackForced) {
1008
+ logger.info(
1009
+ "no IPv6 loopback (::1) on this host — binding the worker to 127.0.0.1 (--ip) so wrangler dev doesn't crash. Pin `dev.ip` in wrangler.jsonc to override."
1010
+ );
1011
+ }
948
1012
  if (plan.codegenEnabled) {
949
1013
  handles.codegen = (options.startCodegen ?? startCodegenWatch)({ apiSpec: options.apiSpec, logger, projectRoot: cwd });
950
1014
  }
@@ -1049,4 +1113,4 @@ const execute = defineHandler(async ({ argument, cwd, logger, options }) => {
1049
1113
  });
1050
1114
  });
1051
1115
 
1052
- export { detectDevFlavor, execute, planDevCommand, resolveRemotePlan, runDevCommand };
1116
+ export { detectDevFlavor, execute, planDevCommand, resolveRemotePlan, resolveWorkerPort, runDevCommand };
@@ -7,7 +7,7 @@ import { Project } from 'ts-morph';
7
7
  import { p as parseApiSpec } from '../packem_shared/api-spec-Bx0iKbxA.mjs';
8
8
  import { r as readWranglerName } from '../packem_shared/wrangler-name-cy4yhm9j.mjs';
9
9
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
10
- import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
10
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
11
11
  import { a as isRailpackAvailable, i as isDockerAvailable } from '../packem_shared/docker-hMQ97KSQ.mjs';
12
12
  import { v as validateOutputFormat, i as isJsonFormat, p as printJson, l as loggerForFormat } from '../packem_shared/output-format-B4642rjE.mjs';
13
13
  import { containerBuildTag } from '@lunora/container';
@@ -15,7 +15,7 @@ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G
15
15
  import { r as resolveWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
16
16
  import { r as runSchemaDriftGate } from '../packem_shared/schema-drift-gate-BtBt0as0.mjs';
17
17
  import { c as createTuiConfirm } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
18
- import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-P2_ZUR-k.mjs';
18
+ import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-Dq_Fkbm-.mjs';
19
19
  import { runMigrateDataCommand } from './runMigrateGenerateCommand.mjs';
20
20
 
21
21
  const WORKERS_DEV_URL = /https?:\/\/[^\s"'<>]+\.workers\.dev[^\s"'<>]*/u;
@@ -7,7 +7,7 @@ import { downloadTemplate } from 'giget';
7
7
  import { modify, applyEdits } from 'jsonc-parser';
8
8
  import { join, dirname } from 'node:path';
9
9
  import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
10
- import { d as detectPackageManager, a as detectInstalledManagers, i as installArgsFor, r as runScriptCommand } from '../packem_shared/detect-package-manager-Cxo6Tpyx.mjs';
10
+ import { d as detectPackageManager, a as detectInstalledManagers, i as installArgsFor, r as runScriptCommand } from '../packem_shared/detect-package-manager-DvEthdCw.mjs';
11
11
  import MagicString from 'magic-string';
12
12
  import { Project, SyntaxKind } from 'ts-morph';
13
13
  import { P as PromptCancelledError } from '../packem_shared/prompt-cancelled-APzX1Im-.mjs';
@@ -1940,8 +1940,7 @@ const maybeOfferGit = async (options, target) => {
1940
1940
  options.logger.warn("`git init` failed — initialize it yourself later with `git init`.");
1941
1941
  }
1942
1942
  };
1943
- const printNextSteps = async (name, installed, insideMonorepo) => {
1944
- const manager = installed ?? "pnpm";
1943
+ const printNextSteps = async (name, installed, manager, insideMonorepo) => {
1945
1944
  const steps = [{ code: `cd ./${name}`, lead: "Enter your project directory using" }];
1946
1945
  if (installed === void 0) {
1947
1946
  steps.push({ code: `${manager} install`, lead: "Install dependencies with", tail: insideMonorepo ? " from the workspace root" : void 0 });
@@ -2462,7 +2461,8 @@ const runPostScaffold = async (options, result, cwd) => {
2462
2461
  const installedManager = options.inPlace === true ? void 0 : await maybeOfferInstall(options, result.target);
2463
2462
  if (options.inPlace !== true) {
2464
2463
  await maybeOfferGit(options, result.target);
2465
- await printNextSteps(basename(result.target), installedManager, isInsideMonorepo(cwd));
2464
+ const manager = installedManager ?? detectPackageManager(result.target);
2465
+ await printNextSteps(basename(result.target), installedManager, manager, isInsideMonorepo(cwd));
2466
2466
  await emitMascot(options.logger);
2467
2467
  }
2468
2468
  };
@@ -1,9 +1,7 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { dirname, join } from '@visulima/path';
2
+ import { LunoraError } from '@lunora/errors';
3
+ import { findPackageManagerSync, identifyInitiatingPackageManager } from '@visulima/package/package-manager';
4
4
 
5
- const FALLBACK = "pnpm";
6
- const KNOWN_MANAGERS = ["pnpm", "yarn", "npm", "bun"];
7
5
  const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
8
6
  const isManagerInstalled = (manager) => {
9
7
  try {
@@ -16,34 +14,23 @@ const detectInstalledManagers = (probe = isManagerInstalled) => INSTALL_PREFEREN
16
14
  const installArgsFor = (manager) => {
17
15
  return { args: ["install"], command: manager };
18
16
  };
19
- const parseDeclaredManager = (declared) => {
20
- if (typeof declared !== "string") {
21
- return void 0;
22
- }
23
- return KNOWN_MANAGERS.find((manager) => declared.startsWith(`${manager}@`));
24
- };
25
- const readDeclaredManager = (directory) => {
26
- const candidate = join(directory, "package.json");
27
- if (!existsSync(candidate)) {
28
- return void 0;
29
- }
17
+ const detectPackageManager = (startDirectory) => {
30
18
  try {
31
- const parsed = JSON.parse(readFileSync(candidate, "utf8"));
32
- return parseDeclaredManager(parsed.packageManager);
19
+ return findPackageManagerSync(startDirectory).packageManager;
33
20
  } catch {
34
- return void 0;
35
21
  }
36
- };
37
- const detectPackageManager = (startDirectory) => {
38
- let directory = startDirectory;
39
- while (directory && directory !== dirname(directory)) {
40
- const declared = readDeclaredManager(directory);
41
- if (declared !== void 0) {
42
- return declared;
43
- }
44
- directory = dirname(directory);
22
+ const initiating = identifyInitiatingPackageManager();
23
+ if (initiating !== void 0) {
24
+ return initiating.name === "cnpm" ? "npm" : initiating.name;
25
+ }
26
+ const [installed] = detectInstalledManagers();
27
+ if (installed !== void 0) {
28
+ return installed;
45
29
  }
46
- return FALLBACK;
30
+ throw new LunoraError(
31
+ "INTERNAL",
32
+ "Could not detect a package manager: no lock file or `packageManager` field was found, and none (pnpm, bun, yarn, npm) is installed on PATH."
33
+ );
47
34
  };
48
35
  const execArgsFor = (manager, command, args) => {
49
36
  if (manager === "yarn") {
@@ -1,4 +1,5 @@
1
1
  import { execFile } from 'node:child_process';
2
+ import { e as execArgsFor, d as detectPackageManager } from './detect-package-manager-DvEthdCw.mjs';
2
3
 
3
4
  const execCode = (error) => {
4
5
  if (!error) {
@@ -25,15 +26,16 @@ const parseSecretNames = (stdout) => {
25
26
  return [...names].toSorted((a, b) => a.localeCompare(b));
26
27
  };
27
28
  const listRemoteSecrets = async (inputs) => {
28
- const args = ["exec", "wrangler", "secret", "list", "--format", "json"];
29
+ const wranglerArgs = ["secret", "list", "--format", "json"];
29
30
  if (inputs.env !== void 0) {
30
- args.push("--env", inputs.env);
31
+ wranglerArgs.push("--env", inputs.env);
31
32
  }
32
33
  if (inputs.temporary) {
33
- args.push("--temporary");
34
+ wranglerArgs.push("--temporary");
34
35
  }
36
+ const { args, command } = execArgsFor(detectPackageManager(inputs.cwd), "wrangler", wranglerArgs);
35
37
  const runner = inputs.runner ?? defaultRunner;
36
- const result = await runner("pnpm", args, inputs.cwd);
38
+ const result = await runner(command, args, inputs.cwd);
37
39
  if (result.code !== 0) {
38
40
  return { error: result.stderr.trim() || `wrangler secret list exited ${String(result.code)}`, names: [], ok: false };
39
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.68",
3
+ "version": "1.0.0-alpha.69",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -61,6 +61,7 @@
61
61
  "@visulima/cerebro": "3.0.0-alpha.32",
62
62
  "@visulima/error": "6.0.0-alpha.34",
63
63
  "@visulima/fs": "5.0.0-alpha.32",
64
+ "@visulima/package": "5.0.0-alpha.23",
64
65
  "@visulima/pail": "4.0.0-alpha.22",
65
66
  "@visulima/path": "3.0.0-alpha.13",
66
67
  "@visulima/spinner": "1.0.0-alpha.4",