@keynv/cli 0.1.0-rc.8 → 0.1.0-rc.9
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 +101 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1056,7 +1056,7 @@ var require_lib = __commonJS({
|
|
|
1056
1056
|
var VERSION, AGENT;
|
|
1057
1057
|
var init_version = __esm({
|
|
1058
1058
|
"src/version.ts"() {
|
|
1059
|
-
VERSION = "0.1.0-rc.
|
|
1059
|
+
VERSION = "0.1.0-rc.9" ;
|
|
1060
1060
|
AGENT = `keynv-cli/${VERSION}`;
|
|
1061
1061
|
}
|
|
1062
1062
|
});
|
|
@@ -7128,6 +7128,95 @@ var init_detect = __esm({
|
|
|
7128
7128
|
KEYNV_ENV_BASENAME = ".keynv.env";
|
|
7129
7129
|
}
|
|
7130
7130
|
});
|
|
7131
|
+
function aiContextBody() {
|
|
7132
|
+
return `## keynv (secrets)
|
|
7133
|
+
|
|
7134
|
+
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.
|
|
7135
|
+
|
|
7136
|
+
### Mental model
|
|
7137
|
+
|
|
7138
|
+
- **\`.keynv.env\`** \u2014 checked into git, contains aliases. Safe to read, edit, commit.
|
|
7139
|
+
- **Vault** \u2014 holds the real values. The \`keynv\` CLI reads it on demand.
|
|
7140
|
+
- **\`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.
|
|
7141
|
+
- **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.
|
|
7142
|
+
|
|
7143
|
+
### What you should do
|
|
7144
|
+
|
|
7145
|
+
When the user asks about secrets or env vars, follow this decision tree:
|
|
7146
|
+
|
|
7147
|
+
| User intent | Run / suggest |
|
|
7148
|
+
|---|---|
|
|
7149
|
+
| "Add a new API key" / "set up a secret" | \`keynv secret create\` (interactive prompt picks project + env + key) |
|
|
7150
|
+
| "I have a \`.env\`, migrate it" | \`keynv init\` (you're seeing this file because that already happened) |
|
|
7151
|
+
| "Run the app / dev server / tests" | \`keynv exec -- <their existing command>\` (auto-loads \`.keynv.env\`) |
|
|
7152
|
+
| "Show me the value of X" | \`keynv secret get @alias\` \u2014 but copy to clipboard, do NOT print the value into chat / terminal |
|
|
7153
|
+
| "Rotate this key" | \`keynv secret rotate @alias\` |
|
|
7154
|
+
| "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\`) |
|
|
7155
|
+
| "Who has access?" | \`keynv member list <project>\` |
|
|
7156
|
+
|
|
7157
|
+
### Hard rules \u2014 do not violate
|
|
7158
|
+
|
|
7159
|
+
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.
|
|
7160
|
+
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.
|
|
7161
|
+
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.
|
|
7162
|
+
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.
|
|
7163
|
+
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.
|
|
7164
|
+
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.
|
|
7165
|
+
|
|
7166
|
+
### Quick reference
|
|
7167
|
+
|
|
7168
|
+
\`\`\`bash
|
|
7169
|
+
keynv # interactive TUI menu (pick projects, secrets, members)
|
|
7170
|
+
keynv exec -- <cmd> # run cmd with .keynv.env loaded
|
|
7171
|
+
keynv secret create # walk through creating a new secret
|
|
7172
|
+
keynv secret list <project> # list aliases (no values)
|
|
7173
|
+
keynv whoami # who am I logged in as
|
|
7174
|
+
keynv --help # full command list
|
|
7175
|
+
\`\`\`
|
|
7176
|
+
|
|
7177
|
+
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\`.`;
|
|
7178
|
+
}
|
|
7179
|
+
function renderKeynvBlock() {
|
|
7180
|
+
return `${KEYNV_BLOCK_START}
|
|
7181
|
+
${aiContextBody()}
|
|
7182
|
+
${KEYNV_BLOCK_END}`;
|
|
7183
|
+
}
|
|
7184
|
+
function writeAiContext(rootPath) {
|
|
7185
|
+
const path = join(rootPath, AGENTS_FILE_BASENAME);
|
|
7186
|
+
const block = renderKeynvBlock();
|
|
7187
|
+
if (!existsSync(path)) {
|
|
7188
|
+
const initial = `# Agent guidance for this project
|
|
7189
|
+
|
|
7190
|
+
${block}
|
|
7191
|
+
`;
|
|
7192
|
+
writeFileSync(path, initial);
|
|
7193
|
+
return "created";
|
|
7194
|
+
}
|
|
7195
|
+
const existing = readFileSync(path, "utf8");
|
|
7196
|
+
const startIdx = existing.indexOf(KEYNV_BLOCK_START);
|
|
7197
|
+
const endIdx = existing.indexOf(KEYNV_BLOCK_END);
|
|
7198
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
7199
|
+
const before = existing.slice(0, startIdx);
|
|
7200
|
+
const after = existing.slice(endIdx + KEYNV_BLOCK_END.length);
|
|
7201
|
+
const next = `${before}${block}${after}`;
|
|
7202
|
+
if (next === existing) return "unchanged";
|
|
7203
|
+
writeFileSync(path, next);
|
|
7204
|
+
return "updated";
|
|
7205
|
+
}
|
|
7206
|
+
const sep = existing.length === 0 ? "" : existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
|
|
7207
|
+
const trailingNewline = existing.endsWith("\n") ? "" : "\n";
|
|
7208
|
+
writeFileSync(path, `${existing}${trailingNewline}${sep}${block}
|
|
7209
|
+
`);
|
|
7210
|
+
return "appended";
|
|
7211
|
+
}
|
|
7212
|
+
var AGENTS_FILE_BASENAME, KEYNV_BLOCK_START, KEYNV_BLOCK_END;
|
|
7213
|
+
var init_aiContext = __esm({
|
|
7214
|
+
"src/init/aiContext.ts"() {
|
|
7215
|
+
AGENTS_FILE_BASENAME = "AGENTS.md";
|
|
7216
|
+
KEYNV_BLOCK_START = "<!-- keynv:start -- generated by `keynv init`; safe to leave intact, will be refreshed on re-run -->";
|
|
7217
|
+
KEYNV_BLOCK_END = "<!-- keynv:end -->";
|
|
7218
|
+
}
|
|
7219
|
+
});
|
|
7131
7220
|
|
|
7132
7221
|
// src/init/heuristics.ts
|
|
7133
7222
|
function shannonEntropyBits(s) {
|
|
@@ -7675,6 +7764,14 @@ async function runInitFlow(client, opts) {
|
|
|
7675
7764
|
R2.error(`Failed to write .keynv.env: ${err instanceof Error ? err.message : String(err)}`);
|
|
7676
7765
|
return { exitCode: 1 };
|
|
7677
7766
|
}
|
|
7767
|
+
try {
|
|
7768
|
+
const outcome = writeAiContext(root.path);
|
|
7769
|
+
if (outcome === "created") R2.success("Wrote AGENTS.md (so AI agents understand keynv)");
|
|
7770
|
+
else if (outcome === "updated") R2.success("Refreshed keynv section in AGENTS.md");
|
|
7771
|
+
else if (outcome === "appended") R2.success("Appended keynv section to AGENTS.md");
|
|
7772
|
+
} catch (err) {
|
|
7773
|
+
R2.warn(`Could not write AGENTS.md: ${err instanceof Error ? err.message : String(err)}`);
|
|
7774
|
+
}
|
|
7678
7775
|
if (scriptWrapSelection.length > 0 && root.packageJsonScripts) {
|
|
7679
7776
|
try {
|
|
7680
7777
|
updatePackageJsonScripts(
|
|
@@ -7869,6 +7966,7 @@ var init_init = __esm({
|
|
|
7869
7966
|
init_dist5();
|
|
7870
7967
|
init_envFile();
|
|
7871
7968
|
init_detect();
|
|
7969
|
+
init_aiContext();
|
|
7872
7970
|
init_heuristics();
|
|
7873
7971
|
init_scriptWrap();
|
|
7874
7972
|
init_cancel();
|
|
@@ -24764,7 +24862,7 @@ var require_named_placeholders = __commonJS({
|
|
|
24764
24862
|
}
|
|
24765
24863
|
return s;
|
|
24766
24864
|
}
|
|
24767
|
-
function
|
|
24865
|
+
function join6(tree) {
|
|
24768
24866
|
if (tree.length === 1) {
|
|
24769
24867
|
return tree;
|
|
24770
24868
|
}
|
|
@@ -24790,7 +24888,7 @@ var require_named_placeholders = __commonJS({
|
|
|
24790
24888
|
if (cache2 && (tree = cache2.get(query))) {
|
|
24791
24889
|
return toArrayParams(tree, paramsObj);
|
|
24792
24890
|
}
|
|
24793
|
-
tree =
|
|
24891
|
+
tree = join6(parse(query));
|
|
24794
24892
|
if (cache2) {
|
|
24795
24893
|
cache2.set(query, tree);
|
|
24796
24894
|
}
|