akm-cli 0.9.0-beta.54 → 0.9.0-beta.55
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/cli.js +5 -3
- package/dist/commands/agent/contribute-cli.js +2 -3
- package/dist/commands/env/env-cli.js +187 -202
- package/dist/commands/env/secret-cli.js +109 -121
- package/dist/commands/feedback-cli.js +152 -155
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/improve-metrics.js +754 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +18 -0
- package/dist/commands/health/windows.js +196 -0
- package/dist/commands/health.js +14 -1624
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/collapse-detector.js +3 -2
- package/dist/commands/improve/consolidate.js +636 -633
- package/dist/commands/improve/dedup.js +1 -1
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +228 -0
- package/dist/commands/improve/distill/quality-gate.js +233 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill.js +49 -575
- package/dist/commands/improve/extract-cli.js +74 -76
- package/dist/commands/improve/extract.js +6 -4
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +3 -2
- package/dist/commands/improve/improve-cli.js +14 -13
- package/dist/commands/improve/improve-result-file.js +2 -1
- package/dist/commands/improve/improve.js +6 -5
- package/dist/commands/improve/loop-stages.js +19 -21
- package/dist/commands/improve/preparation.js +4 -2
- package/dist/commands/improve/procedural.js +10 -31
- package/dist/commands/improve/recombine.js +19 -43
- package/dist/commands/improve/reflect.js +1 -1
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +48 -0
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +2 -2
- package/dist/commands/proposal/drain.js +1 -1
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +3 -3
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +1 -1
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposals.js +5 -920
- package/dist/commands/read/remember-cli.js +132 -137
- package/dist/commands/read/search-cli.js +1 -1
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/sources/add-cli.js +90 -94
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/schema-repair.js +1 -1
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +1 -2
- package/dist/commands/wiki-cli.js +2 -3
- package/dist/core/common.js +3 -3
- package/dist/core/config/config-schema.js +6 -0
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/events.js +2 -1
- package/dist/core/logs-db.js +8 -13
- package/dist/core/paths.js +14 -14
- package/dist/core/state-db.js +13 -1140
- package/dist/indexer/db/db.js +66 -709
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/feedback/utility-policy.js +85 -0
- package/dist/indexer/graph/graph-extraction.js +2 -1
- package/dist/indexer/index-writer-lock.js +9 -0
- package/dist/indexer/indexer.js +78 -23
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/integrations/agent/spawn.js +15 -66
- package/dist/output/text/helpers.js +13 -0
- package/dist/scripts/migrate-storage.js +6891 -7436
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +44 -43
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +24 -1607
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +130 -0
- package/dist/storage/repositories/index-db.js +4 -7
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +3 -3
- package/dist/tasks/runner.js +2 -1
- package/package.json +1 -1
- package/dist/commands/improve/homeostatic.js +0 -497
|
@@ -18,16 +18,14 @@
|
|
|
18
18
|
import { spawnSync } from "node:child_process";
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { defineCommand } from "citty";
|
|
22
21
|
import { getStringArg } from "../../cli/parse-args.js";
|
|
23
|
-
import { defineGroupCommand,
|
|
22
|
+
import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
|
|
24
23
|
import { deriveCanonicalAssetName } from "../../core/asset/asset-spec.js";
|
|
25
24
|
import { loadConfig } from "../../core/config/config.js";
|
|
26
25
|
import { makeSecretRef, resolveSecretPath } from "../../core/env-secret-ref.js";
|
|
27
26
|
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
28
27
|
import { appendEvent } from "../../core/events.js";
|
|
29
28
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
30
|
-
import { getHyphenatedArg } from "../../output/context.js";
|
|
31
29
|
import { readStdin } from "../../runtime.js";
|
|
32
30
|
import { buildChildEnv } from "./child-env.js";
|
|
33
31
|
function parseKeyListFlag(raw) {
|
|
@@ -70,18 +68,16 @@ function listSecretsRecursive() {
|
|
|
70
68
|
}
|
|
71
69
|
return result;
|
|
72
70
|
}
|
|
73
|
-
const secretListCommand =
|
|
71
|
+
const secretListCommand = defineJsonCommand({
|
|
74
72
|
meta: {
|
|
75
73
|
name: "list",
|
|
76
74
|
description: "List all secrets across all stashes by name (the file contents are never shown)",
|
|
77
75
|
},
|
|
78
|
-
run() {
|
|
79
|
-
|
|
80
|
-
output("secret-list", { secrets: listSecretsRecursive() });
|
|
81
|
-
});
|
|
76
|
+
async run() {
|
|
77
|
+
output("secret-list", { secrets: listSecretsRecursive() });
|
|
82
78
|
},
|
|
83
79
|
});
|
|
84
|
-
const secretSetCommand =
|
|
80
|
+
const secretSetCommand = defineJsonCommand({
|
|
85
81
|
meta: {
|
|
86
82
|
name: "set",
|
|
87
83
|
description: "Create or overwrite a secret. The value is read from stdin by default (never via argv). Use --from-file <path> to import an existing file byte-exact, or --from-env <VAR> to read from an environment variable. Multi-line values are allowed.",
|
|
@@ -99,49 +95,47 @@ const secretSetCommand = defineCommand({
|
|
|
99
95
|
"from-file": { type: "string", description: "Read the value from this file (stored byte-exact)" },
|
|
100
96
|
"from-env": { type: "string", description: "Read the value from the named environment variable" },
|
|
101
97
|
},
|
|
102
|
-
run({ args }) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
async run({ args }) {
|
|
99
|
+
const { setSecret } = await import("./secret.js");
|
|
100
|
+
const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
|
|
101
|
+
const fromEnv = args["from-env"];
|
|
102
|
+
const fromFile = args["from-file"];
|
|
103
|
+
if (fromEnv !== undefined && fromFile !== undefined) {
|
|
104
|
+
throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
|
|
105
|
+
}
|
|
106
|
+
const MAX_SECRET_BYTES = 5 * 1024 * 1024; // 5 MB
|
|
107
|
+
let value;
|
|
108
|
+
if (fromFile !== undefined) {
|
|
109
|
+
if (!fs.existsSync(fromFile)) {
|
|
110
|
+
throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (!fs.existsSync(fromFile)) {
|
|
115
|
-
throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
|
|
116
|
-
}
|
|
117
|
-
value = fs.readFileSync(fromFile);
|
|
118
|
-
if (value.byteLength > MAX_SECRET_BYTES) {
|
|
119
|
-
throw new UsageError("Secret exceeds the 5 MB limit.");
|
|
120
|
-
}
|
|
112
|
+
value = fs.readFileSync(fromFile);
|
|
113
|
+
if (value.byteLength > MAX_SECRET_BYTES) {
|
|
114
|
+
throw new UsageError("Secret exceeds the 5 MB limit.");
|
|
121
115
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
value = Buffer.from(envVal, "utf8");
|
|
116
|
+
}
|
|
117
|
+
else if (fromEnv !== undefined) {
|
|
118
|
+
const envVal = process.env[fromEnv];
|
|
119
|
+
if (envVal === undefined) {
|
|
120
|
+
throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
|
|
128
121
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
|
|
135
|
-
// stores the token without the shell-added newline. Use --from-file for
|
|
136
|
-
// byte-exact storage of multi-line material (PEM keys, certs).
|
|
137
|
-
value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
|
|
122
|
+
value = Buffer.from(envVal, "utf8");
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (process.stdin.isTTY) {
|
|
126
|
+
process.stderr.write(`Enter value for secret "${name}" (Ctrl-D when done):\n`);
|
|
138
127
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
const stdinBuf = await readStdin(MAX_SECRET_BYTES, () => new UsageError("Secret exceeds the 5 MB limit."));
|
|
129
|
+
// Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
|
|
130
|
+
// stores the token without the shell-added newline. Use --from-file for
|
|
131
|
+
// byte-exact storage of multi-line material (PEM keys, certs).
|
|
132
|
+
value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
|
|
133
|
+
}
|
|
134
|
+
setSecret(absPath, value);
|
|
135
|
+
output("secret-set", { ref: makeSecretRef(name, source) });
|
|
142
136
|
},
|
|
143
137
|
});
|
|
144
|
-
const secretPathCommand =
|
|
138
|
+
const secretPathCommand = defineJsonCommand({
|
|
145
139
|
meta: {
|
|
146
140
|
name: "path",
|
|
147
141
|
description: "Print the absolute secret file path for the Docker `_FILE` convention, e.g. `MY_SECRET_FILE=$(akm secret path secret:deploy-key)`.",
|
|
@@ -149,17 +143,15 @@ const secretPathCommand = defineCommand({
|
|
|
149
143
|
args: {
|
|
150
144
|
ref: { type: "positional", description: "Secret ref", required: true },
|
|
151
145
|
},
|
|
152
|
-
run({ args }) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
process.stdout.write(`${absPath}\n`);
|
|
159
|
-
});
|
|
146
|
+
async run({ args }) {
|
|
147
|
+
const { name, absPath, source } = resolveSecretPath(args.ref);
|
|
148
|
+
if (!fs.existsSync(absPath)) {
|
|
149
|
+
throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
|
|
150
|
+
}
|
|
151
|
+
process.stdout.write(`${absPath}\n`);
|
|
160
152
|
},
|
|
161
153
|
});
|
|
162
|
-
const secretRunCommand =
|
|
154
|
+
const secretRunCommand = defineJsonCommand({
|
|
163
155
|
meta: {
|
|
164
156
|
name: "run",
|
|
165
157
|
description: "Run a command with a secret's value injected into an env var: `akm secret run <ref> <VAR> -- <command>`. The value is set as $VAR in the child process only. Pass --clean to start the child with a minimal inherited environment instead of the full parent environment.",
|
|
@@ -177,82 +169,78 @@ const secretRunCommand = defineCommand({
|
|
|
177
169
|
description: "When used with --clean, also inherit these parent env vars (comma-separated). Ignored without --clean.",
|
|
178
170
|
},
|
|
179
171
|
},
|
|
180
|
-
run({ args }) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
172
|
+
async run({ args }) {
|
|
173
|
+
// Validate the target env var name FIRST (before the command split) so a
|
|
174
|
+
// dangerous/invalid name is rejected regardless of how the command is
|
|
175
|
+
// supplied — and so the failure does not depend on argv parsing.
|
|
176
|
+
const varName = args.var;
|
|
177
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(varName)) {
|
|
178
|
+
throw new UsageError(`"${varName}" is not a valid environment variable name.`, "INVALID_FLAG_VALUE");
|
|
179
|
+
}
|
|
180
|
+
const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
|
|
181
|
+
if (isDangerousEnvKey(varName)) {
|
|
182
|
+
throw new UsageError(`Refusing to inject a secret into "${varName}": it is a known process-hijacking variable (e.g. LD_PRELOAD, PATH).`, "INVALID_FLAG_VALUE");
|
|
183
|
+
}
|
|
184
|
+
const dashIndex = process.argv.indexOf("--");
|
|
185
|
+
if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
|
|
186
|
+
throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
|
|
187
|
+
}
|
|
188
|
+
const command = process.argv.slice(dashIndex + 1);
|
|
189
|
+
const { name, absPath, source } = resolveSecretPath(args.ref);
|
|
190
|
+
if (!fs.existsSync(absPath)) {
|
|
191
|
+
throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
|
|
192
|
+
}
|
|
193
|
+
const { readValue } = await import("./secret.js");
|
|
194
|
+
const mergedEnv = buildChildEnv(process.env, {
|
|
195
|
+
clean: args.clean === true,
|
|
196
|
+
inherit: parseKeyListFlag(args.inherit) ?? [],
|
|
197
|
+
});
|
|
198
|
+
mergedEnv[varName] = readValue(absPath).toString("utf8");
|
|
199
|
+
// Audit trail: record access by ref + var name only — never the value.
|
|
200
|
+
appendEvent({
|
|
201
|
+
eventType: "secret_access",
|
|
202
|
+
ref: makeSecretRef(name, source),
|
|
203
|
+
metadata: { var: varName },
|
|
204
|
+
});
|
|
205
|
+
const result = spawnSync(command[0], command.slice(1), {
|
|
206
|
+
stdio: "inherit",
|
|
207
|
+
env: mergedEnv,
|
|
208
|
+
});
|
|
209
|
+
if (result.error) {
|
|
210
|
+
const err = result.error;
|
|
211
|
+
if (err.code === "ENOENT") {
|
|
212
|
+
throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
|
|
201
213
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
clean: getHyphenatedArg(args, "clean") === true,
|
|
205
|
-
inherit: parseKeyListFlag(getHyphenatedArg(args, "inherit")) ?? [],
|
|
206
|
-
});
|
|
207
|
-
mergedEnv[varName] = readValue(absPath).toString("utf8");
|
|
208
|
-
// Audit trail: record access by ref + var name only — never the value.
|
|
209
|
-
appendEvent({
|
|
210
|
-
eventType: "secret_access",
|
|
211
|
-
ref: makeSecretRef(name, source),
|
|
212
|
-
metadata: { var: varName },
|
|
213
|
-
});
|
|
214
|
-
const result = spawnSync(command[0], command.slice(1), {
|
|
215
|
-
stdio: "inherit",
|
|
216
|
-
env: mergedEnv,
|
|
217
|
-
});
|
|
218
|
-
if (result.error) {
|
|
219
|
-
const err = result.error;
|
|
220
|
-
if (err.code === "ENOENT") {
|
|
221
|
-
throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
|
|
222
|
-
}
|
|
223
|
-
if (err.code === "EACCES") {
|
|
224
|
-
throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
|
|
225
|
-
}
|
|
226
|
-
throw err;
|
|
214
|
+
if (err.code === "EACCES") {
|
|
215
|
+
throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
|
|
227
216
|
}
|
|
228
|
-
|
|
229
|
-
}
|
|
217
|
+
throw err;
|
|
218
|
+
}
|
|
219
|
+
process.exit(result.status ?? 0);
|
|
230
220
|
},
|
|
231
221
|
});
|
|
232
|
-
const secretRemoveCommand =
|
|
222
|
+
const secretRemoveCommand = defineJsonCommand({
|
|
233
223
|
meta: { name: "remove", description: "Remove a secret (and its .sensitive marker, if any)" },
|
|
234
224
|
args: {
|
|
235
225
|
ref: { type: "positional", description: "Secret ref", required: true },
|
|
236
226
|
yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
|
|
237
227
|
},
|
|
238
|
-
run({ args }) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
yes: args.yes === true,
|
|
244
|
-
});
|
|
245
|
-
if (!confirmed) {
|
|
246
|
-
process.stderr.write("Aborted.\n");
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const { removeSecret } = await import("./secret.js");
|
|
250
|
-
if (!fs.existsSync(absPath)) {
|
|
251
|
-
throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
|
|
252
|
-
}
|
|
253
|
-
const removed = removeSecret(absPath);
|
|
254
|
-
output("secret-remove", { ref: makeSecretRef(name, source), removed });
|
|
228
|
+
async run({ args }) {
|
|
229
|
+
const { name, absPath, source } = resolveSecretPath(args.ref);
|
|
230
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
231
|
+
const confirmed = await confirmDestructive(`Remove secret "${args.ref}"? This cannot be undone.`, {
|
|
232
|
+
yes: args.yes === true,
|
|
255
233
|
});
|
|
234
|
+
if (!confirmed) {
|
|
235
|
+
process.stderr.write("Aborted.\n");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const { removeSecret } = await import("./secret.js");
|
|
239
|
+
if (!fs.existsSync(absPath)) {
|
|
240
|
+
throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
|
|
241
|
+
}
|
|
242
|
+
const removed = removeSecret(absPath);
|
|
243
|
+
output("secret-remove", { ref: makeSecretRef(name, source), removed });
|
|
256
244
|
},
|
|
257
245
|
});
|
|
258
246
|
export const secretCommand = defineGroupCommand({
|