@keynv/cli 0.1.0-rc.13 → 0.1.0-rc.14

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.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module';
3
3
  import { randomBytes } from 'crypto';
4
- import { readFileSync, existsSync, statSync, writeFileSync, unlinkSync, mkdirSync, rmSync, readdirSync } from 'fs';
4
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, statSync, readdirSync, mkdirSync, rmSync } from 'fs';
5
5
  import { hostname, platform, homedir } from 'os';
6
- import { relative, isAbsolute, resolve, join, dirname, basename } from 'path';
6
+ import * as nodePath from 'path';
7
+ import { relative, join, isAbsolute, resolve, dirname, basename } from 'path';
7
8
  import { Entry } from '@napi-rs/keyring';
8
9
  import { styleText } from 'util';
9
10
  import j2, { stdin, stdout } from 'process';
@@ -301,16 +302,16 @@ var require_lib = __commonJS({
301
302
  }
302
303
  });
303
304
  }
304
- function isArray(spec, { delimiter } = {}) {
305
+ function isArray(spec, { delimiter: delimiter2 } = {}) {
305
306
  return makeValidator({
306
307
  test: (value, state) => {
307
308
  var _a;
308
309
  const originalValue = value;
309
- if (typeof value === `string` && typeof delimiter !== `undefined`) {
310
+ if (typeof value === `string` && typeof delimiter2 !== `undefined`) {
310
311
  if (typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined`) {
311
312
  if (typeof (state === null || state === void 0 ? void 0 : state.coercion) === `undefined`)
312
313
  return pushError(state, `Unbound coercion result`);
313
- value = value.split(delimiter);
314
+ value = value.split(delimiter2);
314
315
  }
315
316
  }
316
317
  if (!Array.isArray(value))
@@ -328,8 +329,8 @@ var require_lib = __commonJS({
328
329
  }
329
330
  });
330
331
  }
331
- function isSet(spec, { delimiter } = {}) {
332
- const isArrayValidator = isArray(spec, { delimiter });
332
+ function isSet(spec, { delimiter: delimiter2 } = {}) {
333
+ const isArrayValidator = isArray(spec, { delimiter: delimiter2 });
333
334
  return makeValidator({
334
335
  test: (value, state) => {
335
336
  var _a, _b;
@@ -420,16 +421,16 @@ var require_lib = __commonJS({
420
421
  }
421
422
  });
422
423
  }
423
- function isTuple(spec, { delimiter } = {}) {
424
+ function isTuple(spec, { delimiter: delimiter2 } = {}) {
424
425
  const lengthValidator = hasExactLength(spec.length);
425
426
  return makeValidator({
426
427
  test: (value, state) => {
427
428
  var _a;
428
- if (typeof value === `string` && typeof delimiter !== `undefined`) {
429
+ if (typeof value === `string` && typeof delimiter2 !== `undefined`) {
429
430
  if (typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined`) {
430
431
  if (typeof (state === null || state === void 0 ? void 0 : state.coercion) === `undefined`)
431
432
  return pushError(state, `Unbound coercion result`);
432
- value = value.split(delimiter);
433
+ value = value.split(delimiter2);
433
434
  state.coercions.push([(_a = state.p) !== null && _a !== void 0 ? _a : `.`, state.coercion.bind(null, value)]);
434
435
  }
435
436
  }
@@ -1056,7 +1057,7 @@ var require_lib = __commonJS({
1056
1057
  var VERSION, AGENT;
1057
1058
  var init_version = __esm({
1058
1059
  "src/version.ts"() {
1059
- VERSION = "0.1.0-rc.13" ;
1060
+ VERSION = "0.1.0-rc.14" ;
1060
1061
  AGENT = `keynv-cli/${VERSION}`;
1061
1062
  }
1062
1063
  });
@@ -5656,7 +5657,16 @@ var init_http = __esm({
5656
5657
  return this.creds;
5657
5658
  }
5658
5659
  async ensureHydrated() {
5659
- if (this.hydrated) await this.hydrated;
5660
+ if (this.hydrated) {
5661
+ try {
5662
+ await this.hydrated;
5663
+ } catch (err) {
5664
+ process.stderr.write(
5665
+ `${err instanceof Error ? err.message : `keynv: credential load failed \u2014 ${String(err)}`}
5666
+ `
5667
+ );
5668
+ }
5669
+ }
5660
5670
  }
5661
5671
  async setCredentials(creds) {
5662
5672
  this.creds = creds;
@@ -5678,21 +5688,40 @@ var init_http = __esm({
5678
5688
  headers.authorization = `Bearer ${this.creds.access_token}`;
5679
5689
  }
5680
5690
  const url = this.creds ? buildUrl(this.creds.server_url, path, opts.query) : path;
5681
- let res = await fetch(url, {
5682
- method: opts.method ?? "GET",
5683
- headers,
5684
- ...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
5685
- });
5691
+ let res;
5692
+ try {
5693
+ res = await fetch(url, {
5694
+ method: opts.method ?? "GET",
5695
+ headers,
5696
+ ...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
5697
+ });
5698
+ } catch (err) {
5699
+ const cause = err instanceof Error ? err.message : String(err);
5700
+ const serverUrl = this.creds?.server_url ?? url;
5701
+ throw new Error(
5702
+ `keynv: cannot reach server '${serverUrl}' \u2014 ${cause}
5703
+ Check the server is running: curl ${serverUrl}/v1/health`
5704
+ );
5705
+ }
5686
5706
  if (res.status === 401 && this.creds && opts.authed !== false) {
5687
5707
  const refreshed = await tryRefresh(this.creds);
5688
5708
  if (refreshed) {
5689
5709
  this.creds = refreshed;
5690
5710
  headers.authorization = `Bearer ${refreshed.access_token}`;
5691
- res = await fetch(url, {
5692
- method: opts.method ?? "GET",
5693
- headers,
5694
- ...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
5695
- });
5711
+ try {
5712
+ res = await fetch(url, {
5713
+ method: opts.method ?? "GET",
5714
+ headers,
5715
+ ...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
5716
+ });
5717
+ } catch (err) {
5718
+ const cause = err instanceof Error ? err.message : String(err);
5719
+ const serverUrl = this.creds?.server_url ?? url;
5720
+ throw new Error(
5721
+ `keynv: cannot reach server '${serverUrl}' \u2014 ${cause}
5722
+ Check the server is running: curl ${serverUrl}/v1/health`
5723
+ );
5724
+ }
5696
5725
  }
5697
5726
  }
5698
5727
  if (res.status === 204) return void 0;
@@ -5891,6 +5920,121 @@ var init_envFile = __esm({
5891
5920
  };
5892
5921
  }
5893
5922
  });
5923
+ function findProjectRoot(startDir) {
5924
+ const result = walkUp(startDir, (dir) => {
5925
+ for (const marker of PROJECT_MARKERS) {
5926
+ if (existsSync(join(dir, marker))) {
5927
+ return { dir, marker };
5928
+ }
5929
+ }
5930
+ if (existsSync(join(dir, GIT_MARKER))) {
5931
+ return { dir, marker: GIT_MARKER };
5932
+ }
5933
+ return null;
5934
+ });
5935
+ if (!result) return null;
5936
+ return buildRoot(result.dir, result.marker);
5937
+ }
5938
+ function buildRoot(dir, marker) {
5939
+ let suggestedName = basename(dir);
5940
+ let scripts = null;
5941
+ let invalid = false;
5942
+ if (marker === "package.json" || existsSync(join(dir, "package.json"))) {
5943
+ try {
5944
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
5945
+ if (typeof pkg.name === "string" && pkg.name.length > 0) {
5946
+ suggestedName = pkg.name.replace(/^@[^/]+\//, "");
5947
+ }
5948
+ if (pkg.scripts && typeof pkg.scripts === "object") {
5949
+ scripts = Object.fromEntries(
5950
+ Object.entries(pkg.scripts).filter(([, v2]) => typeof v2 === "string")
5951
+ );
5952
+ }
5953
+ } catch {
5954
+ invalid = true;
5955
+ }
5956
+ }
5957
+ return {
5958
+ path: dir,
5959
+ suggestedName,
5960
+ marker,
5961
+ packageJsonScripts: scripts,
5962
+ packageJsonInvalid: invalid
5963
+ };
5964
+ }
5965
+ function findEnvFiles(rootDir) {
5966
+ let entries;
5967
+ try {
5968
+ entries = readdirSync(rootDir);
5969
+ } catch {
5970
+ return [];
5971
+ }
5972
+ const hits = [];
5973
+ for (const name of entries) {
5974
+ if (!ENV_GLOB.test(name)) continue;
5975
+ if (ENV_EXAMPLE.test(name)) continue;
5976
+ if (name === KEYNV_ENV_BASENAME) continue;
5977
+ const full = join(rootDir, name);
5978
+ try {
5979
+ if (!statSync(full).isFile()) continue;
5980
+ } catch {
5981
+ continue;
5982
+ }
5983
+ const suffixMatch = name.match(/^\.env\.(.+)$/);
5984
+ const suffix = suffixMatch ? suffixMatch[1] : null;
5985
+ hits.push({ path: full, name, suffix, suggestedEnv: suggestedEnvForSuffix(suffix) });
5986
+ }
5987
+ hits.sort((a, b2) => {
5988
+ if (a.suffix === null) return -1;
5989
+ if (b2.suffix === null) return 1;
5990
+ return a.name.localeCompare(b2.name);
5991
+ });
5992
+ return hits;
5993
+ }
5994
+ function suggestedEnvForSuffix(suffix) {
5995
+ if (suffix === null) return "dev";
5996
+ switch (suffix) {
5997
+ case "local":
5998
+ case "development":
5999
+ case "dev":
6000
+ return "dev";
6001
+ case "production":
6002
+ case "prod":
6003
+ return "prod";
6004
+ case "staging":
6005
+ case "stage":
6006
+ return "staging";
6007
+ case "test":
6008
+ return "test";
6009
+ case "preview":
6010
+ return "preview";
6011
+ default:
6012
+ return suffix;
6013
+ }
6014
+ }
6015
+ function hasExistingKeynvEnv(rootDir) {
6016
+ return existsSync(join(rootDir, KEYNV_ENV_BASENAME));
6017
+ }
6018
+ var PROJECT_MARKERS, GIT_MARKER, ENV_GLOB, ENV_EXAMPLE, KEYNV_ENV_BASENAME;
6019
+ var init_detect = __esm({
6020
+ "src/init/detect.ts"() {
6021
+ init_fs();
6022
+ PROJECT_MARKERS = [
6023
+ "package.json",
6024
+ "pyproject.toml",
6025
+ "Cargo.toml",
6026
+ "go.mod",
6027
+ "pnpm-workspace.yaml",
6028
+ "deno.json",
6029
+ "deno.jsonc",
6030
+ "requirements.txt"
6031
+ ];
6032
+ GIT_MARKER = ".git";
6033
+ ENV_GLOB = /^\.env(\.[A-Za-z0-9_-]+)?$/;
6034
+ ENV_EXAMPLE = /^\.env\.(example|sample|template|dist|defaults)$/;
6035
+ KEYNV_ENV_BASENAME = ".keynv.env";
6036
+ }
6037
+ });
5894
6038
 
5895
6039
  // src/init/heuristics.ts
5896
6040
  function shannonEntropyBits(s) {
@@ -6017,6 +6161,95 @@ var init_heuristics = __esm({
6017
6161
  ];
6018
6162
  }
6019
6163
  });
6164
+ function aiContextBody() {
6165
+ return `## keynv (secrets)
6166
+
6167
+ This project uses [keynv](https://keynv.dev) for secrets. There is a \`.keynv.env\` file at the project root containing **alias references** like \`OPENAI_API_KEY=@projectname.dev.openai-key\` \u2014 these are NOT real values, they are references to secrets stored in the keynv vault.
6168
+
6169
+ ### Mental model
6170
+
6171
+ - **\`.keynv.env\`** \u2014 checked into git, contains aliases. Safe to read, edit, commit.
6172
+ - **Vault** \u2014 holds the real values. The \`keynv\` CLI reads it on demand.
6173
+ - **\`keynv exec -- <command>\`** \u2014 forks the command in a subprocess where the real values are injected into env vars. The parent process (where you, the AI agent, run) NEVER sees the real values.
6174
+ - **Redactor** \u2014 pipes subprocess stdout/stderr through a masker before they reach this terminal. Even if a script accidentally echoes a secret, you see \`<REDACTED:...>\` here.
6175
+
6176
+ ### What you should do
6177
+
6178
+ When the user asks about secrets or env vars, follow this decision tree:
6179
+
6180
+ | User intent | Run / suggest |
6181
+ |---|---|
6182
+ | "Add a new API key" / "set up a secret" | \`keynv secret create\` (interactive prompt picks project + env + key) |
6183
+ | "I have a \`.env\`, migrate it" | \`keynv init\` (you're seeing this file because that already happened) |
6184
+ | "Run the app / dev server / tests" | \`keynv exec -- <their existing command>\` (auto-loads \`.keynv.env\`) |
6185
+ | "Show me the value of X" | \`keynv secret get @alias\` \u2014 but copy to clipboard, do NOT print the value into chat / terminal |
6186
+ | "Rotate this key" | \`keynv secret rotate @alias\` |
6187
+ | "Add a new env var to the project" | Edit \`.keynv.env\` and add \`NAME=@project.env.key\` (after creating the secret with \`keynv secret create\`) |
6188
+ | "Who has access?" | \`keynv member list <project>\` |
6189
+
6190
+ ### Hard rules \u2014 do not violate
6191
+
6192
+ 1. **Never print resolved secret values** to the chat, terminal, log, or any file. The user's editor / scrollback / chat history is an attack surface.
6193
+ 2. **Never write a \`.env\` file** (or any file) containing real secret values. The whole point of keynv is that those don't exist on disk.
6194
+ 3. **Never edit \`.keynv.env\` to substitute resolved values for aliases.** The file should only ever contain \`@project.env.key\` references and plain non-secret literals.
6195
+ 4. **Use \`keynv exec --\` to run commands that need env vars.** Don't try to load \`.keynv.env\` yourself by parsing it manually; the wrapper handles resolution + redaction + subprocess isolation.
6196
+ 5. **If a tool fails with "blocked by keynv-guard" or similar**, do NOT try to work around it. Tell the user \u2014 that's a sign of a misconfiguration, not an obstacle to bypass.
6197
+ 6. **Treat the alias literal (\`@project.env.key\`) as the canonical reference**, never the resolved value. When you write code, configs, or commands that need a secret, write the alias.
6198
+
6199
+ ### Quick reference
6200
+
6201
+ \`\`\`bash
6202
+ keynv # interactive TUI menu (pick projects, secrets, members)
6203
+ keynv exec -- <cmd> # run cmd with .keynv.env loaded
6204
+ keynv secret create # walk through creating a new secret
6205
+ keynv secret list <project> # list aliases (no values)
6206
+ keynv whoami # who am I logged in as
6207
+ keynv --help # full command list
6208
+ \`\`\`
6209
+
6210
+ If \`.keynv.env\` is missing or empty, the user probably hasn't run \`keynv init\` yet \u2014 suggest they do so before they paste a secret into a \`.env\`.`;
6211
+ }
6212
+ function renderKeynvBlock() {
6213
+ return `${KEYNV_BLOCK_START}
6214
+ ${aiContextBody()}
6215
+ ${KEYNV_BLOCK_END}`;
6216
+ }
6217
+ function writeAiContext(rootPath) {
6218
+ const path = join(rootPath, AGENTS_FILE_BASENAME);
6219
+ const block = renderKeynvBlock();
6220
+ if (!existsSync(path)) {
6221
+ const initial = `# Agent guidance for this project
6222
+
6223
+ ${block}
6224
+ `;
6225
+ writeFileSync(path, initial);
6226
+ return "created";
6227
+ }
6228
+ const existing = readFileSync(path, "utf8");
6229
+ const startIdx = existing.indexOf(KEYNV_BLOCK_START);
6230
+ const endIdx = existing.indexOf(KEYNV_BLOCK_END);
6231
+ if (startIdx >= 0 && endIdx > startIdx) {
6232
+ const before = existing.slice(0, startIdx);
6233
+ const after = existing.slice(endIdx + KEYNV_BLOCK_END.length);
6234
+ const next = `${before}${block}${after}`;
6235
+ if (next === existing) return "unchanged";
6236
+ writeFileSync(path, next);
6237
+ return "updated";
6238
+ }
6239
+ const sep = existing.length === 0 ? "" : existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
6240
+ const trailingNewline = existing.endsWith("\n") ? "" : "\n";
6241
+ writeFileSync(path, `${existing}${trailingNewline}${sep}${block}
6242
+ `);
6243
+ return "appended";
6244
+ }
6245
+ var AGENTS_FILE_BASENAME, KEYNV_BLOCK_START, KEYNV_BLOCK_END;
6246
+ var init_aiContext = __esm({
6247
+ "src/init/aiContext.ts"() {
6248
+ AGENTS_FILE_BASENAME = "AGENTS.md";
6249
+ KEYNV_BLOCK_START = "<!-- keynv:start -- generated by `keynv init`; safe to leave intact, will be refreshed on re-run -->";
6250
+ KEYNV_BLOCK_END = "<!-- keynv:end -->";
6251
+ }
6252
+ });
6020
6253
 
6021
6254
  // ../../node_modules/.pnpm/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
6022
6255
  var getCodePointsLength, isFullWidth, isWideNotCJKTNotEmoji;
@@ -7172,210 +7405,6 @@ ${c2}
7172
7405
  } }).prompt();
7173
7406
  }
7174
7407
  });
7175
- function aiContextBody() {
7176
- return `## keynv (secrets)
7177
-
7178
- This project uses [keynv](https://keynv.dev) for secrets. There is a \`.keynv.env\` file at the project root containing **alias references** like \`OPENAI_API_KEY=@projectname.dev.openai-key\` \u2014 these are NOT real values, they are references to secrets stored in the keynv vault.
7179
-
7180
- ### Mental model
7181
-
7182
- - **\`.keynv.env\`** \u2014 checked into git, contains aliases. Safe to read, edit, commit.
7183
- - **Vault** \u2014 holds the real values. The \`keynv\` CLI reads it on demand.
7184
- - **\`keynv exec -- <command>\`** \u2014 forks the command in a subprocess where the real values are injected into env vars. The parent process (where you, the AI agent, run) NEVER sees the real values.
7185
- - **Redactor** \u2014 pipes subprocess stdout/stderr through a masker before they reach this terminal. Even if a script accidentally echoes a secret, you see \`<REDACTED:...>\` here.
7186
-
7187
- ### What you should do
7188
-
7189
- When the user asks about secrets or env vars, follow this decision tree:
7190
-
7191
- | User intent | Run / suggest |
7192
- |---|---|
7193
- | "Add a new API key" / "set up a secret" | \`keynv secret create\` (interactive prompt picks project + env + key) |
7194
- | "I have a \`.env\`, migrate it" | \`keynv init\` (you're seeing this file because that already happened) |
7195
- | "Run the app / dev server / tests" | \`keynv exec -- <their existing command>\` (auto-loads \`.keynv.env\`) |
7196
- | "Show me the value of X" | \`keynv secret get @alias\` \u2014 but copy to clipboard, do NOT print the value into chat / terminal |
7197
- | "Rotate this key" | \`keynv secret rotate @alias\` |
7198
- | "Add a new env var to the project" | Edit \`.keynv.env\` and add \`NAME=@project.env.key\` (after creating the secret with \`keynv secret create\`) |
7199
- | "Who has access?" | \`keynv member list <project>\` |
7200
-
7201
- ### Hard rules \u2014 do not violate
7202
-
7203
- 1. **Never print resolved secret values** to the chat, terminal, log, or any file. The user's editor / scrollback / chat history is an attack surface.
7204
- 2. **Never write a \`.env\` file** (or any file) containing real secret values. The whole point of keynv is that those don't exist on disk.
7205
- 3. **Never edit \`.keynv.env\` to substitute resolved values for aliases.** The file should only ever contain \`@project.env.key\` references and plain non-secret literals.
7206
- 4. **Use \`keynv exec --\` to run commands that need env vars.** Don't try to load \`.keynv.env\` yourself by parsing it manually; the wrapper handles resolution + redaction + subprocess isolation.
7207
- 5. **If a tool fails with "blocked by keynv-guard" or similar**, do NOT try to work around it. Tell the user \u2014 that's a sign of a misconfiguration, not an obstacle to bypass.
7208
- 6. **Treat the alias literal (\`@project.env.key\`) as the canonical reference**, never the resolved value. When you write code, configs, or commands that need a secret, write the alias.
7209
-
7210
- ### Quick reference
7211
-
7212
- \`\`\`bash
7213
- keynv # interactive TUI menu (pick projects, secrets, members)
7214
- keynv exec -- <cmd> # run cmd with .keynv.env loaded
7215
- keynv secret create # walk through creating a new secret
7216
- keynv secret list <project> # list aliases (no values)
7217
- keynv whoami # who am I logged in as
7218
- keynv --help # full command list
7219
- \`\`\`
7220
-
7221
- If \`.keynv.env\` is missing or empty, the user probably hasn't run \`keynv init\` yet \u2014 suggest they do so before they paste a secret into a \`.env\`.`;
7222
- }
7223
- function renderKeynvBlock() {
7224
- return `${KEYNV_BLOCK_START}
7225
- ${aiContextBody()}
7226
- ${KEYNV_BLOCK_END}`;
7227
- }
7228
- function writeAiContext(rootPath) {
7229
- const path = join(rootPath, AGENTS_FILE_BASENAME);
7230
- const block = renderKeynvBlock();
7231
- if (!existsSync(path)) {
7232
- const initial = `# Agent guidance for this project
7233
-
7234
- ${block}
7235
- `;
7236
- writeFileSync(path, initial);
7237
- return "created";
7238
- }
7239
- const existing = readFileSync(path, "utf8");
7240
- const startIdx = existing.indexOf(KEYNV_BLOCK_START);
7241
- const endIdx = existing.indexOf(KEYNV_BLOCK_END);
7242
- if (startIdx >= 0 && endIdx > startIdx) {
7243
- const before = existing.slice(0, startIdx);
7244
- const after = existing.slice(endIdx + KEYNV_BLOCK_END.length);
7245
- const next = `${before}${block}${after}`;
7246
- if (next === existing) return "unchanged";
7247
- writeFileSync(path, next);
7248
- return "updated";
7249
- }
7250
- const sep = existing.length === 0 ? "" : existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
7251
- const trailingNewline = existing.endsWith("\n") ? "" : "\n";
7252
- writeFileSync(path, `${existing}${trailingNewline}${sep}${block}
7253
- `);
7254
- return "appended";
7255
- }
7256
- var AGENTS_FILE_BASENAME, KEYNV_BLOCK_START, KEYNV_BLOCK_END;
7257
- var init_aiContext = __esm({
7258
- "src/init/aiContext.ts"() {
7259
- AGENTS_FILE_BASENAME = "AGENTS.md";
7260
- KEYNV_BLOCK_START = "<!-- keynv:start -- generated by `keynv init`; safe to leave intact, will be refreshed on re-run -->";
7261
- KEYNV_BLOCK_END = "<!-- keynv:end -->";
7262
- }
7263
- });
7264
- function findProjectRoot(startDir) {
7265
- const result = walkUp(startDir, (dir) => {
7266
- for (const marker of PROJECT_MARKERS) {
7267
- if (existsSync(join(dir, marker))) {
7268
- return { dir, marker };
7269
- }
7270
- }
7271
- if (existsSync(join(dir, GIT_MARKER))) {
7272
- return { dir, marker: GIT_MARKER };
7273
- }
7274
- return null;
7275
- });
7276
- if (!result) return null;
7277
- return buildRoot(result.dir, result.marker);
7278
- }
7279
- function buildRoot(dir, marker) {
7280
- let suggestedName = basename(dir);
7281
- let scripts = null;
7282
- let invalid = false;
7283
- if (marker === "package.json" || existsSync(join(dir, "package.json"))) {
7284
- try {
7285
- const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
7286
- if (typeof pkg.name === "string" && pkg.name.length > 0) {
7287
- suggestedName = pkg.name.replace(/^@[^/]+\//, "");
7288
- }
7289
- if (pkg.scripts && typeof pkg.scripts === "object") {
7290
- scripts = Object.fromEntries(
7291
- Object.entries(pkg.scripts).filter(([, v2]) => typeof v2 === "string")
7292
- );
7293
- }
7294
- } catch {
7295
- invalid = true;
7296
- }
7297
- }
7298
- return {
7299
- path: dir,
7300
- suggestedName,
7301
- marker,
7302
- packageJsonScripts: scripts,
7303
- packageJsonInvalid: invalid
7304
- };
7305
- }
7306
- function findEnvFiles(rootDir) {
7307
- let entries;
7308
- try {
7309
- entries = readdirSync(rootDir);
7310
- } catch {
7311
- return [];
7312
- }
7313
- const hits = [];
7314
- for (const name of entries) {
7315
- if (!ENV_GLOB.test(name)) continue;
7316
- if (ENV_EXAMPLE.test(name)) continue;
7317
- if (name === KEYNV_ENV_BASENAME) continue;
7318
- const full = join(rootDir, name);
7319
- try {
7320
- if (!statSync(full).isFile()) continue;
7321
- } catch {
7322
- continue;
7323
- }
7324
- const suffixMatch = name.match(/^\.env\.(.+)$/);
7325
- const suffix = suffixMatch ? suffixMatch[1] : null;
7326
- hits.push({ path: full, name, suffix, suggestedEnv: suggestedEnvForSuffix(suffix) });
7327
- }
7328
- hits.sort((a, b2) => {
7329
- if (a.suffix === null) return -1;
7330
- if (b2.suffix === null) return 1;
7331
- return a.name.localeCompare(b2.name);
7332
- });
7333
- return hits;
7334
- }
7335
- function suggestedEnvForSuffix(suffix) {
7336
- if (suffix === null) return "dev";
7337
- switch (suffix) {
7338
- case "local":
7339
- case "development":
7340
- case "dev":
7341
- return "dev";
7342
- case "production":
7343
- case "prod":
7344
- return "prod";
7345
- case "staging":
7346
- case "stage":
7347
- return "staging";
7348
- case "test":
7349
- return "test";
7350
- case "preview":
7351
- return "preview";
7352
- default:
7353
- return suffix;
7354
- }
7355
- }
7356
- function hasExistingKeynvEnv(rootDir) {
7357
- return existsSync(join(rootDir, KEYNV_ENV_BASENAME));
7358
- }
7359
- var PROJECT_MARKERS, GIT_MARKER, ENV_GLOB, ENV_EXAMPLE, KEYNV_ENV_BASENAME;
7360
- var init_detect = __esm({
7361
- "src/init/detect.ts"() {
7362
- init_fs();
7363
- PROJECT_MARKERS = [
7364
- "package.json",
7365
- "pyproject.toml",
7366
- "Cargo.toml",
7367
- "go.mod",
7368
- "pnpm-workspace.yaml",
7369
- "deno.json",
7370
- "deno.jsonc",
7371
- "requirements.txt"
7372
- ];
7373
- GIT_MARKER = ".git";
7374
- ENV_GLOB = /^\.env(\.[A-Za-z0-9_-]+)?$/;
7375
- ENV_EXAMPLE = /^\.env\.(example|sample|template|dist|defaults)$/;
7376
- KEYNV_ENV_BASENAME = ".keynv.env";
7377
- }
7378
- });
7379
7408
 
7380
7409
  // src/init/scriptWrap.ts
7381
7410
  function analyzeScript(name, command) {
@@ -7596,6 +7625,14 @@ __export(init_exports, {
7596
7625
  UserCancelled: () => UserCancelled,
7597
7626
  runInitFlow: () => runInitFlow
7598
7627
  });
7628
+ function toAliasKey(name) {
7629
+ if (!name) return name;
7630
+ const KEY_RE3 = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
7631
+ if (KEY_RE3.test(name)) return name;
7632
+ const normalised = name.toLowerCase().replace(/_/g, "-");
7633
+ if (KEY_RE3.test(normalised)) return normalised;
7634
+ return normalised.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64) || "key";
7635
+ }
7599
7636
  async function runInitFlow(client, opts) {
7600
7637
  ge("keynv init");
7601
7638
  const root = findProjectRoot(opts.cwd);
@@ -7754,7 +7791,7 @@ ${detail}`
7754
7791
  if (!selected.has(`${env2}|${e2.name}`)) continue;
7755
7792
  i++;
7756
7793
  s.message(`Uploading (${i}/${totalToUpload}) [${env2}] ${e2.name}`);
7757
- const aliasKey = e2.name.toLowerCase().replace(/_/g, "-");
7794
+ const aliasKey = toAliasKey(e2.name);
7758
7795
  try {
7759
7796
  await client.request(`/v1/projects/${projectId2}/secrets`, {
7760
7797
  method: "POST",
@@ -7812,6 +7849,30 @@ ${detail}`
7812
7849
  R2.error(`Failed to write .keynv.env: ${err instanceof Error ? err.message : String(err)}`);
7813
7850
  return { exitCode: 1 };
7814
7851
  }
7852
+ const otherEnvsWithAliases = distinctEnvs.filter(
7853
+ (env2) => env2 !== defaultEnv && (uploadedByEnv.get(env2)?.size ?? 0) > 0
7854
+ );
7855
+ for (const env2 of otherEnvsWithAliases) {
7856
+ const envUploaded = uploadedByEnv.get(env2) ?? /* @__PURE__ */ new Map();
7857
+ const envLiterals = (perEnv.get(env2) ?? []).filter((e2) => !selected.has(`${env2}|${e2.name}`));
7858
+ const envFilePath = join(root.path, `.keynv.${env2}.env`);
7859
+ try {
7860
+ const lines = composeKeynvEnv({
7861
+ uploadedAliases: envUploaded,
7862
+ literals: envLiterals,
7863
+ mergeWithExisting: null
7864
+ });
7865
+ writeFileSync(envFilePath, `${lines.join("\n")}
7866
+ `);
7867
+ R2.success(
7868
+ `Wrote .keynv.${env2}.env (${envUploaded.size} secret alias${envUploaded.size === 1 ? "" : "es"} from "${env2}")`
7869
+ );
7870
+ } catch (err) {
7871
+ R2.warn(
7872
+ `Could not write .keynv.${env2}.env: ${err instanceof Error ? err.message : String(err)}`
7873
+ );
7874
+ }
7875
+ }
7815
7876
  try {
7816
7877
  const outcome = writeAiContext(root.path);
7817
7878
  if (outcome === "created") R2.success("Wrote AGENTS.md (so AI agents understand keynv)");
@@ -7848,7 +7909,8 @@ ${detail}`
7848
7909
  if (otherEnvs.length > 0) {
7849
7910
  const lines = otherEnvs.map((env2) => {
7850
7911
  const count = uploadedByEnv.get(env2)?.size ?? 0;
7851
- return ` ${env2}: ${count} secret${count === 1 ? "" : "s"} in vault (use \`keynv exec --from .keynv.${env2}.env -- <cmd>\` after creating that file)`;
7912
+ const hasFile = count > 0;
7913
+ return hasFile ? ` ${env2}: ${count} secret${count === 1 ? "" : "s"} \u2192 .keynv.${env2}.env (use \`keynv exec --from .keynv.${env2}.env -- <cmd>\`)` : ` ${env2}: 0 secrets in vault (no alias file written)`;
7852
7914
  });
7853
7915
  Se(lines.join("\n"), "Secrets in other envs");
7854
7916
  }
@@ -8098,7 +8160,7 @@ var init_tty = __esm({
8098
8160
  }
8099
8161
  });
8100
8162
  function sleep(ms) {
8101
- return new Promise((resolve3) => setTimeout(resolve3, ms));
8163
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
8102
8164
  }
8103
8165
  async function errorMessage(res, fallback) {
8104
8166
  const text = await res.text();
@@ -8340,16 +8402,16 @@ async function runSecretMenu(client, project, alias2) {
8340
8402
  async function copyToClipboard(value) {
8341
8403
  const platform2 = process.platform;
8342
8404
  const cmd = platform2 === "darwin" ? ["pbcopy", []] : platform2 === "win32" ? ["clip", []] : ["xclip", ["-selection", "clipboard"]];
8343
- return new Promise((resolve3) => {
8405
+ return new Promise((resolve4) => {
8344
8406
  try {
8345
8407
  const child = spawn(cmd[0], cmd[1], {
8346
8408
  stdio: ["pipe", "ignore", "ignore"]
8347
8409
  });
8348
- child.on("error", () => resolve3(false));
8349
- child.on("exit", (code) => resolve3(code === 0));
8410
+ child.on("error", () => resolve4(false));
8411
+ child.on("exit", (code) => resolve4(code === 0));
8350
8412
  child.stdin.end(value);
8351
8413
  } catch {
8352
- resolve3(false);
8414
+ resolve4(false);
8353
8415
  }
8354
8416
  });
8355
8417
  }
@@ -25095,7 +25157,7 @@ var require_named_placeholders = __commonJS({
25095
25157
  }
25096
25158
  return s;
25097
25159
  }
25098
- function join6(tree) {
25160
+ function join8(tree) {
25099
25161
  if (tree.length === 1) {
25100
25162
  return tree;
25101
25163
  }
@@ -25121,7 +25183,7 @@ var require_named_placeholders = __commonJS({
25121
25183
  if (cache2 && (tree = cache2.get(query))) {
25122
25184
  return toArrayParams(tree, paramsObj);
25123
25185
  }
25124
- tree = join6(parse(query));
25186
+ tree = join8(parse(query));
25125
25187
  if (cache2) {
25126
25188
  cache2.set(query, tree);
25127
25189
  }
@@ -25277,11 +25339,11 @@ var require_connection = __commonJS({
25277
25339
  const config = this.config;
25278
25340
  tracePromise(
25279
25341
  connectChannel,
25280
- () => new Promise((resolve3, reject) => {
25342
+ () => new Promise((resolve4, reject) => {
25281
25343
  let onConnect, onError;
25282
25344
  onConnect = (param) => {
25283
25345
  this.removeListener("error", onError);
25284
- resolve3(param);
25346
+ resolve4(param);
25285
25347
  };
25286
25348
  onError = (err) => {
25287
25349
  this.removeListener("connect", onConnect);
@@ -25706,9 +25768,9 @@ var require_connection = __commonJS({
25706
25768
  } else if (shouldTrace(queryChannel)) {
25707
25769
  tracePromise(
25708
25770
  queryChannel,
25709
- () => new Promise((resolve3, reject) => {
25771
+ () => new Promise((resolve4, reject) => {
25710
25772
  cmdQuery.once("error", reject);
25711
- cmdQuery.once("end", () => resolve3());
25773
+ cmdQuery.once("end", () => resolve4());
25712
25774
  this.addCommand(cmdQuery);
25713
25775
  }),
25714
25776
  () => {
@@ -25855,12 +25917,12 @@ var require_connection = __commonJS({
25855
25917
  } else if (shouldTrace(executeChannel)) {
25856
25918
  tracePromise(
25857
25919
  executeChannel,
25858
- () => new Promise((resolve3, reject) => {
25920
+ () => new Promise((resolve4, reject) => {
25859
25921
  prepareAndExecute((err) => {
25860
25922
  executeCommand.emit("error", err);
25861
25923
  });
25862
25924
  executeCommand.once("error", reject);
25863
- executeCommand.once("end", () => resolve3());
25925
+ executeCommand.once("end", () => resolve4());
25864
25926
  }),
25865
25927
  () => {
25866
25928
  const server = getServerContext(this.config);
@@ -26125,13 +26187,13 @@ var require_capture_local_err = __commonJS({
26125
26187
  var require_make_done_cb = __commonJS({
26126
26188
  "../../node_modules/.pnpm/mysql2@3.22.3_@types+node@24.12.3/node_modules/mysql2/lib/promise/make_done_cb.js"(exports, module) {
26127
26189
  var { applyCapturedStack } = require_capture_local_err();
26128
- function makeDoneCb(resolve3, reject, stackHolder) {
26190
+ function makeDoneCb(resolve4, reject, stackHolder) {
26129
26191
  return function(err, rows, fields) {
26130
26192
  if (err) {
26131
26193
  applyCapturedStack(err, stackHolder);
26132
26194
  reject(err);
26133
26195
  } else {
26134
- resolve3([rows, fields]);
26196
+ resolve4([rows, fields]);
26135
26197
  }
26136
26198
  };
26137
26199
  }
@@ -26154,8 +26216,8 @@ var require_prepared_statement_info = __commonJS({
26154
26216
  const stackHolder = captureStackHolder(
26155
26217
  _PromisePreparedStatementInfo.prototype.execute
26156
26218
  );
26157
- return new this.Promise((resolve3, reject) => {
26158
- const done = makeDoneCb(resolve3, reject, stackHolder);
26219
+ return new this.Promise((resolve4, reject) => {
26220
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26159
26221
  if (parameters) {
26160
26222
  s.execute(parameters, done);
26161
26223
  } else {
@@ -26164,9 +26226,9 @@ var require_prepared_statement_info = __commonJS({
26164
26226
  });
26165
26227
  }
26166
26228
  close() {
26167
- return new this.Promise((resolve3) => {
26229
+ return new this.Promise((resolve4) => {
26168
26230
  this.statement.close();
26169
- resolve3();
26231
+ resolve4();
26170
26232
  });
26171
26233
  }
26172
26234
  };
@@ -26237,8 +26299,8 @@ var require_connection2 = __commonJS({
26237
26299
  "Callback function is not available with promise clients."
26238
26300
  );
26239
26301
  }
26240
- return new this.Promise((resolve3, reject) => {
26241
- const done = makeDoneCb(resolve3, reject, stackHolder);
26302
+ return new this.Promise((resolve4, reject) => {
26303
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26242
26304
  if (params !== void 0) {
26243
26305
  c2.query(query, params, done);
26244
26306
  } else {
@@ -26254,8 +26316,8 @@ var require_connection2 = __commonJS({
26254
26316
  "Callback function is not available with promise clients."
26255
26317
  );
26256
26318
  }
26257
- return new this.Promise((resolve3, reject) => {
26258
- const done = makeDoneCb(resolve3, reject, stackHolder);
26319
+ return new this.Promise((resolve4, reject) => {
26320
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26259
26321
  if (params !== void 0) {
26260
26322
  c2.execute(query, params, done);
26261
26323
  } else {
@@ -26264,8 +26326,8 @@ var require_connection2 = __commonJS({
26264
26326
  });
26265
26327
  }
26266
26328
  end() {
26267
- return new this.Promise((resolve3) => {
26268
- this.connection.end(resolve3);
26329
+ return new this.Promise((resolve4) => {
26330
+ this.connection.end(resolve4);
26269
26331
  });
26270
26332
  }
26271
26333
  async [Symbol.asyncDispose]() {
@@ -26278,16 +26340,16 @@ var require_connection2 = __commonJS({
26278
26340
  const stackHolder = captureStackHolder(
26279
26341
  _PromiseConnection.prototype.beginTransaction
26280
26342
  );
26281
- return new this.Promise((resolve3, reject) => {
26282
- const done = makeDoneCb(resolve3, reject, stackHolder);
26343
+ return new this.Promise((resolve4, reject) => {
26344
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26283
26345
  c2.beginTransaction(done);
26284
26346
  });
26285
26347
  }
26286
26348
  commit() {
26287
26349
  const c2 = this.connection;
26288
26350
  const stackHolder = captureStackHolder(_PromiseConnection.prototype.commit);
26289
- return new this.Promise((resolve3, reject) => {
26290
- const done = makeDoneCb(resolve3, reject, stackHolder);
26351
+ return new this.Promise((resolve4, reject) => {
26352
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26291
26353
  c2.commit(done);
26292
26354
  });
26293
26355
  }
@@ -26296,21 +26358,21 @@ var require_connection2 = __commonJS({
26296
26358
  const stackHolder = captureStackHolder(
26297
26359
  _PromiseConnection.prototype.rollback
26298
26360
  );
26299
- return new this.Promise((resolve3, reject) => {
26300
- const done = makeDoneCb(resolve3, reject, stackHolder);
26361
+ return new this.Promise((resolve4, reject) => {
26362
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26301
26363
  c2.rollback(done);
26302
26364
  });
26303
26365
  }
26304
26366
  ping() {
26305
26367
  const c2 = this.connection;
26306
26368
  const stackHolder = captureStackHolder(_PromiseConnection.prototype.ping);
26307
- return new this.Promise((resolve3, reject) => {
26369
+ return new this.Promise((resolve4, reject) => {
26308
26370
  c2.ping((err) => {
26309
26371
  if (err) {
26310
26372
  applyCapturedStack(err, stackHolder);
26311
26373
  reject(err);
26312
26374
  } else {
26313
- resolve3(true);
26375
+ resolve4(true);
26314
26376
  }
26315
26377
  });
26316
26378
  });
@@ -26318,13 +26380,13 @@ var require_connection2 = __commonJS({
26318
26380
  reset() {
26319
26381
  const c2 = this.connection;
26320
26382
  const stackHolder = captureStackHolder(_PromiseConnection.prototype.reset);
26321
- return new this.Promise((resolve3, reject) => {
26383
+ return new this.Promise((resolve4, reject) => {
26322
26384
  c2.reset((err) => {
26323
26385
  if (err) {
26324
26386
  applyCapturedStack(err, stackHolder);
26325
26387
  reject(err);
26326
26388
  } else {
26327
- resolve3();
26389
+ resolve4();
26328
26390
  }
26329
26391
  });
26330
26392
  });
@@ -26332,13 +26394,13 @@ var require_connection2 = __commonJS({
26332
26394
  connect() {
26333
26395
  const c2 = this.connection;
26334
26396
  const stackHolder = captureStackHolder(_PromiseConnection.prototype.connect);
26335
- return new this.Promise((resolve3, reject) => {
26397
+ return new this.Promise((resolve4, reject) => {
26336
26398
  c2.connect((err, param) => {
26337
26399
  if (err) {
26338
26400
  applyCapturedStack(err, stackHolder);
26339
26401
  reject(err);
26340
26402
  } else {
26341
- resolve3(param);
26403
+ resolve4(param);
26342
26404
  }
26343
26405
  });
26344
26406
  });
@@ -26347,7 +26409,7 @@ var require_connection2 = __commonJS({
26347
26409
  const c2 = this.connection;
26348
26410
  const promiseImpl = this.Promise;
26349
26411
  const stackHolder = captureStackHolder(_PromiseConnection.prototype.prepare);
26350
- return new this.Promise((resolve3, reject) => {
26412
+ return new this.Promise((resolve4, reject) => {
26351
26413
  c2.prepare(options, (err, statement) => {
26352
26414
  if (err) {
26353
26415
  applyCapturedStack(err, stackHolder);
@@ -26357,7 +26419,7 @@ var require_connection2 = __commonJS({
26357
26419
  statement,
26358
26420
  promiseImpl
26359
26421
  );
26360
- resolve3(wrappedStatement);
26422
+ resolve4(wrappedStatement);
26361
26423
  }
26362
26424
  });
26363
26425
  });
@@ -26367,13 +26429,13 @@ var require_connection2 = __commonJS({
26367
26429
  const stackHolder = captureStackHolder(
26368
26430
  _PromiseConnection.prototype.changeUser
26369
26431
  );
26370
- return new this.Promise((resolve3, reject) => {
26432
+ return new this.Promise((resolve4, reject) => {
26371
26433
  c2.changeUser(options, (err) => {
26372
26434
  if (err) {
26373
26435
  applyCapturedStack(err, stackHolder);
26374
26436
  reject(err);
26375
26437
  } else {
26376
- resolve3();
26438
+ resolve4();
26377
26439
  }
26378
26440
  });
26379
26441
  });
@@ -26835,12 +26897,12 @@ var require_pool2 = __commonJS({
26835
26897
  }
26836
26898
  getConnection() {
26837
26899
  const corePool = this.pool;
26838
- return new this.Promise((resolve3, reject) => {
26900
+ return new this.Promise((resolve4, reject) => {
26839
26901
  corePool.getConnection((err, coreConnection) => {
26840
26902
  if (err) {
26841
26903
  reject(err);
26842
26904
  } else {
26843
- resolve3(new PromisePoolConnection(coreConnection, this.Promise));
26905
+ resolve4(new PromisePoolConnection(coreConnection, this.Promise));
26844
26906
  }
26845
26907
  });
26846
26908
  });
@@ -26856,8 +26918,8 @@ var require_pool2 = __commonJS({
26856
26918
  "Callback function is not available with promise clients."
26857
26919
  );
26858
26920
  }
26859
- return new this.Promise((resolve3, reject) => {
26860
- const done = makeDoneCb(resolve3, reject, stackHolder);
26921
+ return new this.Promise((resolve4, reject) => {
26922
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26861
26923
  if (args !== void 0) {
26862
26924
  corePool.query(sql, args, done);
26863
26925
  } else {
@@ -26873,8 +26935,8 @@ var require_pool2 = __commonJS({
26873
26935
  "Callback function is not available with promise clients."
26874
26936
  );
26875
26937
  }
26876
- return new this.Promise((resolve3, reject) => {
26877
- const done = makeDoneCb(resolve3, reject, stackHolder);
26938
+ return new this.Promise((resolve4, reject) => {
26939
+ const done = makeDoneCb(resolve4, reject, stackHolder);
26878
26940
  if (args) {
26879
26941
  corePool.execute(sql, args, done);
26880
26942
  } else {
@@ -26885,13 +26947,13 @@ var require_pool2 = __commonJS({
26885
26947
  end() {
26886
26948
  const corePool = this.pool;
26887
26949
  const stackHolder = captureStackHolder(_PromisePool.prototype.end);
26888
- return new this.Promise((resolve3, reject) => {
26950
+ return new this.Promise((resolve4, reject) => {
26889
26951
  corePool.end((err) => {
26890
26952
  if (err) {
26891
26953
  applyCapturedStack(err, stackHolder);
26892
26954
  reject(err);
26893
26955
  } else {
26894
- resolve3();
26956
+ resolve4();
26895
26957
  }
26896
26958
  });
26897
26959
  });
@@ -27316,12 +27378,12 @@ var require_pool_cluster2 = __commonJS({
27316
27378
  }
27317
27379
  getConnection() {
27318
27380
  const corePoolNamespace = this.poolNamespace;
27319
- return new this.Promise((resolve3, reject) => {
27381
+ return new this.Promise((resolve4, reject) => {
27320
27382
  corePoolNamespace.getConnection((err, coreConnection) => {
27321
27383
  if (err) {
27322
27384
  reject(err);
27323
27385
  } else {
27324
- resolve3(new PromisePoolConnection(coreConnection, this.Promise));
27386
+ resolve4(new PromisePoolConnection(coreConnection, this.Promise));
27325
27387
  }
27326
27388
  });
27327
27389
  });
@@ -27336,8 +27398,8 @@ var require_pool_cluster2 = __commonJS({
27336
27398
  "Callback function is not available with promise clients."
27337
27399
  );
27338
27400
  }
27339
- return new this.Promise((resolve3, reject) => {
27340
- const done = makeDoneCb(resolve3, reject, stackHolder);
27401
+ return new this.Promise((resolve4, reject) => {
27402
+ const done = makeDoneCb(resolve4, reject, stackHolder);
27341
27403
  corePoolNamespace.query(sql, values, done);
27342
27404
  });
27343
27405
  }
@@ -27351,8 +27413,8 @@ var require_pool_cluster2 = __commonJS({
27351
27413
  "Callback function is not available with promise clients."
27352
27414
  );
27353
27415
  }
27354
- return new this.Promise((resolve3, reject) => {
27355
- const done = makeDoneCb(resolve3, reject, stackHolder);
27416
+ return new this.Promise((resolve4, reject) => {
27417
+ const done = makeDoneCb(resolve4, reject, stackHolder);
27356
27418
  corePoolNamespace.execute(sql, values, done);
27357
27419
  });
27358
27420
  }
@@ -27390,9 +27452,9 @@ var require_promise = __commonJS({
27390
27452
  "no Promise implementation available.Use promise-enabled node version or pass userland Promise implementation as parameter, for example: { Promise: require('bluebird') }"
27391
27453
  );
27392
27454
  }
27393
- return new thePromise((resolve3, reject) => {
27455
+ return new thePromise((resolve4, reject) => {
27394
27456
  coreConnection.once("connect", () => {
27395
- resolve3(new PromiseConnection(coreConnection, thePromise));
27457
+ resolve4(new PromiseConnection(coreConnection, thePromise));
27396
27458
  });
27397
27459
  coreConnection.once("error", (err) => {
27398
27460
  applyCapturedStack(err, stackHolder);
@@ -27419,7 +27481,7 @@ var require_promise = __commonJS({
27419
27481
  }
27420
27482
  getConnection(pattern, selector) {
27421
27483
  const corePoolCluster = this.poolCluster;
27422
- return new this.Promise((resolve3, reject) => {
27484
+ return new this.Promise((resolve4, reject) => {
27423
27485
  corePoolCluster.getConnection(
27424
27486
  pattern,
27425
27487
  selector,
@@ -27427,7 +27489,7 @@ var require_promise = __commonJS({
27427
27489
  if (err) {
27428
27490
  reject(err);
27429
27491
  } else {
27430
- resolve3(new PromisePoolConnection(coreConnection, this.Promise));
27492
+ resolve4(new PromisePoolConnection(coreConnection, this.Promise));
27431
27493
  }
27432
27494
  }
27433
27495
  );
@@ -27441,8 +27503,8 @@ var require_promise = __commonJS({
27441
27503
  "Callback function is not available with promise clients."
27442
27504
  );
27443
27505
  }
27444
- return new this.Promise((resolve3, reject) => {
27445
- const done = makeDoneCb(resolve3, reject, stackHolder);
27506
+ return new this.Promise((resolve4, reject) => {
27507
+ const done = makeDoneCb(resolve4, reject, stackHolder);
27446
27508
  corePoolCluster.query(sql, args, done);
27447
27509
  });
27448
27510
  }
@@ -27456,8 +27518,8 @@ var require_promise = __commonJS({
27456
27518
  "Callback function is not available with promise clients."
27457
27519
  );
27458
27520
  }
27459
- return new this.Promise((resolve3, reject) => {
27460
- const done = makeDoneCb(resolve3, reject, stackHolder);
27521
+ return new this.Promise((resolve4, reject) => {
27522
+ const done = makeDoneCb(resolve4, reject, stackHolder);
27461
27523
  corePoolCluster.execute(sql, args, done);
27462
27524
  });
27463
27525
  }
@@ -27470,13 +27532,13 @@ var require_promise = __commonJS({
27470
27532
  end() {
27471
27533
  const corePoolCluster = this.poolCluster;
27472
27534
  const stackHolder = captureStackHolder(_PromisePoolCluster.prototype.end);
27473
- return new this.Promise((resolve3, reject) => {
27535
+ return new this.Promise((resolve4, reject) => {
27474
27536
  corePoolCluster.end((err) => {
27475
27537
  if (err) {
27476
27538
  applyCapturedStack(err, stackHolder);
27477
27539
  reject(err);
27478
27540
  } else {
27479
- resolve3();
27541
+ resolve4();
27480
27542
  }
27481
27543
  });
27482
27544
  });
@@ -30551,7 +30613,7 @@ var require_dist = __commonJS({
30551
30613
  function parse(stream, callback) {
30552
30614
  const parser = new parser_1.Parser();
30553
30615
  stream.on("data", (buffer) => parser.parse(buffer, callback));
30554
- return new Promise((resolve3) => stream.on("end", () => resolve3()));
30616
+ return new Promise((resolve4) => stream.on("end", () => resolve4()));
30555
30617
  }
30556
30618
  exports.parse = parse;
30557
30619
  }
@@ -31276,12 +31338,12 @@ var require_client2 = __commonJS({
31276
31338
  this._connect(callback);
31277
31339
  return;
31278
31340
  }
31279
- return new this._Promise((resolve3, reject) => {
31341
+ return new this._Promise((resolve4, reject) => {
31280
31342
  this._connect((error) => {
31281
31343
  if (error) {
31282
31344
  reject(error);
31283
31345
  } else {
31284
- resolve3(this);
31346
+ resolve4(this);
31285
31347
  }
31286
31348
  });
31287
31349
  });
@@ -31627,8 +31689,8 @@ var require_client2 = __commonJS({
31627
31689
  readTimeout = config.query_timeout || this.connectionParameters.query_timeout;
31628
31690
  query = new Query2(config, values, callback);
31629
31691
  if (!query.callback) {
31630
- result = new this._Promise((resolve3, reject) => {
31631
- query.callback = (err, res) => err ? reject(err) : resolve3(res);
31692
+ result = new this._Promise((resolve4, reject) => {
31693
+ query.callback = (err, res) => err ? reject(err) : resolve4(res);
31632
31694
  }).catch((err) => {
31633
31695
  Error.captureStackTrace(err);
31634
31696
  throw err;
@@ -31705,8 +31767,8 @@ var require_client2 = __commonJS({
31705
31767
  if (cb) {
31706
31768
  this.connection.once("end", cb);
31707
31769
  } else {
31708
- return new this._Promise((resolve3) => {
31709
- this.connection.once("end", resolve3);
31770
+ return new this._Promise((resolve4) => {
31771
+ this.connection.once("end", resolve4);
31710
31772
  });
31711
31773
  }
31712
31774
  }
@@ -31754,8 +31816,8 @@ var require_pg_pool = __commonJS({
31754
31816
  const cb = function(err, client) {
31755
31817
  err ? rej(err) : res(client);
31756
31818
  };
31757
- const result = new Promise2(function(resolve3, reject) {
31758
- res = resolve3;
31819
+ const result = new Promise2(function(resolve4, reject) {
31820
+ res = resolve4;
31759
31821
  rej = reject;
31760
31822
  }).catch((err) => {
31761
31823
  Error.captureStackTrace(err);
@@ -31816,7 +31878,7 @@ var require_pg_pool = __commonJS({
31816
31878
  if (typeof Promise2.try === "function") {
31817
31879
  return Promise2.try(f);
31818
31880
  }
31819
- return new Promise2((resolve3) => resolve3(f()));
31881
+ return new Promise2((resolve4) => resolve4(f()));
31820
31882
  }
31821
31883
  _isFull() {
31822
31884
  return this._clients.length >= this.options.max;
@@ -32208,8 +32270,8 @@ var require_query4 = __commonJS({
32208
32270
  NativeQuery.prototype._getPromise = function() {
32209
32271
  if (this._promise) return this._promise;
32210
32272
  this._promise = new Promise(
32211
- function(resolve3, reject) {
32212
- this._once("end", resolve3);
32273
+ function(resolve4, reject) {
32274
+ this._once("end", resolve4);
32213
32275
  this._once("error", reject);
32214
32276
  }.bind(this)
32215
32277
  );
@@ -32386,12 +32448,12 @@ var require_client3 = __commonJS({
32386
32448
  this._connect(callback);
32387
32449
  return;
32388
32450
  }
32389
- return new this._Promise((resolve3, reject) => {
32451
+ return new this._Promise((resolve4, reject) => {
32390
32452
  this._connect((error) => {
32391
32453
  if (error) {
32392
32454
  reject(error);
32393
32455
  } else {
32394
- resolve3(this);
32456
+ resolve4(this);
32395
32457
  }
32396
32458
  });
32397
32459
  });
@@ -32415,8 +32477,8 @@ var require_client3 = __commonJS({
32415
32477
  query = new NativeQuery(config, values, callback);
32416
32478
  if (!query.callback) {
32417
32479
  let resolveOut, rejectOut;
32418
- result = new this._Promise((resolve3, reject) => {
32419
- resolveOut = resolve3;
32480
+ result = new this._Promise((resolve4, reject) => {
32481
+ resolveOut = resolve4;
32420
32482
  rejectOut = reject;
32421
32483
  }).catch((err) => {
32422
32484
  Error.captureStackTrace(err);
@@ -32476,8 +32538,8 @@ var require_client3 = __commonJS({
32476
32538
  }
32477
32539
  let result;
32478
32540
  if (!cb) {
32479
- result = new this._Promise(function(resolve3, reject) {
32480
- cb = (err) => err ? reject(err) : resolve3();
32541
+ result = new this._Promise(function(resolve4, reject) {
32542
+ cb = (err) => err ? reject(err) : resolve4();
32481
32543
  });
32482
32544
  }
32483
32545
  this.native.end(function() {
@@ -36400,8 +36462,8 @@ var require_common = __commonJS({
36400
36462
  }
36401
36463
  return debug2;
36402
36464
  }
36403
- function extend(namespace, delimiter) {
36404
- const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
36465
+ function extend(namespace, delimiter2) {
36466
+ const newDebug = createDebug(this.namespace + (typeof delimiter2 === "undefined" ? ":" : delimiter2) + namespace);
36405
36467
  newDebug.log = this.log;
36406
36468
  return newDebug;
36407
36469
  }
@@ -37584,7 +37646,7 @@ var require_Command = __commonJS({
37584
37646
  }
37585
37647
  }
37586
37648
  initPromise() {
37587
- const promise = new Promise((resolve3, reject) => {
37649
+ const promise = new Promise((resolve4, reject) => {
37588
37650
  if (!this.transformed) {
37589
37651
  this.transformed = true;
37590
37652
  const transformer = _Command._transformer.argument[this.name];
@@ -37593,7 +37655,7 @@ var require_Command = __commonJS({
37593
37655
  }
37594
37656
  this.stringifyArguments();
37595
37657
  }
37596
- this.resolve = this._convertValue(resolve3);
37658
+ this.resolve = this._convertValue(resolve4);
37597
37659
  this.reject = (err) => {
37598
37660
  this._clearTimers();
37599
37661
  if (this.errorStack) {
@@ -37626,11 +37688,11 @@ var require_Command = __commonJS({
37626
37688
  /**
37627
37689
  * Convert the value from buffer to the target encoding.
37628
37690
  */
37629
- _convertValue(resolve3) {
37691
+ _convertValue(resolve4) {
37630
37692
  return (value) => {
37631
37693
  try {
37632
37694
  this._clearTimers();
37633
- resolve3(this.transformReply(value));
37695
+ resolve4(this.transformReply(value));
37634
37696
  this.isResolved = true;
37635
37697
  } catch (err) {
37636
37698
  this.reject(err);
@@ -37908,13 +37970,13 @@ var require_autoPipelining = __commonJS({
37908
37970
  if (client.isCluster && !client.slots.length) {
37909
37971
  if (client.status === "wait")
37910
37972
  client.connect().catch(lodash_1.noop);
37911
- return (0, standard_as_callback_1.default)(new Promise(function(resolve3, reject) {
37973
+ return (0, standard_as_callback_1.default)(new Promise(function(resolve4, reject) {
37912
37974
  client.delayUntilReady((err) => {
37913
37975
  if (err) {
37914
37976
  reject(err);
37915
37977
  return;
37916
37978
  }
37917
- executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve3, reject);
37979
+ executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve4, reject);
37918
37980
  });
37919
37981
  }), callback);
37920
37982
  }
@@ -37935,13 +37997,13 @@ var require_autoPipelining = __commonJS({
37935
37997
  pipeline[exports.kExec] = true;
37936
37998
  setImmediate(executeAutoPipeline, client, slotKey);
37937
37999
  }
37938
- const autoPipelinePromise = new Promise(function(resolve3, reject) {
38000
+ const autoPipelinePromise = new Promise(function(resolve4, reject) {
37939
38001
  pipeline[exports.kCallbacks].push(function(err, value) {
37940
38002
  if (err) {
37941
38003
  reject(err);
37942
38004
  return;
37943
38005
  }
37944
- resolve3(value);
38006
+ resolve4(value);
37945
38007
  });
37946
38008
  if (functionName === "call") {
37947
38009
  args.unshift(commandName);
@@ -38176,8 +38238,8 @@ var require_Pipeline = __commonJS({
38176
38238
  this[name] = redis[name];
38177
38239
  this[name + "Buffer"] = redis[name + "Buffer"];
38178
38240
  });
38179
- this.promise = new Promise((resolve3, reject) => {
38180
- this.resolve = resolve3;
38241
+ this.promise = new Promise((resolve4, reject) => {
38242
+ this.resolve = resolve4;
38181
38243
  this.reject = reject;
38182
38244
  });
38183
38245
  const _this = this;
@@ -38477,13 +38539,13 @@ var require_transaction = __commonJS({
38477
38539
  if (this.isCluster && !this.redis.slots.length) {
38478
38540
  if (this.redis.status === "wait")
38479
38541
  this.redis.connect().catch(utils_1.noop);
38480
- return (0, standard_as_callback_1.default)(new Promise((resolve3, reject) => {
38542
+ return (0, standard_as_callback_1.default)(new Promise((resolve4, reject) => {
38481
38543
  this.redis.delayUntilReady((err) => {
38482
38544
  if (err) {
38483
38545
  reject(err);
38484
38546
  return;
38485
38547
  }
38486
- this.exec(pipeline).then(resolve3, reject);
38548
+ this.exec(pipeline).then(resolve4, reject);
38487
38549
  });
38488
38550
  }), callback);
38489
38551
  }
@@ -39612,7 +39674,7 @@ var require_cluster = __commonJS({
39612
39674
  * Connect to a cluster
39613
39675
  */
39614
39676
  connect() {
39615
- return new Promise((resolve3, reject) => {
39677
+ return new Promise((resolve4, reject) => {
39616
39678
  if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
39617
39679
  reject(new Error("Redis is already connecting/connected"));
39618
39680
  return;
@@ -39641,7 +39703,7 @@ var require_cluster = __commonJS({
39641
39703
  this.retryAttempts = 0;
39642
39704
  this.executeOfflineCommands();
39643
39705
  this.resetNodesRefreshInterval();
39644
- resolve3();
39706
+ resolve4();
39645
39707
  };
39646
39708
  let closeListener = void 0;
39647
39709
  const refreshListener = () => {
@@ -40251,7 +40313,7 @@ var require_cluster = __commonJS({
40251
40313
  });
40252
40314
  }
40253
40315
  resolveSrv(hostname2) {
40254
- return new Promise((resolve3, reject) => {
40316
+ return new Promise((resolve4, reject) => {
40255
40317
  this.options.resolveSrv(hostname2, (err, records) => {
40256
40318
  if (err) {
40257
40319
  return reject(err);
@@ -40265,7 +40327,7 @@ var require_cluster = __commonJS({
40265
40327
  if (!group.records.length) {
40266
40328
  sortedKeys.shift();
40267
40329
  }
40268
- self2.dnsLookup(record.name).then((host) => resolve3({
40330
+ self2.dnsLookup(record.name).then((host) => resolve4({
40269
40331
  host,
40270
40332
  port: record.port
40271
40333
  }), tryFirstOne);
@@ -40275,14 +40337,14 @@ var require_cluster = __commonJS({
40275
40337
  });
40276
40338
  }
40277
40339
  dnsLookup(hostname2) {
40278
- return new Promise((resolve3, reject) => {
40340
+ return new Promise((resolve4, reject) => {
40279
40341
  this.options.dnsLookup(hostname2, (err, address) => {
40280
40342
  if (err) {
40281
40343
  debug2("failed to resolve hostname %s to IP: %s", hostname2, err.message);
40282
40344
  reject(err);
40283
40345
  } else {
40284
40346
  debug2("resolved hostname %s to IP %s", hostname2, address);
40285
- resolve3(address);
40347
+ resolve4(address);
40286
40348
  }
40287
40349
  });
40288
40350
  });
@@ -40437,7 +40499,7 @@ var require_StandaloneConnector = __commonJS({
40437
40499
  if (options.tls) {
40438
40500
  Object.assign(connectionOptions, options.tls);
40439
40501
  }
40440
- return new Promise((resolve3, reject) => {
40502
+ return new Promise((resolve4, reject) => {
40441
40503
  process.nextTick(() => {
40442
40504
  if (!this.connecting) {
40443
40505
  reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
@@ -40456,7 +40518,7 @@ var require_StandaloneConnector = __commonJS({
40456
40518
  this.stream.once("error", (err) => {
40457
40519
  this.firstError = err;
40458
40520
  });
40459
- resolve3(this.stream);
40521
+ resolve4(this.stream);
40460
40522
  });
40461
40523
  });
40462
40524
  }
@@ -40612,7 +40674,7 @@ var require_SentinelConnector = __commonJS({
40612
40674
  const error = new Error(errorMsg);
40613
40675
  if (typeof retryDelay === "number") {
40614
40676
  eventEmitter("error", error);
40615
- await new Promise((resolve3) => setTimeout(resolve3, retryDelay));
40677
+ await new Promise((resolve4) => setTimeout(resolve4, retryDelay));
40616
40678
  return connectToNext();
40617
40679
  } else {
40618
40680
  throw error;
@@ -41929,7 +41991,7 @@ var require_Redis = __commonJS({
41929
41991
  * if the connection fails, times out, or if Redis is already connecting/connected.
41930
41992
  */
41931
41993
  connect(callback) {
41932
- const promise = new Promise((resolve3, reject) => {
41994
+ const promise = new Promise((resolve4, reject) => {
41933
41995
  if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
41934
41996
  reject(new Error("Redis is already connecting/connected"));
41935
41997
  return;
@@ -42008,7 +42070,7 @@ var require_Redis = __commonJS({
42008
42070
  }
42009
42071
  const connectionReadyHandler = function() {
42010
42072
  _this.removeListener("close", connectionCloseHandler);
42011
- resolve3();
42073
+ resolve4();
42012
42074
  };
42013
42075
  var connectionCloseHandler = function() {
42014
42076
  _this.removeListener("ready", connectionReadyHandler);
@@ -42102,10 +42164,10 @@ var require_Redis = __commonJS({
42102
42164
  monitor: true,
42103
42165
  lazyConnect: false
42104
42166
  });
42105
- return (0, standard_as_callback_1.default)(new Promise(function(resolve3, reject) {
42167
+ return (0, standard_as_callback_1.default)(new Promise(function(resolve4, reject) {
42106
42168
  monitorInstance.once("error", reject);
42107
42169
  monitorInstance.once("monitoring", function() {
42108
- resolve3(monitorInstance);
42170
+ resolve4(monitorInstance);
42109
42171
  });
42110
42172
  }), callback);
42111
42173
  }
@@ -42878,7 +42940,15 @@ async function runMenu() {
42878
42940
  });
42879
42941
  if (!q(setup) && setup) {
42880
42942
  const { runInitFlow: runInitFlow2 } = await Promise.resolve().then(() => (init_init(), init_exports));
42881
- await runInitFlow2(client, { cwd: process.cwd(), dryRun: false, noScripts: false });
42943
+ const outcome = await runInitFlow2(client, {
42944
+ cwd: process.cwd(),
42945
+ dryRun: false,
42946
+ noScripts: false
42947
+ });
42948
+ if (outcome.exitCode === 0) {
42949
+ ye("All set. Run keynv again anytime to manage secrets.");
42950
+ return 0;
42951
+ }
42882
42952
  }
42883
42953
  }
42884
42954
  }
@@ -42948,8 +43018,8 @@ var init_menu = __esm({
42948
43018
  "src/ui/menu.ts"() {
42949
43019
  init_dist5();
42950
43020
  init_http();
42951
- init_detect();
42952
43021
  init_store();
43022
+ init_detect();
42953
43023
  init_version();
42954
43024
  init_audit2();
42955
43025
  init_login();
@@ -44749,8 +44819,6 @@ var AuditVerifyCommand = class extends Command {
44749
44819
  }
44750
44820
  }
44751
44821
  };
44752
-
44753
- // src/commands/exec.ts
44754
44822
  init_http();
44755
44823
  init_envFile();
44756
44824
 
@@ -45099,7 +45167,9 @@ var ENV_ALLOWLIST = [
45099
45167
  "WINDIR",
45100
45168
  "COMSPEC",
45101
45169
  "APPDATA",
45102
- "LOCALAPPDATA"
45170
+ "LOCALAPPDATA",
45171
+ "PATHEXT"
45172
+ // needed so child processes can resolve executables by name
45103
45173
  ];
45104
45174
  function spawnPrivileged(opts) {
45105
45175
  const startedAt = Date.now();
@@ -45108,6 +45178,11 @@ function spawnPrivileged(opts) {
45108
45178
  const v2 = process.env[name];
45109
45179
  if (v2 !== void 0) env2[name] = v2;
45110
45180
  }
45181
+ const nmBin = findNodeModulesBin(process.cwd());
45182
+ if (nmBin) {
45183
+ const existingPath = env2.PATH ?? "";
45184
+ env2.PATH = existingPath.startsWith(nmBin + nodePath.delimiter) ? existingPath : nmBin + nodePath.delimiter + existingPath;
45185
+ }
45111
45186
  if (opts.injectedEnv) {
45112
45187
  for (const [k2, v2] of Object.entries(opts.injectedEnv)) env2[k2] = v2;
45113
45188
  }
@@ -45133,14 +45208,33 @@ function spawnPrivileged(opts) {
45133
45208
  ]);
45134
45209
  let spawnCmd = opts.command;
45135
45210
  let spawnArgs = opts.args;
45136
- if (process.platform === "win32" && WIN_BUILTINS.has(opts.command.toLowerCase())) {
45137
- spawnCmd = process.env.COMSPEC ?? "cmd.exe";
45138
- spawnArgs = ["/d", "/s", "/c", opts.command, ...opts.args];
45211
+ let windowsVerbatimArguments = false;
45212
+ if (process.platform === "win32") {
45213
+ const comspec = env2.COMSPEC ?? process.env.COMSPEC ?? "cmd.exe";
45214
+ if (WIN_BUILTINS.has(opts.command.toLowerCase())) {
45215
+ spawnCmd = comspec;
45216
+ spawnArgs = ["/d", "/s", "/c", opts.command, ...opts.args];
45217
+ } else {
45218
+ const resolved = resolveWindowsCmd(opts.command, env2);
45219
+ if (resolved !== null) {
45220
+ const ext = nodePath.extname(resolved).toLowerCase();
45221
+ if (ext === ".cmd" || ext === ".bat") {
45222
+ const q2 = (s) => s.includes(" ") ? `"${s}"` : s;
45223
+ const inner = [q2(resolved), ...opts.args.map(q2)].join(" ");
45224
+ spawnCmd = comspec;
45225
+ spawnArgs = ["/d", "/s", "/c", `"${inner}"`];
45226
+ windowsVerbatimArguments = true;
45227
+ } else {
45228
+ spawnCmd = resolved;
45229
+ }
45230
+ }
45231
+ }
45139
45232
  }
45140
45233
  const child = spawn(spawnCmd, spawnArgs, {
45141
45234
  env: env2,
45142
45235
  stdio,
45143
- detached: false
45236
+ detached: false,
45237
+ windowsVerbatimArguments
45144
45238
  });
45145
45239
  const literals = opts.resolved.map((r) => r.value).filter((v2) => v2.length > 0);
45146
45240
  if (!opts.noRedact && child.stdout) {
@@ -45161,14 +45255,14 @@ function spawnPrivileged(opts) {
45161
45255
  }, opts.timeoutS * 1e3);
45162
45256
  timer.unref();
45163
45257
  }
45164
- return new Promise((resolve3, reject) => {
45258
+ return new Promise((resolve4, reject) => {
45165
45259
  child.on("error", (err) => {
45166
45260
  if (timer) clearTimeout(timer);
45167
45261
  reject(err);
45168
45262
  });
45169
45263
  child.on("close", (code, signal) => {
45170
45264
  if (timer) clearTimeout(timer);
45171
- resolve3({
45265
+ resolve4({
45172
45266
  exitCode: code ?? 0,
45173
45267
  signal,
45174
45268
  durationMs: Date.now() - startedAt
@@ -45176,6 +45270,34 @@ function spawnPrivileged(opts) {
45176
45270
  });
45177
45271
  });
45178
45272
  }
45273
+ function resolveWindowsCmd(command, subprocessEnv) {
45274
+ if (nodePath.extname(command)) return null;
45275
+ if (command.includes("/") || command.includes("\\")) return null;
45276
+ const pathExt = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e2) => e2.toLowerCase()).filter(Boolean);
45277
+ const pathStr = subprocessEnv.PATH ?? process.env.PATH ?? "";
45278
+ const pathDirs = pathStr.split(nodePath.delimiter).filter(Boolean);
45279
+ for (const dir of pathDirs) {
45280
+ for (const ext of pathExt) {
45281
+ try {
45282
+ const full = nodePath.join(dir, command + ext);
45283
+ if (existsSync(full)) return full;
45284
+ } catch {
45285
+ }
45286
+ }
45287
+ }
45288
+ return null;
45289
+ }
45290
+ function findNodeModulesBin(cwd) {
45291
+ let dir = nodePath.resolve(cwd);
45292
+ for (let i = 0; i < 20; i++) {
45293
+ const candidate = nodePath.join(dir, "node_modules", ".bin");
45294
+ if (existsSync(candidate)) return candidate;
45295
+ const parent = nodePath.dirname(dir);
45296
+ if (parent === dir) break;
45297
+ dir = parent;
45298
+ }
45299
+ return null;
45300
+ }
45179
45301
 
45180
45302
  // src/commands/exec.ts
45181
45303
  var ExecCommand = class extends Command {
@@ -45232,6 +45354,10 @@ spelled \`--from\` to avoid the collision.)
45232
45354
  quiet = options_exports.Boolean("--quiet", false, {
45233
45355
  description: `Suppress the "loaded N vars from ${ENV_FILE_BASENAME}" status line.`
45234
45356
  });
45357
+ // Option.Rest: keynv options (e.g. --no-env-file) are parsed before `--`.
45358
+ // Everything after `--` is captured in rest verbatim, including subprocess
45359
+ // flags like `--port 3005` and `-v`. Always use `--` to separate keynv
45360
+ // options from the subprocess command.
45235
45361
  rest = options_exports.Rest();
45236
45362
  async execute() {
45237
45363
  if (!this.rest || this.rest.length === 0) {
@@ -45274,6 +45400,13 @@ spelled \`--from\` to avoid the collision.)
45274
45400
  );
45275
45401
  return 2;
45276
45402
  }
45403
+ if (!envFileLoaded && !this.noEnvFile && !this.envFile && !process.env.KEYNV_ENV_FILE) {
45404
+ this.context.stderr.write(
45405
+ `keynv: no ${ENV_FILE_BASENAME} found in this directory or any parent.
45406
+ Run \`keynv init\` in your project root to migrate secrets and create one.
45407
+ `
45408
+ );
45409
+ }
45277
45410
  const viaEnvSpecs = [];
45278
45411
  for (const spec of this.viaEnv ?? []) {
45279
45412
  const eq = spec.indexOf("=");
@@ -45398,7 +45531,9 @@ function signalNumber(sig) {
45398
45531
  init_dist();
45399
45532
  init_http();
45400
45533
  init_envFile();
45534
+ init_detect();
45401
45535
  init_heuristics();
45536
+ init_aiContext();
45402
45537
  init_init();
45403
45538
  init_cancel();
45404
45539
  init_tty();
@@ -45414,7 +45549,8 @@ async function resolveProjectId(client, input) {
45414
45549
  return input;
45415
45550
  }
45416
45551
  const data = await client.request("/v1/projects");
45417
- const match = data.projects.find((p2) => p2.name === input);
45552
+ const lowerInput = input.toLowerCase();
45553
+ const match = data.projects.find((p2) => p2.name.toLowerCase() === lowerInput);
45418
45554
  if (!match) throw new Error(`project not found: ${input}`);
45419
45555
  return match.id;
45420
45556
  }
@@ -45547,6 +45683,14 @@ var ProjectDeleteCommand = class extends Command {
45547
45683
  };
45548
45684
 
45549
45685
  // src/commands/init.ts
45686
+ function toAliasKey2(name) {
45687
+ if (!name) return name;
45688
+ const KEY_RE3 = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
45689
+ if (KEY_RE3.test(name)) return name;
45690
+ const normalised = name.toLowerCase().replace(/_/g, "-");
45691
+ if (KEY_RE3.test(normalised)) return normalised;
45692
+ return normalised.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64) || "key";
45693
+ }
45550
45694
  var InitCommand = class extends Command {
45551
45695
  static paths = [["init"]];
45552
45696
  static usage = Command.Usage({
@@ -45565,15 +45709,19 @@ mappings that would be written to .keynv.env, then exits without
45565
45709
  touching any files or making any network calls. Use it to preview
45566
45710
  what init will do before committing.
45567
45711
 
45568
- Requires an interactive terminal (clack TUI). For scripted
45569
- migration, use the lower-level \`keynv project\` and \`keynv secret\`
45570
- commands directly.
45712
+ For scripted migration, use \`--env-file\` or \`--secret\` flags, or
45713
+ \`--yes\` to auto-scan the project directory and set up without
45714
+ any prompts.
45571
45715
  `,
45572
45716
  examples: [
45573
45717
  ["Walk the current project", "$0 init"],
45574
45718
  ["Preview without writing or uploading", "$0 init --dry-run"],
45575
45719
  ["Skip the package.json script-wrapping step", "$0 init --no-scripts"],
45576
- ["Non-interactive (CI/CD)", "$0 init --env-file .env --project myproject --env dev"]
45720
+ ["Non-interactive (CI/CD)", "$0 init --env-file .env --project myproject --env dev"],
45721
+ [
45722
+ "Auto-scan & set up without prompts (CI/CD)",
45723
+ "$0 init --yes"
45724
+ ]
45577
45725
  ]
45578
45726
  });
45579
45727
  dryRun = options_exports.Boolean("--dry-run", false, {
@@ -45594,6 +45742,9 @@ commands directly.
45594
45742
  secret = options_exports.Array("--secret", {
45595
45743
  description: "KEY=value secret to upload (non-interactive). Can be specified multiple times."
45596
45744
  });
45745
+ yes = options_exports.Boolean("--yes", false, {
45746
+ description: "Auto-scan .env files, classify, and set up without prompts. Implies non-interactive mode."
45747
+ });
45597
45748
  async execute() {
45598
45749
  const client = new ApiClient();
45599
45750
  await client.ensureHydrated();
@@ -45603,13 +45754,13 @@ commands directly.
45603
45754
  }
45604
45755
  const hasEnvFile = this.envFile != null;
45605
45756
  const hasSecrets = this.secret != null && this.secret.length > 0;
45606
- const isNonInteractive = hasEnvFile || hasSecrets;
45757
+ const isNonInteractive = hasEnvFile || hasSecrets || this.dryRun || this.yes;
45607
45758
  if (isNonInteractive) {
45608
45759
  return this.runNonInteractive(client);
45609
45760
  }
45610
45761
  if (!isInteractive()) {
45611
45762
  this.context.stderr.write(
45612
- "keynv init requires an interactive terminal. Use --env-file or --secret for scripted setup.\n"
45763
+ "keynv init requires an interactive terminal. Use --dry-run, --env-file, or --secret for scripted setup.\n"
45613
45764
  );
45614
45765
  return 1;
45615
45766
  }
@@ -45629,6 +45780,9 @@ commands directly.
45629
45780
  }
45630
45781
  }
45631
45782
  async runNonInteractive(client) {
45783
+ if (this.yes && !this.envFile && !this.secret) {
45784
+ return this.runAutoScan(client);
45785
+ }
45632
45786
  const projectName = this.project;
45633
45787
  if (!projectName) {
45634
45788
  this.context.stderr.write("keynv: --project is required in non-interactive mode.\n");
@@ -45678,34 +45832,134 @@ commands directly.
45678
45832
  return 0;
45679
45833
  }
45680
45834
  if (this.dryRun) {
45681
- this.context.stdout.write(
45682
- `keynv: dry-run \u2014 would upload ${secrets.length} secret(s) to project ${projectName} (${projectId2}) in env ${envName}
45835
+ if (this.envFile || this.secret && this.secret.length > 0) {
45836
+ this.context.stdout.write(
45837
+ `keynv: dry-run \u2014 would upload ${secrets.length} secret(s) to project ${projectName} (${projectId2}) in env ${envName}
45683
45838
  `
45839
+ );
45840
+ for (const { name } of secrets) {
45841
+ const aliasKey = toAliasKey2(name);
45842
+ this.context.stdout.write(` ${name}=@${projectName}.${envName}.${aliasKey}
45843
+ `);
45844
+ }
45845
+ return 0;
45846
+ }
45847
+ this.context.stdout.write(
45848
+ "keynv: dry-run mode \u2014 no --env-file or --secret provided. Nothing to scan.\n"
45684
45849
  );
45850
+ return 0;
45851
+ }
45852
+ const secretsWithKeys = secrets.map((s) => ({ ...s, aliasKey: toAliasKey2(s.name) }));
45853
+ return this.uploadSecrets(client, projectId2, projectName, envName, secretsWithKeys);
45854
+ }
45855
+ /**
45856
+ * Auto-scan mode (--yes without explicit --env-file). Finds .env files
45857
+ * in the project root, classifies entries, creates a project, uploads
45858
+ * secrets, and writes .keynv.env — all without prompts.
45859
+ */
45860
+ async runAutoScan(client) {
45861
+ const root = findProjectRoot(process.cwd());
45862
+ if (!root) {
45863
+ this.context.stderr.write(
45864
+ "keynv: no project root found (no package.json, .git, etc.).\n"
45865
+ );
45866
+ return 1;
45867
+ }
45868
+ const envFiles = findEnvFiles(root.path);
45869
+ if (envFiles.length === 0) {
45870
+ this.context.stdout.write("keynv: no .env files found. Nothing to migrate.\n");
45871
+ return 0;
45872
+ }
45873
+ const projectName = this.project ?? root.suggestedName;
45874
+ const envName = this.env ?? suggestedEnvForSuffix(envFiles[0]?.suffix ?? null);
45875
+ const secrets = [];
45876
+ for (const f of envFiles) {
45877
+ try {
45878
+ const entries = parseEnvFile(readFileSync(f.path, "utf8"), f.path);
45879
+ for (const e2 of entries) {
45880
+ if (classifyEntry(e2.name, e2.value).verdict === "secret") {
45881
+ secrets.push({ name: e2.name, value: e2.value });
45882
+ }
45883
+ }
45884
+ } catch {
45885
+ this.context.stderr.write(`keynv: warning \u2014 could not parse ${f.name}, skipping.
45886
+ `);
45887
+ }
45888
+ }
45889
+ if (secrets.length === 0) {
45890
+ this.context.stdout.write("keynv: no secrets detected in .env files. Nothing to upload.\n");
45891
+ return 0;
45892
+ }
45893
+ this.context.stdout.write(
45894
+ `keynv: auto-scan found ${secrets.length} secret(s) across ${envFiles.length} file(s).
45895
+ `
45896
+ );
45897
+ if (this.dryRun) {
45685
45898
  for (const { name } of secrets) {
45686
- const aliasKey = name.toLowerCase().replace(/_/g, "-");
45899
+ const aliasKey = toAliasKey2(name);
45687
45900
  this.context.stdout.write(` ${name}=@${projectName}.${envName}.${aliasKey}
45688
45901
  `);
45689
45902
  }
45690
45903
  return 0;
45691
45904
  }
45905
+ let projectId2;
45906
+ try {
45907
+ projectId2 = await resolveProjectId(client, projectName);
45908
+ } catch {
45909
+ const created = await client.request("/v1/projects", {
45910
+ method: "POST",
45911
+ body: { name: projectName, environments: [{ name: envName, tier: "non-production", require_approval: false }] }
45912
+ });
45913
+ projectId2 = created.id;
45914
+ this.context.stdout.write(`keynv: created project "${projectName}" (${projectId2}).
45915
+ `);
45916
+ }
45917
+ const secretsWithKeys = secrets.map((s) => ({ ...s, aliasKey: toAliasKey2(s.name) }));
45918
+ const result = await this.uploadSecrets(client, projectId2, projectName, envName, secretsWithKeys);
45919
+ if (result !== 0) return result;
45920
+ const aliasLines = secretsWithKeys.map((s) => `# ${s.name}
45921
+ ${s.name}=@${projectName}.${envName}.${s.aliasKey}`).join("\n");
45922
+ const keynvEnvPath = join(root.path, ".keynv.env");
45923
+ writeFileSync(keynvEnvPath, `# Auto-generated by keynv init --yes
45924
+ ${aliasLines}
45925
+ `);
45926
+ this.context.stdout.write(`keynv: wrote ${keynvEnvPath}
45927
+ `);
45928
+ try {
45929
+ const outcome = writeAiContext(root.path);
45930
+ if (outcome === "created") this.context.stdout.write("keynv: wrote AGENTS.md\n");
45931
+ } catch {
45932
+ this.context.stdout.write("keynv: warning \u2014 could not write AGENTS.md.\n");
45933
+ }
45934
+ for (const f of envFiles) {
45935
+ unlinkSync(f.path);
45936
+ this.context.stdout.write(`keynv: removed ${f.name} (secrets migrated to vault).
45937
+ `);
45938
+ }
45939
+ this.context.stdout.write("keynv: done. Use `keynv exec` to run commands with resolved secrets.\n");
45940
+ return 0;
45941
+ }
45942
+ /**
45943
+ * Upload secrets to the vault and print the result. Shared between
45944
+ * runNonInteractive and runAutoScan.
45945
+ */
45946
+ async uploadSecrets(client, projectId2, projectName, envName, secrets) {
45692
45947
  let uploaded = 0;
45693
45948
  const failed = [];
45694
- for (const { name, value } of secrets) {
45695
- const aliasKey = name.toLowerCase().replace(/_/g, "-");
45696
- const alias2 = buildAlias({ project: projectName, environment: envName, key: aliasKey });
45949
+ for (const s of secrets) {
45950
+ const alias2 = buildAlias({ project: projectName, environment: envName, key: s.aliasKey });
45697
45951
  if (!alias2) {
45698
- failed.push(`${name} (invalid alias key: ${aliasKey})`);
45952
+ failed.push({ name: s.name, reason: `invalid alias key: ${s.aliasKey}` });
45699
45953
  continue;
45700
45954
  }
45701
45955
  try {
45702
45956
  await client.request(`/v1/projects/${projectId2}/secrets`, {
45703
45957
  method: "POST",
45704
- body: { env: envName, key: aliasKey, value }
45958
+ body: { env: envName, key: s.aliasKey, value: s.value }
45705
45959
  });
45706
45960
  uploaded++;
45707
45961
  } catch (err) {
45708
- failed.push(`${name}: ${err instanceof Error ? err.message : String(err)}`);
45962
+ failed.push({ name: s.name, reason: err instanceof Error ? err.message : String(err) });
45709
45963
  }
45710
45964
  }
45711
45965
  this.context.stdout.write(
@@ -45713,7 +45967,7 @@ commands directly.
45713
45967
  `
45714
45968
  );
45715
45969
  if (failed.length > 0) {
45716
- for (const f of failed) this.context.stderr.write(` failed: ${f}
45970
+ for (const f of failed) this.context.stderr.write(` failed: ${f.name} \u2014 ${f.reason}
45717
45971
  `);
45718
45972
  return 1;
45719
45973
  }
@@ -45735,7 +45989,7 @@ async function promptHidden(prompt) {
45735
45989
  process.stdin.setRawMode(true);
45736
45990
  process.stdin.resume();
45737
45991
  process.stdin.setEncoding("utf8");
45738
- return new Promise((resolve3) => {
45992
+ return new Promise((resolve4) => {
45739
45993
  let buf = "";
45740
45994
  const onData = (chunk) => {
45741
45995
  for (const ch of chunk) {
@@ -45744,7 +45998,7 @@ async function promptHidden(prompt) {
45744
45998
  process.stdin.pause();
45745
45999
  process.stdin.removeListener("data", onData);
45746
46000
  process.stdout.write("\n");
45747
- resolve3(buf);
46001
+ resolve4(buf);
45748
46002
  return;
45749
46003
  }
45750
46004
  if (ch === "") {
@@ -45762,10 +46016,10 @@ async function promptHidden(prompt) {
45762
46016
  }
45763
46017
  async function promptLine(prompt) {
45764
46018
  const rl = createInterface({ input: process.stdin, output: process.stdout });
45765
- return new Promise((resolve3) => {
46019
+ return new Promise((resolve4) => {
45766
46020
  rl.question(prompt, (answer) => {
45767
46021
  rl.close();
45768
- resolve3(answer.trim());
46022
+ resolve4(answer.trim());
45769
46023
  });
45770
46024
  });
45771
46025
  }
@@ -46152,9 +46406,9 @@ here (see the streaming-mode limitation in the redactor package).
46152
46406
  `
46153
46407
  });
46154
46408
  async execute() {
46155
- return new Promise((resolve3, reject) => {
46409
+ return new Promise((resolve4, reject) => {
46156
46410
  const transform = createRedactStream();
46157
- this.context.stdin.pipe(transform).pipe(this.context.stdout).on("error", reject).on("finish", () => resolve3(0));
46411
+ this.context.stdin.pipe(transform).pipe(this.context.stdout).on("error", reject).on("finish", () => resolve4(0));
46158
46412
  this.context.stdin.on("error", reject);
46159
46413
  });
46160
46414
  }
@@ -46234,10 +46488,13 @@ var SecretCreateCommand = class extends Command {
46234
46488
  value = await promptHidden("value: ");
46235
46489
  }
46236
46490
  const projectId2 = await resolveProjectId(client, parsed.project);
46237
- await client.request(`/v1/projects/${projectId2}/secrets`, {
46238
- method: "POST",
46239
- body: { env: parsed.environment, key: parsed.key, value }
46240
- });
46491
+ await client.request(
46492
+ `/v1/projects/${projectId2}/secrets`,
46493
+ {
46494
+ method: "POST",
46495
+ body: { env: parsed.environment, key: parsed.key, value }
46496
+ }
46497
+ );
46241
46498
  this.context.stdout.write(`created ${parsed.literal}
46242
46499
  `);
46243
46500
  return 0;
@@ -46320,7 +46577,15 @@ var SecretListCommand = class extends Command {
46320
46577
  throw err;
46321
46578
  }
46322
46579
  }
46323
- const resolvedProjectName = projectName.startsWith("@") ? projectName.slice(1).split(".")[0] ?? projectName : projectName;
46580
+ let resolvedProjectName = projectName;
46581
+ if (projectName.startsWith("@")) {
46582
+ const parsed = parseAlias(projectName);
46583
+ if (parsed) {
46584
+ resolvedProjectName = parsed.project;
46585
+ } else {
46586
+ resolvedProjectName = projectName.slice(1).split(".")[0] ?? projectName;
46587
+ }
46588
+ }
46324
46589
  const projectId2 = await resolveProjectId(client, resolvedProjectName);
46325
46590
  const data = await client.request(`/v1/projects/${projectId2}/secrets`);
46326
46591
  if (this.json) {
@@ -46425,9 +46690,12 @@ var SecretDeleteCommand = class extends Command {
46425
46690
  return 1;
46426
46691
  }
46427
46692
  const projectId2 = await resolveProjectId(client, parsed.project);
46428
- await client.request(`/v1/projects/${projectId2}/secrets/${parsed.environment}/${parsed.key}`, {
46429
- method: "DELETE"
46430
- });
46693
+ await client.request(
46694
+ `/v1/projects/${projectId2}/secrets/${parsed.environment}/${parsed.key}`,
46695
+ {
46696
+ method: "DELETE"
46697
+ }
46698
+ );
46431
46699
  this.context.stdout.write(`deleted ${parsed.literal}
46432
46700
  `);
46433
46701
  return 0;
@@ -46495,8 +46763,8 @@ Non-interactive with --yes to skip prompts.
46495
46763
  "# KEYNV_ARGON2_MEMORY_KIB=46080",
46496
46764
  "# KEYNV_ARGON2_TIME_COST=3"
46497
46765
  ].join("\n");
46498
- const { writeFileSync: writeFileSync4 } = await import('fs');
46499
- writeFileSync4(outPath, `${contents}
46766
+ const { writeFileSync: writeFileSync5 } = await import('fs');
46767
+ writeFileSync5(outPath, `${contents}
46500
46768
  `);
46501
46769
  this.context.stdout.write(`Wrote ${outPath}
46502
46770
 
@@ -46562,7 +46830,7 @@ services:
46562
46830
  volumes:
46563
46831
  keynv-data:
46564
46832
  `;
46565
- writeFileSync4("docker-compose.yml", composeContent);
46833
+ writeFileSync5("docker-compose.yml", composeContent);
46566
46834
  this.context.stdout.write(
46567
46835
  "\nWrote docker-compose.yml (update Litestream S3 config before deploying).\n"
46568
46836
  );
@@ -46798,7 +47066,7 @@ var sshTester = {
46798
47066
  async test(secret, target) {
46799
47067
  const start = Date.now();
46800
47068
  const { Client: Client2 } = await import('ssh2');
46801
- return new Promise((resolve3) => {
47069
+ return new Promise((resolve4) => {
46802
47070
  const client = new Client2();
46803
47071
  let settled = false;
46804
47072
  const settle = (result) => {
@@ -46809,7 +47077,7 @@ var sshTester = {
46809
47077
  client.end();
46810
47078
  } catch {
46811
47079
  }
46812
- resolve3(result);
47080
+ resolve4(result);
46813
47081
  };
46814
47082
  client.once("ready", () => {
46815
47083
  client.exec("true", (err, stream) => {
@@ -46875,12 +47143,12 @@ function sanitizeResult(result, secret) {
46875
47143
 
46876
47144
  // ../../packages/testers/dist/run.js
46877
47145
  function withTimeout(promise, ms) {
46878
- return new Promise((resolve3, reject) => {
47146
+ return new Promise((resolve4, reject) => {
46879
47147
  const t = setTimeout(() => reject(new Error(`tester timed out after ${ms}ms`)), ms);
46880
47148
  t.unref();
46881
47149
  promise.then((v2) => {
46882
47150
  clearTimeout(t);
46883
- resolve3(v2);
47151
+ resolve4(v2);
46884
47152
  }, (err) => {
46885
47153
  clearTimeout(t);
46886
47154
  reject(err);