finitemonkey 0.1.0-next.17 → 0.1.0-next.19
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/README.es.md +1 -0
- package/README.md +1 -0
- package/README.pt-BR.md +1 -0
- package/dist/finitemonkey.mjs +471 -125
- package/package.json +4 -2
- package/runbooks/anthropic.md +31 -0
- package/runbooks/asaas.md +31 -0
- package/runbooks/aws.md +32 -0
- package/runbooks/cloudflare.md +31 -0
- package/runbooks/database.md +32 -0
- package/runbooks/digitalocean.md +31 -0
- package/runbooks/generic.md +31 -0
- package/runbooks/github.md +32 -0
- package/runbooks/google.md +31 -0
- package/runbooks/mailgun.md +31 -0
- package/runbooks/openai.md +31 -0
- package/runbooks/stripe.md +31 -0
package/dist/finitemonkey.mjs
CHANGED
|
@@ -1156,8 +1156,8 @@ var isOwnHandler = (handler) => {
|
|
|
1156
1156
|
if (args2.length === 0) return OWN_SHELL.test(`${command} `);
|
|
1157
1157
|
const hookAt = args2.indexOf("hook");
|
|
1158
1158
|
if (hookAt < 0) return false;
|
|
1159
|
-
const
|
|
1160
|
-
return OWN_SCRIPT.test(
|
|
1159
|
+
const target2 = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
|
|
1160
|
+
return OWN_SCRIPT.test(target2);
|
|
1161
1161
|
};
|
|
1162
1162
|
|
|
1163
1163
|
// src/install/skill-template.ts
|
|
@@ -3356,8 +3356,8 @@ var readRedirect = (cursor, match) => {
|
|
|
3356
3356
|
cursor.pos += match[0].length;
|
|
3357
3357
|
skipBlank(cursor);
|
|
3358
3358
|
const processSub = peek(cursor) === "(" && op === "<";
|
|
3359
|
-
const
|
|
3360
|
-
return { kind: "redirect", op, fd: match[1] ?? "", target, body: void 0 };
|
|
3359
|
+
const target2 = processSub ? "" : readWord(cursor).text;
|
|
3360
|
+
return { kind: "redirect", op, fd: match[1] ?? "", target: target2, body: void 0 };
|
|
3361
3361
|
};
|
|
3362
3362
|
var processSubstitution = (cursor) => {
|
|
3363
3363
|
if (peek(cursor) !== "<" && peek(cursor) !== ">" || peek(cursor, 1) !== "(") return false;
|
|
@@ -3536,8 +3536,8 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3536
3536
|
if (redirect !== null) {
|
|
3537
3537
|
cursor.pos += redirect[0].length;
|
|
3538
3538
|
while (peek(cursor) === " ") cursor.pos += 1;
|
|
3539
|
-
const
|
|
3540
|
-
tokens.push({ kind: "redirect", op: redirect[2] ?? ">", fd: redirect[1] ?? "", target, body: void 0 });
|
|
3539
|
+
const target2 = redirect[3] ?? word(cursor, cmd).text;
|
|
3540
|
+
tokens.push({ kind: "redirect", op: redirect[2] ?? ">", fd: redirect[1] ?? "", target: target2, body: void 0 });
|
|
3541
3541
|
return;
|
|
3542
3542
|
}
|
|
3543
3543
|
const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
|
|
@@ -3759,8 +3759,8 @@ var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
|
3759
3759
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
3760
3760
|
var lex = (text6, language) => language === "bash" ? lexBash(text6) : lexPowerShell(text6, language === "cmd");
|
|
3761
3761
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
3762
|
-
var joinDir = (current2,
|
|
3763
|
-
const next2 = toPosixPath(
|
|
3762
|
+
var joinDir = (current2, target2) => {
|
|
3763
|
+
const next2 = toPosixPath(target2);
|
|
3764
3764
|
if (next2.startsWith("/") || isWindowsAbsolute(next2) || next2.startsWith("~")) return next2;
|
|
3765
3765
|
return current2 === void 0 ? posix.normalize(next2) : posix.join(current2, next2);
|
|
3766
3766
|
};
|
|
@@ -3785,8 +3785,8 @@ var track = (words, language, context) => {
|
|
|
3785
3785
|
if (assigned !== void 0) context.env = { ...context.env, ...assigned };
|
|
3786
3786
|
const program = language === "bash" ? words[0] ?? "" : powershellProgram(words[0] ?? "");
|
|
3787
3787
|
if (!CHANGES_DIR.has(program)) return;
|
|
3788
|
-
const
|
|
3789
|
-
context.dir =
|
|
3788
|
+
const target2 = words.slice(1).find((word2) => !word2.startsWith("-"));
|
|
3789
|
+
context.dir = target2 === void 0 || target2 === "-" ? "~" : joinDir(context.dir, target2);
|
|
3790
3790
|
};
|
|
3791
3791
|
var emit = (stage, raw, language, context, collector) => {
|
|
3792
3792
|
const first = stage.argv[0] ?? "";
|
|
@@ -3923,10 +3923,21 @@ var gitFindings = (args2) => {
|
|
|
3923
3923
|
return found;
|
|
3924
3924
|
};
|
|
3925
3925
|
var FM_NAMES = /* @__PURE__ */ new Set(["finitemonkey", "fm"]);
|
|
3926
|
+
var FM_SCRIPT = /(^|[\\/])(finitemonkey|fm)(\.c?m?js)?$/;
|
|
3927
|
+
var RUNNERS = /* @__PURE__ */ new Set(["npx", "pnpm", "bunx", "yarn"]);
|
|
3928
|
+
var fmStart = (argv) => {
|
|
3929
|
+
const first = argv[0] ?? "";
|
|
3930
|
+
if (RUNNERS.has(first)) return argv.findIndex((arg) => FM_NAMES.has(arg.replace(/@.*$/, "")));
|
|
3931
|
+
if (first === "node" || first === "bun" || first === "deno") return FM_SCRIPT.test(argv[1] ?? "") ? 1 : -1;
|
|
3932
|
+
return FM_SCRIPT.test(first) ? 0 : -1;
|
|
3933
|
+
};
|
|
3926
3934
|
var fmFindings = (argv) => {
|
|
3927
|
-
const start = argv
|
|
3928
|
-
|
|
3929
|
-
|
|
3935
|
+
const start = fmStart(argv);
|
|
3936
|
+
if (start < 0) return [];
|
|
3937
|
+
const sub = argv[start + 1];
|
|
3938
|
+
if (sub === "allow") return [{ kind: "fm_allow", detail: "finitemonkey allow" }];
|
|
3939
|
+
if (sub === "off" || sub === "on") return [{ kind: "fm_pause", detail: `finitemonkey ${sub}` }];
|
|
3940
|
+
return [];
|
|
3930
3941
|
};
|
|
3931
3942
|
var detectBypass = (commandLine) => parseShell(commandLine).flatMap((command) => [
|
|
3932
3943
|
...envFindings(command),
|
|
@@ -4244,9 +4255,6 @@ var testsGuard = (input) => {
|
|
|
4244
4255
|
return deny(t("gate.guard.tests.model", { detail: reason }), t("gate.guard.tests.human", { detail: reason }));
|
|
4245
4256
|
};
|
|
4246
4257
|
|
|
4247
|
-
// src/gate/hooks/config.ts
|
|
4248
|
-
import { join as join13 } from "node:path";
|
|
4249
|
-
|
|
4250
4258
|
// src/gate/hooks/common.ts
|
|
4251
4259
|
import { dirname as dirname5, join as join12 } from "node:path";
|
|
4252
4260
|
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
@@ -4277,7 +4285,90 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
|
4277
4285
|
});
|
|
4278
4286
|
var human = (text6) => `finitemonkey: ${text6}`;
|
|
4279
4287
|
|
|
4288
|
+
// src/guards/release/markers.ts
|
|
4289
|
+
var LINE = /^\s*(?:finitemonkey|fm):\s+(allow|approve|trust)\s+(\S+)(?:\s+(.*))?$/i;
|
|
4290
|
+
var NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,80}$/;
|
|
4291
|
+
var NOT_FROM_PERSON = [
|
|
4292
|
+
/<task-notification\b/i,
|
|
4293
|
+
/<agent-message\b/i,
|
|
4294
|
+
/^\s*\[julius2claude\]/,
|
|
4295
|
+
/\[Subagent hand-back\]/i
|
|
4296
|
+
];
|
|
4297
|
+
var comesFromPerson = (prompt) => !NOT_FROM_PERSON.some((pattern) => pattern.test(prompt));
|
|
4298
|
+
var toMarker = (verb, target2, rest) => {
|
|
4299
|
+
if (!NAME.test(target2)) return void 0;
|
|
4300
|
+
if (verb === "trust") return { type: "trust", task: target2 };
|
|
4301
|
+
if (verb === "allow") {
|
|
4302
|
+
return rest.length >= 3 ? { type: "allow", guard: target2.toLowerCase(), reason: rest } : void 0;
|
|
4303
|
+
}
|
|
4304
|
+
const slash = target2.indexOf("/");
|
|
4305
|
+
const task = slash < 0 ? void 0 : target2.slice(0, slash);
|
|
4306
|
+
const check2 = slash < 0 ? target2 : target2.slice(slash + 1);
|
|
4307
|
+
if (check2 === "") return void 0;
|
|
4308
|
+
return { type: "approve", check: check2, reason: rest, ...task === void 0 || task === "" ? {} : { task } };
|
|
4309
|
+
};
|
|
4310
|
+
var parseMarkers = (prompt) => {
|
|
4311
|
+
if (!comesFromPerson(prompt)) return [];
|
|
4312
|
+
return prompt.split(/\r?\n/).flatMap((line2) => {
|
|
4313
|
+
const match = LINE.exec(line2);
|
|
4314
|
+
if (match === null) return [];
|
|
4315
|
+
const marker = toMarker((match[1] ?? "").toLowerCase(), match[2] ?? "", (match[3] ?? "").trim());
|
|
4316
|
+
return marker === void 0 ? [] : [marker];
|
|
4317
|
+
});
|
|
4318
|
+
};
|
|
4319
|
+
|
|
4320
|
+
// src/policy/pause.ts
|
|
4321
|
+
var PAUSE_FILE = "pause.json";
|
|
4322
|
+
var readPause = (runtime, paths) => {
|
|
4323
|
+
const text6 = runtime.readText(paths.cache(PAUSE_FILE));
|
|
4324
|
+
const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
|
|
4325
|
+
if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
|
|
4326
|
+
const { sessionId, since, by } = parsed.value;
|
|
4327
|
+
if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
|
|
4328
|
+
return { sessionId, since, by: by === "terminal" ? "terminal" : "prompt" };
|
|
4329
|
+
};
|
|
4330
|
+
var pausedFor = (runtime, paths, sessionId) => {
|
|
4331
|
+
const pause = readPause(runtime, paths);
|
|
4332
|
+
return pause !== void 0 && sessionId !== void 0 && pause.sessionId === sessionId ? pause : void 0;
|
|
4333
|
+
};
|
|
4334
|
+
var setPause = (runtime, paths, sessionId, by) => {
|
|
4335
|
+
const pause = { sessionId, since: runtime.now(), by };
|
|
4336
|
+
runtime.writeText(paths.cache(PAUSE_FILE), `${JSON.stringify(pause)}
|
|
4337
|
+
`);
|
|
4338
|
+
logEvent(runtime, paths, "gate_paused", { sessionId, by });
|
|
4339
|
+
return pause;
|
|
4340
|
+
};
|
|
4341
|
+
var clearPause = (runtime, paths, by) => {
|
|
4342
|
+
const pause = readPause(runtime, paths);
|
|
4343
|
+
if (pause === void 0) return void 0;
|
|
4344
|
+
runtime.remove(paths.cache(PAUSE_FILE));
|
|
4345
|
+
logEvent(runtime, paths, "gate_resumed", { sessionId: pause.sessionId, since: pause.since, by });
|
|
4346
|
+
return pause;
|
|
4347
|
+
};
|
|
4348
|
+
|
|
4349
|
+
// src/policy/pause-hooks.ts
|
|
4350
|
+
var COMMAND = /^\s*(fm|finitemonkey)\s+(off|on)\s*$/i;
|
|
4351
|
+
var pausePrompt = (input) => {
|
|
4352
|
+
const prompt = textField(input.payload.raw, "prompt", "prompt_text") ?? "";
|
|
4353
|
+
const match = COMMAND.exec(prompt);
|
|
4354
|
+
if (match === null || !comesFromPerson(prompt)) return { kind: "allow" };
|
|
4355
|
+
const paths = projectPaths(input);
|
|
4356
|
+
const sessionId = input.payload.sessionId;
|
|
4357
|
+
if (paths === void 0 || sessionId === void 0) return { kind: "allow" };
|
|
4358
|
+
const off = (match[2] ?? "").toLowerCase() === "off";
|
|
4359
|
+
if (off) setPause(input.runtime, paths, sessionId, "prompt");
|
|
4360
|
+
else clearPause(input.runtime, paths, "prompt");
|
|
4361
|
+
return { kind: "block", reason: human(input.t(off ? "pause.off" : "pause.on")) };
|
|
4362
|
+
};
|
|
4363
|
+
var unlessPaused = (handler) => (input) => {
|
|
4364
|
+
const paths = projectPaths(input);
|
|
4365
|
+
const pause = paths === void 0 ? void 0 : pausedFor(input.runtime, paths, input.payload.sessionId);
|
|
4366
|
+
if (pause === void 0) return handler(input);
|
|
4367
|
+
return allowWith({ systemMessage: human(input.t("pause.report", { since: pause.since })) });
|
|
4368
|
+
};
|
|
4369
|
+
|
|
4280
4370
|
// src/gate/hooks/config.ts
|
|
4371
|
+
import { join as join13 } from "node:path";
|
|
4281
4372
|
var CONFIG_FILE3 = "config-hooks.json";
|
|
4282
4373
|
var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
|
|
4283
4374
|
var countIn = (value) => {
|
|
@@ -4343,38 +4434,6 @@ var configChange = (input) => {
|
|
|
4343
4434
|
};
|
|
4344
4435
|
};
|
|
4345
4436
|
|
|
4346
|
-
// src/guards/release/markers.ts
|
|
4347
|
-
var LINE = /^\s*(?:finitemonkey|fm):\s+(allow|approve|trust)\s+(\S+)(?:\s+(.*))?$/i;
|
|
4348
|
-
var NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,80}$/;
|
|
4349
|
-
var NOT_FROM_PERSON = [
|
|
4350
|
-
/<task-notification\b/i,
|
|
4351
|
-
/<agent-message\b/i,
|
|
4352
|
-
/^\s*\[julius2claude\]/,
|
|
4353
|
-
/\[Subagent hand-back\]/i
|
|
4354
|
-
];
|
|
4355
|
-
var comesFromPerson = (prompt) => !NOT_FROM_PERSON.some((pattern) => pattern.test(prompt));
|
|
4356
|
-
var toMarker = (verb, target, rest) => {
|
|
4357
|
-
if (!NAME.test(target)) return void 0;
|
|
4358
|
-
if (verb === "trust") return { type: "trust", task: target };
|
|
4359
|
-
if (verb === "allow") {
|
|
4360
|
-
return rest.length >= 3 ? { type: "allow", guard: target.toLowerCase(), reason: rest } : void 0;
|
|
4361
|
-
}
|
|
4362
|
-
const slash = target.indexOf("/");
|
|
4363
|
-
const task = slash < 0 ? void 0 : target.slice(0, slash);
|
|
4364
|
-
const check2 = slash < 0 ? target : target.slice(slash + 1);
|
|
4365
|
-
if (check2 === "") return void 0;
|
|
4366
|
-
return { type: "approve", check: check2, reason: rest, ...task === void 0 || task === "" ? {} : { task } };
|
|
4367
|
-
};
|
|
4368
|
-
var parseMarkers = (prompt) => {
|
|
4369
|
-
if (!comesFromPerson(prompt)) return [];
|
|
4370
|
-
return prompt.split(/\r?\n/).flatMap((line2) => {
|
|
4371
|
-
const match = LINE.exec(line2);
|
|
4372
|
-
if (match === null) return [];
|
|
4373
|
-
const marker = toMarker((match[1] ?? "").toLowerCase(), match[2] ?? "", (match[3] ?? "").trim());
|
|
4374
|
-
return marker === void 0 ? [] : [marker];
|
|
4375
|
-
});
|
|
4376
|
-
};
|
|
4377
|
-
|
|
4378
4437
|
// src/task/implicit.ts
|
|
4379
4438
|
var IMPLICIT_ID = "implicit";
|
|
4380
4439
|
var LOCKFILES2 = [
|
|
@@ -5132,9 +5191,9 @@ var prStatus = (runtime, git, paths, ttlMs = PR_CACHE_TTL_MS) => {
|
|
|
5132
5191
|
return { kind: "found", branch, pr: status.pr, localHead: headSha(git) };
|
|
5133
5192
|
};
|
|
5134
5193
|
var conflictFiles = (git, base) => {
|
|
5135
|
-
for (const
|
|
5136
|
-
if (git.out(["rev-parse", "--verify", "-q", `${
|
|
5137
|
-
const result = git.run(["merge-tree", "--write-tree", "--name-only", "--no-messages",
|
|
5194
|
+
for (const target2 of [`origin/${base}`, base]) {
|
|
5195
|
+
if (git.out(["rev-parse", "--verify", "-q", `${target2}^{commit}`]) === void 0) continue;
|
|
5196
|
+
const result = git.run(["merge-tree", "--write-tree", "--name-only", "--no-messages", target2, "HEAD"]);
|
|
5138
5197
|
if (result.found && result.exitCode === 1) return parseMergeTreeConflicts(result.stdout);
|
|
5139
5198
|
if (result.found && result.exitCode === 0) return [];
|
|
5140
5199
|
}
|
|
@@ -5953,6 +6012,7 @@ var en_default = {
|
|
|
5953
6012
|
"gate.guard.bypass.hooks_path": "Finite Monkey: blocked: {detail} points git away from the project hooks. requires: core.hooksPath stays as configured.",
|
|
5954
6013
|
"gate.guard.bypass.hook_env": "Finite Monkey: blocked: {detail} turns hooks or guards off. requires: hooks and guards stay on.",
|
|
5955
6014
|
"gate.guard.bypass.fm_allow": "Finite Monkey: blocked: releases are made by the person, not by a tool call.",
|
|
6015
|
+
"gate.guard.bypass.fm_pause": "Finite Monkey: blocked: only the person pauses or resumes the gate, by typing `fm off` or `fm on` as the whole prompt.",
|
|
5956
6016
|
"gate.guard.bypass.human": "Blocked {detail}: it would switch the safety net off. If you really want it, type on its own line: finitemonkey: allow bypass <reason>",
|
|
5957
6017
|
"gate.guard.self.policy": "Finite Monkey: blocked: {file} is written only by the finitemonkey executable (evidence, policy and state). requires: change it through finitemonkey commands.",
|
|
5958
6018
|
"gate.guard.self.allow_store": "Finite Monkey: blocked: {file} records what the person released; tools never write it.",
|
|
@@ -6129,7 +6189,24 @@ var en_default = {
|
|
|
6129
6189
|
"gate.runtime.body_mismatch": '{route} did not contain "{body}"',
|
|
6130
6190
|
"gate.runtime.request_failed": "the request to {route} failed: {detail}",
|
|
6131
6191
|
"gate.runtime.orphan": "the app on port {port} could not be stopped",
|
|
6132
|
-
"gate.runtime.probe_failed": "the app check did not report a result: {line}"
|
|
6192
|
+
"gate.runtime.probe_failed": "the app check did not report a result: {line}",
|
|
6193
|
+
"command.off.summary": "Pause the gate and warnings for the current Claude Code session (the person only)",
|
|
6194
|
+
"command.on.summary": "Resume the gate after fm off",
|
|
6195
|
+
"pause.off": "Gate paused for this session by the person. Secrets, production and destructive git guards stay on. Type `fm on` to resume.",
|
|
6196
|
+
"pause.on": "Gate resumed.",
|
|
6197
|
+
"pause.report": "The gate did not check this stop: the person paused it with fm off at {since}.",
|
|
6198
|
+
"pause.no_session": "No Claude Code session is recorded for this project yet; start one, then run this again or type `fm off` there.",
|
|
6199
|
+
"pause.error.person_only": "Only the person can pause or resume the gate, and this does not look like their terminal (no TTY, or running inside Claude Code)",
|
|
6200
|
+
"secrets.incident.runbook": " runbook: {link}",
|
|
6201
|
+
"secrets.command.live": "Check whether a key still works with its provider (off by default; sends only with --send, and prints the host first)",
|
|
6202
|
+
"secrets.action.live": "send a key to its provider",
|
|
6203
|
+
"secrets.live.dry": "Nothing was sent. With --send, the value of ${variable} would go to {host} ({url}) to check whether the {provider} key still works. Run it yourself, in your own terminal, with --send.",
|
|
6204
|
+
"secrets.live.contacting": "Contacting {host} ({url}) with the key; the value is passed to curl on stdin, never as an argument.",
|
|
6205
|
+
"secrets.live.live": "The key in ${variable} still works at {host} (HTTP {status}). Revoke it now: see the runbook in `finitemonkey secrets incident`.",
|
|
6206
|
+
"secrets.live.rejected": "{host} rejected the key in ${variable} (HTTP {status}): it no longer works.",
|
|
6207
|
+
"secrets.live.unknown": "{host} answered HTTP {status}; that does not say whether the key in ${variable} works. Check it in the provider's console.",
|
|
6208
|
+
"secrets.live.unreachable": "Could not reach {host} with curl, so the key in ${variable} was not checked.",
|
|
6209
|
+
"secrets.live.no_value": "${variable} is empty or not set in this terminal; export it first, nothing was sent"
|
|
6133
6210
|
};
|
|
6134
6211
|
|
|
6135
6212
|
// src/i18n/es.json
|
|
@@ -6506,6 +6583,7 @@ var es_default = {
|
|
|
6506
6583
|
"gate.guard.bypass.hooks_path": "Finite Monkey: bloqueado: {detail} desvía git de los hooks del proyecto. exige: core.hooksPath queda como está configurado.",
|
|
6507
6584
|
"gate.guard.bypass.hook_env": "Finite Monkey: bloqueado: {detail} apaga hooks o guardas. exige: hooks y guardas quedan encendidos.",
|
|
6508
6585
|
"gate.guard.bypass.fm_allow": "Finite Monkey: bloqueado: las liberaciones las hace la persona, no una llamada de herramienta.",
|
|
6586
|
+
"gate.guard.bypass.fm_pause": "Finite Monkey: bloqueado: solo la persona pausa o reanuda la compuerta, escribiendo `fm off` o `fm on` como el prompt completo.",
|
|
6509
6587
|
"gate.guard.bypass.human": "Bloqueé {detail}: apagaría la red de seguridad. Si de verdad lo quiere, escriba en una línea: finitemonkey: allow bypass <motivo>",
|
|
6510
6588
|
"gate.guard.self.policy": "Finite Monkey: bloqueado: {file} solo lo escribe el ejecutable finitemonkey (evidencia, política y estado). exige: cambiarlo con los comandos de finitemonkey.",
|
|
6511
6589
|
"gate.guard.self.allow_store": "Finite Monkey: bloqueado: {file} registra lo que la persona liberó; ninguna herramienta lo escribe.",
|
|
@@ -6682,7 +6760,24 @@ var es_default = {
|
|
|
6682
6760
|
"gate.runtime.body_mismatch": '{route} no contenía "{body}"',
|
|
6683
6761
|
"gate.runtime.request_failed": "la petición a {route} falló: {detail}",
|
|
6684
6762
|
"gate.runtime.orphan": "no se pudo detener la app del puerto {port}",
|
|
6685
|
-
"gate.runtime.probe_failed": "la comprobación de la app no devolvió resultado: {line}"
|
|
6763
|
+
"gate.runtime.probe_failed": "la comprobación de la app no devolvió resultado: {line}",
|
|
6764
|
+
"command.off.summary": "Pausa la compuerta y los avisos en la sesión actual de Claude Code (solo la persona)",
|
|
6765
|
+
"command.on.summary": "Reanuda la compuerta después de fm off",
|
|
6766
|
+
"pause.off": "Compuerta pausada en esta sesión por la persona. Los bloqueos de secretos, producción y git destructivo siguen activos. Escribe `fm on` para reanudar.",
|
|
6767
|
+
"pause.on": "Compuerta reanudada.",
|
|
6768
|
+
"pause.report": "La compuerta no revisó esta parada: la persona la pausó con fm off a las {since}.",
|
|
6769
|
+
"pause.no_session": "Todavía no hay una sesión de Claude Code registrada en este proyecto; abre una y vuelve a ejecutarlo, o escribe `fm off` allí.",
|
|
6770
|
+
"pause.error.person_only": "Solo la persona puede pausar o reanudar la compuerta, y esto no parece su terminal (sin TTY, o dentro de Claude Code)",
|
|
6771
|
+
"secrets.incident.runbook": " guía: {link}",
|
|
6772
|
+
"secrets.command.live": "Comprueba si una clave aún funciona en su proveedor (apagado por defecto; solo envía con --send y muestra el host antes)",
|
|
6773
|
+
"secrets.action.live": "enviar una clave a su proveedor",
|
|
6774
|
+
"secrets.live.dry": "No se envió nada. Con --send, el valor de ${variable} iría a {host} ({url}) para comprobar si la clave de {provider} aún funciona. Ejecútalo tú, en tu terminal, con --send.",
|
|
6775
|
+
"secrets.live.contacting": "Contactando {host} ({url}) con la clave; el valor va a curl por la entrada estándar, nunca como argumento.",
|
|
6776
|
+
"secrets.live.live": "La clave en ${variable} aún funciona en {host} (HTTP {status}). Revócala ya: mira la guía en `finitemonkey secrets incident`.",
|
|
6777
|
+
"secrets.live.rejected": "{host} rechazó la clave en ${variable} (HTTP {status}): ya no funciona.",
|
|
6778
|
+
"secrets.live.unknown": "{host} respondió HTTP {status}; eso no dice si la clave en ${variable} funciona. Revísala en la consola del proveedor.",
|
|
6779
|
+
"secrets.live.unreachable": "No se pudo llegar a {host} con curl, así que la clave en ${variable} no se comprobó.",
|
|
6780
|
+
"secrets.live.no_value": "${variable} está vacía o no existe en esta terminal; expórtala antes, no se envió nada"
|
|
6686
6781
|
};
|
|
6687
6782
|
|
|
6688
6783
|
// src/i18n/pt-BR.json
|
|
@@ -7059,6 +7154,7 @@ var pt_BR_default = {
|
|
|
7059
7154
|
"gate.guard.bypass.hooks_path": "Finite Monkey: barrado: {detail} desvia o git dos hooks do projeto. exige: core.hooksPath fica como está configurado.",
|
|
7060
7155
|
"gate.guard.bypass.hook_env": "Finite Monkey: barrado: {detail} desliga hooks ou travas. exige: hooks e travas ficam ligados.",
|
|
7061
7156
|
"gate.guard.bypass.fm_allow": "Finite Monkey: barrado: liberação é feita pela pessoa, não por chamada de ferramenta.",
|
|
7157
|
+
"gate.guard.bypass.fm_pause": "Finite Monkey: bloqueado: só a pessoa pausa ou retoma o portão, digitando `fm off` ou `fm on` como o prompt inteiro.",
|
|
7062
7158
|
"gate.guard.bypass.human": "Barrei {detail}: desligaria a rede de proteção. Se quiser mesmo, digite numa linha: finitemonkey: allow bypass <motivo>",
|
|
7063
7159
|
"gate.guard.self.policy": "Finite Monkey: barrado: {file} só é escrito pelo executável finitemonkey (evidência, política e estado). exige: mudar pelos comandos do finitemonkey.",
|
|
7064
7160
|
"gate.guard.self.allow_store": "Finite Monkey: barrado: {file} registra o que a pessoa liberou; ferramenta nunca escreve nele.",
|
|
@@ -7235,7 +7331,24 @@ var pt_BR_default = {
|
|
|
7235
7331
|
"gate.runtime.body_mismatch": '{route} não continha "{body}"',
|
|
7236
7332
|
"gate.runtime.request_failed": "a requisição para {route} falhou: {detail}",
|
|
7237
7333
|
"gate.runtime.orphan": "o app na porta {port} não pôde ser parado",
|
|
7238
|
-
"gate.runtime.probe_failed": "a checagem do app não devolveu resultado: {line}"
|
|
7334
|
+
"gate.runtime.probe_failed": "a checagem do app não devolveu resultado: {line}",
|
|
7335
|
+
"command.off.summary": "Pausa o portão e os avisos na sessão atual do Claude Code (só a pessoa)",
|
|
7336
|
+
"command.on.summary": "Retoma o portão depois de fm off",
|
|
7337
|
+
"pause.off": "Portão pausado nesta sessão pela pessoa. As travas de segredos, produção e git destrutivo continuam ligadas. Digite `fm on` para retomar.",
|
|
7338
|
+
"pause.on": "Portão retomado.",
|
|
7339
|
+
"pause.report": "O portão não conferiu esta parada: a pessoa pausou com fm off em {since}.",
|
|
7340
|
+
"pause.no_session": "Ainda não há sessão do Claude Code registrada neste projeto; abra uma e rode de novo, ou digite `fm off` nela.",
|
|
7341
|
+
"pause.error.person_only": "Só a pessoa pode pausar ou retomar o portão, e isto não parece o terminal dela (sem TTY, ou rodando dentro do Claude Code)",
|
|
7342
|
+
"secrets.incident.runbook": " passo a passo: {link}",
|
|
7343
|
+
"secrets.command.live": "Verifica se uma chave ainda funciona no provedor (desligado por padrão; só envia com --send e mostra o host antes)",
|
|
7344
|
+
"secrets.action.live": "enviar uma chave ao provedor",
|
|
7345
|
+
"secrets.live.dry": "Nada foi enviado. Com --send, o valor de ${variable} iria para {host} ({url}) para ver se a chave {provider} ainda funciona. Rode você mesmo, no seu terminal, com --send.",
|
|
7346
|
+
"secrets.live.contacting": "Contatando {host} ({url}) com a chave; o valor vai para o curl pela entrada padrão, nunca como argumento.",
|
|
7347
|
+
"secrets.live.live": "A chave em ${variable} ainda funciona em {host} (HTTP {status}). Revogue agora: veja o passo a passo em `finitemonkey secrets incident`.",
|
|
7348
|
+
"secrets.live.rejected": "{host} recusou a chave em ${variable} (HTTP {status}): ela não funciona mais.",
|
|
7349
|
+
"secrets.live.unknown": "{host} respondeu HTTP {status}; isso não diz se a chave em ${variable} funciona. Confira no painel do provedor.",
|
|
7350
|
+
"secrets.live.unreachable": "Não deu para alcançar {host} com o curl, então a chave em ${variable} não foi verificada.",
|
|
7351
|
+
"secrets.live.no_value": "${variable} está vazia ou não existe neste terminal; exporte antes, nada foi enviado"
|
|
7239
7352
|
};
|
|
7240
7353
|
|
|
7241
7354
|
// src/i18n/review.json
|
|
@@ -7613,6 +7726,7 @@ var review_default = {
|
|
|
7613
7726
|
"gate.guard.bypass.hooks_path": "machine",
|
|
7614
7727
|
"gate.guard.bypass.hook_env": "machine",
|
|
7615
7728
|
"gate.guard.bypass.fm_allow": "machine",
|
|
7729
|
+
"gate.guard.bypass.fm_pause": "machine",
|
|
7616
7730
|
"gate.guard.bypass.human": "machine",
|
|
7617
7731
|
"gate.guard.self.policy": "machine",
|
|
7618
7732
|
"gate.guard.self.allow_store": "machine",
|
|
@@ -7789,7 +7903,24 @@ var review_default = {
|
|
|
7789
7903
|
"gate.runtime.body_mismatch": "machine",
|
|
7790
7904
|
"gate.runtime.request_failed": "machine",
|
|
7791
7905
|
"gate.runtime.orphan": "machine",
|
|
7792
|
-
"gate.runtime.probe_failed": "machine"
|
|
7906
|
+
"gate.runtime.probe_failed": "machine",
|
|
7907
|
+
"command.off.summary": "machine",
|
|
7908
|
+
"command.on.summary": "machine",
|
|
7909
|
+
"pause.off": "machine",
|
|
7910
|
+
"pause.on": "machine",
|
|
7911
|
+
"pause.report": "machine",
|
|
7912
|
+
"pause.no_session": "machine",
|
|
7913
|
+
"pause.error.person_only": "machine",
|
|
7914
|
+
"secrets.incident.runbook": "machine",
|
|
7915
|
+
"secrets.command.live": "machine",
|
|
7916
|
+
"secrets.action.live": "machine",
|
|
7917
|
+
"secrets.live.dry": "machine",
|
|
7918
|
+
"secrets.live.contacting": "machine",
|
|
7919
|
+
"secrets.live.live": "machine",
|
|
7920
|
+
"secrets.live.rejected": "machine",
|
|
7921
|
+
"secrets.live.unknown": "machine",
|
|
7922
|
+
"secrets.live.unreachable": "machine",
|
|
7923
|
+
"secrets.live.no_value": "machine"
|
|
7793
7924
|
},
|
|
7794
7925
|
es: {
|
|
7795
7926
|
"command.init.summary": "machine",
|
|
@@ -8164,6 +8295,7 @@ var review_default = {
|
|
|
8164
8295
|
"gate.guard.bypass.hooks_path": "machine",
|
|
8165
8296
|
"gate.guard.bypass.hook_env": "machine",
|
|
8166
8297
|
"gate.guard.bypass.fm_allow": "machine",
|
|
8298
|
+
"gate.guard.bypass.fm_pause": "machine",
|
|
8167
8299
|
"gate.guard.bypass.human": "machine",
|
|
8168
8300
|
"gate.guard.self.policy": "machine",
|
|
8169
8301
|
"gate.guard.self.allow_store": "machine",
|
|
@@ -8340,7 +8472,24 @@ var review_default = {
|
|
|
8340
8472
|
"gate.runtime.body_mismatch": "machine",
|
|
8341
8473
|
"gate.runtime.request_failed": "machine",
|
|
8342
8474
|
"gate.runtime.orphan": "machine",
|
|
8343
|
-
"gate.runtime.probe_failed": "machine"
|
|
8475
|
+
"gate.runtime.probe_failed": "machine",
|
|
8476
|
+
"command.off.summary": "machine",
|
|
8477
|
+
"command.on.summary": "machine",
|
|
8478
|
+
"pause.off": "machine",
|
|
8479
|
+
"pause.on": "machine",
|
|
8480
|
+
"pause.report": "machine",
|
|
8481
|
+
"pause.no_session": "machine",
|
|
8482
|
+
"pause.error.person_only": "machine",
|
|
8483
|
+
"secrets.incident.runbook": "machine",
|
|
8484
|
+
"secrets.command.live": "machine",
|
|
8485
|
+
"secrets.action.live": "machine",
|
|
8486
|
+
"secrets.live.dry": "machine",
|
|
8487
|
+
"secrets.live.contacting": "machine",
|
|
8488
|
+
"secrets.live.live": "machine",
|
|
8489
|
+
"secrets.live.rejected": "machine",
|
|
8490
|
+
"secrets.live.unknown": "machine",
|
|
8491
|
+
"secrets.live.unreachable": "machine",
|
|
8492
|
+
"secrets.live.no_value": "machine"
|
|
8344
8493
|
}
|
|
8345
8494
|
};
|
|
8346
8495
|
|
|
@@ -8637,8 +8786,8 @@ var callersRate = (runtime, paths) => {
|
|
|
8637
8786
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
8638
8787
|
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
8639
8788
|
const event = parsed.value["event"];
|
|
8640
|
-
const
|
|
8641
|
-
if (
|
|
8789
|
+
const target2 = event === FLAGGED_EVENT ? flagged : event === RELEASED_EVENT ? released2 : void 0;
|
|
8790
|
+
if (target2 !== void 0) for (const pair of pairs(parsed.value)) target2.add(pair);
|
|
8642
8791
|
}
|
|
8643
8792
|
const total = (/* @__PURE__ */ new Set([...flagged, ...released2])).size;
|
|
8644
8793
|
const rate = total === 0 ? 0 : released2.size / total;
|
|
@@ -8660,13 +8809,13 @@ var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
|
8660
8809
|
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
8661
8810
|
var jsSpecifiers = (text6) => [...text6.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
8662
8811
|
var jsImports = (callerPath, callerText, origin) => {
|
|
8663
|
-
const
|
|
8812
|
+
const target2 = stripModule(origin);
|
|
8664
8813
|
return jsSpecifiers(callerText).some((specifier) => {
|
|
8665
8814
|
if (specifier.startsWith(".")) {
|
|
8666
|
-
return stripModule(posix2.normalize(posix2.join(posix2.dirname(callerPath), specifier))) ===
|
|
8815
|
+
return stripModule(posix2.normalize(posix2.join(posix2.dirname(callerPath), specifier))) === target2;
|
|
8667
8816
|
}
|
|
8668
8817
|
const bare = stripModule(specifier.replace(/^[@~#]\//, ""));
|
|
8669
|
-
return
|
|
8818
|
+
return target2 === bare || target2.endsWith(`/${bare}`);
|
|
8670
8819
|
});
|
|
8671
8820
|
};
|
|
8672
8821
|
var pyImports = (callerText, origin) => {
|
|
@@ -10184,13 +10333,13 @@ var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
|
10184
10333
|
var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
10185
10334
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
10186
10335
|
var binary = (text6) => text6.slice(0, 8e3).includes("\0");
|
|
10187
|
-
var listed2 = (runtime, root,
|
|
10188
|
-
const git = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--",
|
|
10336
|
+
var listed2 = (runtime, root, target2) => {
|
|
10337
|
+
const git = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
10189
10338
|
if (git.ok)
|
|
10190
10339
|
return accept(
|
|
10191
10340
|
git.stdout.split("\0").filter((name) => name !== "").map(toPosixPath)
|
|
10192
10341
|
);
|
|
10193
|
-
const base = toAbsolutePath(
|
|
10342
|
+
const base = toAbsolutePath(target2, root);
|
|
10194
10343
|
const walked = runtime.walkFiles(base);
|
|
10195
10344
|
if (!walked.ok) return walked;
|
|
10196
10345
|
return accept(walked.value.map((name) => toPosixPath(relative4(root, join20(base, toPosixPath(name))))));
|
|
@@ -10200,8 +10349,8 @@ var scanFile = (runtime, root, file) => {
|
|
|
10200
10349
|
if (!text6.ok || text6.value.length > MAX_FILE_BYTES || binary(text6.value)) return [];
|
|
10201
10350
|
return scanText(text6.value, { path: file }).map((finding3) => locate(finding3, file, finding3.line));
|
|
10202
10351
|
};
|
|
10203
|
-
var scanPath = (runtime, root,
|
|
10204
|
-
const files = listed2(runtime, root,
|
|
10352
|
+
var scanPath = (runtime, root, target2) => {
|
|
10353
|
+
const files = listed2(runtime, root, target2);
|
|
10205
10354
|
if (!files.ok) return files;
|
|
10206
10355
|
return accept(files.value.filter((file) => !SKIPPED.test(file)).flatMap((file) => scanFile(runtime, root, file)));
|
|
10207
10356
|
};
|
|
@@ -11221,7 +11370,7 @@ var integrationRefs = (runtime, root, defaultBranch) => [`refs/heads/${defaultBr
|
|
|
11221
11370
|
var contained = (runtime, root, branch, targets2) => {
|
|
11222
11371
|
const tip = line(runtime, root, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
|
|
11223
11372
|
if (tip === void 0) return true;
|
|
11224
|
-
return targets2.filter((
|
|
11373
|
+
return targets2.filter((target2) => target2 !== `refs/heads/${branch}`).some((target2) => runtime.git(["merge-base", "--is-ancestor", tip, target2], root).ok);
|
|
11225
11374
|
};
|
|
11226
11375
|
var shared = (runtime, root, upstream, base) => {
|
|
11227
11376
|
if (upstream === void 0 || !refExists(runtime, root, upstream.remoteRef)) return false;
|
|
@@ -11566,9 +11715,9 @@ var subagentStart = (input) => {
|
|
|
11566
11715
|
// src/gate/hooks/index.ts
|
|
11567
11716
|
var PORTAO_HANDLERS = {
|
|
11568
11717
|
PreToolUse: [selfGuard, bypassGuard, testsGuard],
|
|
11569
|
-
UserPromptSubmit: [promptRelease],
|
|
11570
|
-
Stop: [stopGate],
|
|
11571
|
-
SubagentStop: [subagentStopGate],
|
|
11718
|
+
UserPromptSubmit: [pausePrompt, promptRelease],
|
|
11719
|
+
Stop: [unlessPaused(stopGate)],
|
|
11720
|
+
SubagentStop: [unlessPaused(subagentStopGate)],
|
|
11572
11721
|
SubagentStart: [subagentStart],
|
|
11573
11722
|
PreCompact: [preCompact],
|
|
11574
11723
|
SessionStart: [sessionStart],
|
|
@@ -11970,7 +12119,7 @@ var loadProdTargets = (runtime, root, config) => {
|
|
|
11970
12119
|
var isProdContext = (name, declared) => declared.includes(name) || /(?:^|[-_.:/])(?:prod|production)(?:$|[-_.:/])/i.test(name);
|
|
11971
12120
|
|
|
11972
12121
|
// src/prod/classify.ts
|
|
11973
|
-
var hit2 = (effect,
|
|
12122
|
+
var hit2 = (effect, target2, tool) => ({ effect, target: target2, tool });
|
|
11974
12123
|
var sshHit = (command, isProd) => {
|
|
11975
12124
|
const call = parseSsh(command);
|
|
11976
12125
|
if (call === void 0 || !isProd(call.destination)) return void 0;
|
|
@@ -12006,9 +12155,9 @@ var dockerHit = (command, targets2, isProd) => {
|
|
|
12006
12155
|
return hit2("effect", switching, "docker context");
|
|
12007
12156
|
const prodHost = remote !== void 0 && isProd(remote) ? hostKey(remote) : void 0;
|
|
12008
12157
|
const prodContext = context !== void 0 && isProdContext(context, targets2.dockerContexts) ? context : void 0;
|
|
12009
|
-
const
|
|
12010
|
-
if (
|
|
12011
|
-
return hit2(dockerReadOnly(command.args) ? "read" : "effect",
|
|
12158
|
+
const target2 = prodHost ?? prodContext;
|
|
12159
|
+
if (target2 === void 0) return void 0;
|
|
12160
|
+
return hit2(dockerReadOnly(command.args) ? "read" : "effect", target2, "docker");
|
|
12012
12161
|
};
|
|
12013
12162
|
var kubeHit = (command, targets2) => {
|
|
12014
12163
|
const context = valueAfter(command.args, "--context", "--kube-context");
|
|
@@ -12693,8 +12842,8 @@ var pushDestinations = (parsed, facts) => {
|
|
|
12693
12842
|
return [facts.upstream(current2)?.branch ?? current2];
|
|
12694
12843
|
}
|
|
12695
12844
|
return refspecs.flatMap((refspec) => {
|
|
12696
|
-
const
|
|
12697
|
-
return
|
|
12845
|
+
const target2 = refspecTarget(refspec, current2);
|
|
12846
|
+
return target2 === void 0 ? [] : [target2];
|
|
12698
12847
|
});
|
|
12699
12848
|
};
|
|
12700
12849
|
var leaseVerdict = (parsed, facts) => {
|
|
@@ -12874,8 +13023,8 @@ var guardGitCommand = (context, command, depth = 0) => {
|
|
|
12874
13023
|
if (repo === void 0) return [];
|
|
12875
13024
|
const facts = withChain(context.facts?.(repo) ?? repoFacts(context.runtime, repo), context.chain);
|
|
12876
13025
|
const expanded = expandAlias(context.runtime, repo, invocation);
|
|
12877
|
-
const
|
|
12878
|
-
if (
|
|
13026
|
+
const target2 = switchTarget(expanded, facts);
|
|
13027
|
+
if (target2 !== void 0 && context.chain !== void 0) context.chain.branch = target2;
|
|
12879
13028
|
const nested = depth < MAX_ALIAS_DEPTH ? shellAliasCommands(expanded, context.runtime, repo).flatMap(
|
|
12880
13029
|
(inner) => guardGitCommand(context, inner, depth + 1)
|
|
12881
13030
|
) : [];
|
|
@@ -12912,11 +13061,11 @@ var removalTargets = (command) => {
|
|
|
12912
13061
|
if (command.program === "remove-item") return powershellTargets(command);
|
|
12913
13062
|
return void 0;
|
|
12914
13063
|
};
|
|
12915
|
-
var globBase = (
|
|
12916
|
-
const at =
|
|
12917
|
-
if (at < 0) return
|
|
12918
|
-
const slash =
|
|
12919
|
-
return slash < 0 ? "." :
|
|
13064
|
+
var globBase = (target2) => {
|
|
13065
|
+
const at = target2.search(GLOB2);
|
|
13066
|
+
if (at < 0) return target2;
|
|
13067
|
+
const slash = target2.lastIndexOf("/", at);
|
|
13068
|
+
return slash < 0 ? "." : target2.slice(0, slash) || "/";
|
|
12920
13069
|
};
|
|
12921
13070
|
var containsGitDir = (runtime, path) => runtime.stat(toAbsolutePath(resolveFrom(path, ".git"))) !== void 0;
|
|
12922
13071
|
var targetVerdict = (runtime, context, command, raw) => {
|
|
@@ -12936,8 +13085,8 @@ var targetVerdict = (runtime, context, command, raw) => {
|
|
|
12936
13085
|
var rmVerdict = (runtime, context, command) => {
|
|
12937
13086
|
const targets2 = removalTargets(command);
|
|
12938
13087
|
if (targets2 === void 0) return PASS;
|
|
12939
|
-
for (const
|
|
12940
|
-
const verdict = targetVerdict(runtime, context, command,
|
|
13088
|
+
for (const target2 of targets2) {
|
|
13089
|
+
const verdict = targetVerdict(runtime, context, command, target2);
|
|
12941
13090
|
if (verdict.kind === "block") return verdict;
|
|
12942
13091
|
}
|
|
12943
13092
|
return PASS;
|
|
@@ -13000,7 +13149,7 @@ var appendJournal = (runtime, root, entry2) => {
|
|
|
13000
13149
|
|
|
13001
13150
|
// src/git/snapshot.ts
|
|
13002
13151
|
var BACKUP_PREFIX = "refs/finitemonkey/backup/";
|
|
13003
|
-
var DEFAULT_SNAPSHOT_TIMEOUT_MS =
|
|
13152
|
+
var DEFAULT_SNAPSHOT_TIMEOUT_MS = 5e3;
|
|
13004
13153
|
var stampOf = (iso) => iso.replace(/[:.]/g, "-");
|
|
13005
13154
|
var slug = (op) => op.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "manual";
|
|
13006
13155
|
var optionalLine = (runtime, repo, args2) => {
|
|
@@ -13289,6 +13438,7 @@ var recordShadow = (input, paths, set, policy, reason) => {
|
|
|
13289
13438
|
var governed = (set, handler) => async (input) => {
|
|
13290
13439
|
const paths = projectPaths(input);
|
|
13291
13440
|
if (paths === void 0) return handler(input);
|
|
13441
|
+
if (pausedFor(input.runtime, paths, input.payload.sessionId) !== void 0) return { kind: "allow" };
|
|
13292
13442
|
const policy = loadPolicy(input.runtime, paths.root);
|
|
13293
13443
|
const mode = modeOf2(set, policy, readShadow(input.runtime, paths));
|
|
13294
13444
|
if (mode === "off") return { kind: "allow" };
|
|
@@ -13474,12 +13624,12 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
13474
13624
|
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text6) => text6 !== void 0);
|
|
13475
13625
|
return { kind: "inline", text: piped ?? "" };
|
|
13476
13626
|
};
|
|
13477
|
-
var collect3 = (
|
|
13627
|
+
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
13478
13628
|
const values = flagValues(args2, spec2);
|
|
13479
13629
|
if (values.length === 0) return void 0;
|
|
13480
13630
|
const title = values.find((item) => spec2.title.includes(item.flag));
|
|
13481
13631
|
const bodies = values.filter((item) => !spec2.title.includes(item.flag)).map((item) => sourceOf(item.value, spec2.file.includes(item.flag), segment, heredocs));
|
|
13482
|
-
return { target, title: title === void 0 ? void 0 : sourceOf(title.value, false, segment, heredocs), bodies };
|
|
13632
|
+
return { target: target2, title: title === void 0 ? void 0 : sourceOf(title.value, false, segment, heredocs), bodies };
|
|
13483
13633
|
};
|
|
13484
13634
|
var gitSubcommand = (args2) => {
|
|
13485
13635
|
let index = 0;
|
|
@@ -13698,7 +13848,7 @@ var READERS = /* @__PURE__ */ new Set([
|
|
|
13698
13848
|
"dotenv"
|
|
13699
13849
|
]);
|
|
13700
13850
|
var SECRET_NAME = /(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL)/i;
|
|
13701
|
-
var PERSON_ONLY = /\b(?:finitemonkey|fm)(?:\.mjs)?\s+secrets\s+(?:accept\b|incident\s+(?:rotated|close)\b)/;
|
|
13851
|
+
var PERSON_ONLY = /\b(?:finitemonkey|fm)(?:\.mjs)?\s+secrets\s+(?:accept\b|incident\s+(?:rotated|close)\b|live\b[^;&|\n]*\s--send\b)/;
|
|
13702
13852
|
var LISTS_ENV = /* @__PURE__ */ new Set(["get-childitem", "gci", "dir", "ls"]);
|
|
13703
13853
|
var onlyFlags = (args2) => args2.every((arg) => arg.startsWith("-"));
|
|
13704
13854
|
var readTarget = (segment) => {
|
|
@@ -13724,8 +13874,8 @@ var bashFindings = (command, segments) => {
|
|
|
13724
13874
|
const person = PERSON_ONLY.exec(command);
|
|
13725
13875
|
if (person !== null) return [{ kind: "person-only", command: person[0] }];
|
|
13726
13876
|
return segments.flatMap((segment) => {
|
|
13727
|
-
const
|
|
13728
|
-
if (
|
|
13877
|
+
const target2 = readTarget(segment);
|
|
13878
|
+
if (target2 !== void 0) return [{ kind: "read", target: target2 }];
|
|
13729
13879
|
if (dumpsEnv(segment)) return [{ kind: "env-dump", command: [segment.program, ...segment.args].join(" ") }];
|
|
13730
13880
|
return [];
|
|
13731
13881
|
});
|
|
@@ -13816,9 +13966,9 @@ var bashDecision = (input) => {
|
|
|
13816
13966
|
};
|
|
13817
13967
|
var readDecision = (input) => {
|
|
13818
13968
|
const inputs = toolInput2(input.payload);
|
|
13819
|
-
const
|
|
13820
|
-
if (
|
|
13821
|
-
return { kind: "block", reason: input.t("secrets.block.read", { target: relativeFile(input.cwd,
|
|
13969
|
+
const target2 = optionalString(inputs, "file_path") ?? optionalString(inputs, "path") ?? "";
|
|
13970
|
+
if (target2 === "" || !isSensitivePath(target2)) return ALLOW5;
|
|
13971
|
+
return { kind: "block", reason: input.t("secrets.block.read", { target: relativeFile(input.cwd, target2) }) };
|
|
13822
13972
|
};
|
|
13823
13973
|
var WRITERS2 = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
13824
13974
|
var SHELLS4 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
@@ -16081,6 +16231,35 @@ var initCommand = {
|
|
|
16081
16231
|
}
|
|
16082
16232
|
};
|
|
16083
16233
|
|
|
16234
|
+
// src/commands/pause.ts
|
|
16235
|
+
var toggle = (ctx, off) => {
|
|
16236
|
+
const command = off ? "finitemonkey off" : "finitemonkey on";
|
|
16237
|
+
if (!isPerson(ctx)) return failureFrom(ctx.t, needsPersonProblem("pause.error.person_only", command));
|
|
16238
|
+
const paths = fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd);
|
|
16239
|
+
if (!off) {
|
|
16240
|
+
const ended = clearPause(ctx.runtime, paths, "terminal");
|
|
16241
|
+
return success({ paused: false, sessionId: ended?.sessionId ?? null }, [ctx.t("pause.on")]);
|
|
16242
|
+
}
|
|
16243
|
+
const session = readSession(ctx.runtime, paths);
|
|
16244
|
+
if (session === void 0) return success({ paused: false, sessionId: null }, [ctx.t("pause.no_session")]);
|
|
16245
|
+
setPause(ctx.runtime, paths, session.sessionId, "terminal");
|
|
16246
|
+
return success({ paused: true, sessionId: session.sessionId }, [ctx.t("pause.off")]);
|
|
16247
|
+
};
|
|
16248
|
+
var offCommand = {
|
|
16249
|
+
path: ["off"],
|
|
16250
|
+
summaryKey: "command.off.summary",
|
|
16251
|
+
usage: "finitemonkey off",
|
|
16252
|
+
status: "ready",
|
|
16253
|
+
run: (ctx) => Promise.resolve(toggle(ctx, true))
|
|
16254
|
+
};
|
|
16255
|
+
var onCommand = {
|
|
16256
|
+
path: ["on"],
|
|
16257
|
+
summaryKey: "command.on.summary",
|
|
16258
|
+
usage: "finitemonkey on",
|
|
16259
|
+
status: "ready",
|
|
16260
|
+
run: (ctx) => Promise.resolve(toggle(ctx, false))
|
|
16261
|
+
};
|
|
16262
|
+
|
|
16084
16263
|
// src/commands/policy.ts
|
|
16085
16264
|
var FIX = `finitemonkey policy false-positive <${OPTIONAL_GUARDS.join("|")}>`;
|
|
16086
16265
|
var recordFalsePositive = (ctx, guard) => {
|
|
@@ -16169,15 +16348,15 @@ var problem2 = (code, messageKey, params2) => ({
|
|
|
16169
16348
|
exitCode: ExitCode.Usage
|
|
16170
16349
|
});
|
|
16171
16350
|
var underRoot = (root, path) => toPosixPath(relative8(root, path));
|
|
16172
|
-
var expand2 = (ctx, root,
|
|
16173
|
-
const path = toAbsolutePath(
|
|
16351
|
+
var expand2 = (ctx, root, target2) => {
|
|
16352
|
+
const path = toAbsolutePath(target2, ctx.cwd);
|
|
16174
16353
|
const stat = ctx.runtime.stat(path);
|
|
16175
16354
|
if (stat === void 0)
|
|
16176
|
-
return reject(problem2("prose_unreadable", "prose.error.unreadable", { file:
|
|
16355
|
+
return reject(problem2("prose_unreadable", "prose.error.unreadable", { file: target2, detail: "not found" }));
|
|
16177
16356
|
if (stat.kind === "file") return accept([underRoot(root, path)]);
|
|
16178
16357
|
const walked = ctx.runtime.walkFiles(path);
|
|
16179
16358
|
if (!walked.ok)
|
|
16180
|
-
return reject(problem2("prose_unreadable", "prose.error.unreadable", { file:
|
|
16359
|
+
return reject(problem2("prose_unreadable", "prose.error.unreadable", { file: target2, detail: walked.error }));
|
|
16181
16360
|
return accept(
|
|
16182
16361
|
walked.value.map((name) => underRoot(root, toAbsolutePath(toPosixPath(name), path))).filter((name) => TEXT_FILE.test(name) && !/(?:^|\/)(?:node_modules|\.git)\//.test(name)).sort()
|
|
16183
16362
|
);
|
|
@@ -16361,6 +16540,33 @@ var secretsAcceptCommand = {
|
|
|
16361
16540
|
}
|
|
16362
16541
|
};
|
|
16363
16542
|
|
|
16543
|
+
// src/secrets/runbooks.ts
|
|
16544
|
+
import { dirname as dirname9, join as join39 } from "node:path";
|
|
16545
|
+
var PROVIDER_OF_RULE = {
|
|
16546
|
+
"aws-access-token": "aws",
|
|
16547
|
+
"aws-secret-access-key": "aws",
|
|
16548
|
+
"github-pat": "github",
|
|
16549
|
+
"github-oauth": "github",
|
|
16550
|
+
"github-app-token": "github",
|
|
16551
|
+
"github-refresh-token": "github",
|
|
16552
|
+
"github-fine-grained-pat": "github",
|
|
16553
|
+
"stripe-access-token": "stripe",
|
|
16554
|
+
"anthropic-api-key": "anthropic",
|
|
16555
|
+
"openai-api-key": "openai",
|
|
16556
|
+
"gcp-api-key": "google",
|
|
16557
|
+
"asaas-api-key": "asaas",
|
|
16558
|
+
"credential-in-url": "database",
|
|
16559
|
+
"password-assignment": "database",
|
|
16560
|
+
"secret-in-process-args": "database"
|
|
16561
|
+
};
|
|
16562
|
+
var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/runbooks";
|
|
16563
|
+
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
16564
|
+
var runbookLink = (runtime, rule) => {
|
|
16565
|
+
const file = `${runbookProvider(rule)}.md`;
|
|
16566
|
+
const local = toAbsolutePath(join39(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
16567
|
+
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
16568
|
+
};
|
|
16569
|
+
|
|
16364
16570
|
// src/commands/secrets-incident.ts
|
|
16365
16571
|
var INCIDENT_ACTIONS = ["list", "rotated", "close", "purge"];
|
|
16366
16572
|
var USAGE = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
|
|
@@ -16386,9 +16592,19 @@ var itemLine = (ctx, incident) => ctx.t("secrets.incident.item", {
|
|
|
16386
16592
|
where: incident.where,
|
|
16387
16593
|
created: incident.createdAt.slice(0, 10)
|
|
16388
16594
|
});
|
|
16595
|
+
var itemLines2 = (ctx, incident) => [
|
|
16596
|
+
itemLine(ctx, incident),
|
|
16597
|
+
ctx.t("secrets.incident.runbook", { link: incident.runbook })
|
|
16598
|
+
];
|
|
16389
16599
|
var list2 = (ctx, root) => {
|
|
16390
|
-
const incidents = listIncidents(ctx.runtime, root)
|
|
16391
|
-
|
|
16600
|
+
const incidents = listIncidents(ctx.runtime, root).map((incident) => ({
|
|
16601
|
+
...incident,
|
|
16602
|
+
runbook: runbookLink(ctx.runtime, incident.rule)
|
|
16603
|
+
}));
|
|
16604
|
+
const human2 = incidents.length === 0 ? [ctx.t("secrets.incident.none")] : [
|
|
16605
|
+
ctx.t("secrets.incident.title", { count: incidents.length }),
|
|
16606
|
+
...incidents.flatMap((item) => itemLines2(ctx, item))
|
|
16607
|
+
];
|
|
16392
16608
|
return success({ schema: "finitemonkey/secrets-incidents@1", incidents }, human2);
|
|
16393
16609
|
};
|
|
16394
16610
|
var notRotated = (ctx, incident) => fail2(ctx, {
|
|
@@ -16455,6 +16671,133 @@ var secretsIncidentCommand = {
|
|
|
16455
16671
|
}
|
|
16456
16672
|
};
|
|
16457
16673
|
|
|
16674
|
+
// src/secrets/live.ts
|
|
16675
|
+
var bearer = (key) => [`Authorization: Bearer ${key}`];
|
|
16676
|
+
var LIVE_PROBES = {
|
|
16677
|
+
github: {
|
|
16678
|
+
host: "api.github.com",
|
|
16679
|
+
path: "/user",
|
|
16680
|
+
headers: (key) => [...bearer(key), "User-Agent: finitemonkey"]
|
|
16681
|
+
},
|
|
16682
|
+
stripe: { host: "api.stripe.com", path: "/v1/balance", headers: bearer },
|
|
16683
|
+
openai: { host: "api.openai.com", path: "/v1/models", headers: bearer },
|
|
16684
|
+
anthropic: {
|
|
16685
|
+
host: "api.anthropic.com",
|
|
16686
|
+
path: "/v1/models",
|
|
16687
|
+
headers: (key) => [`x-api-key: ${key}`, "anthropic-version: 2023-06-01"]
|
|
16688
|
+
},
|
|
16689
|
+
digitalocean: { host: "api.digitalocean.com", path: "/v2/account", headers: bearer },
|
|
16690
|
+
cloudflare: { host: "api.cloudflare.com", path: "/client/v4/user/tokens/verify", headers: bearer },
|
|
16691
|
+
asaas: {
|
|
16692
|
+
host: "api.asaas.com",
|
|
16693
|
+
path: "/v3/finance/balance",
|
|
16694
|
+
headers: (key) => [`access_token: ${key}`, "User-Agent: finitemonkey"]
|
|
16695
|
+
}
|
|
16696
|
+
};
|
|
16697
|
+
var LIVE_PROVIDERS = Object.keys(LIVE_PROBES).filter((name) => name in LIVE_PROBES);
|
|
16698
|
+
var LIVE_HOSTS = LIVE_PROVIDERS.map((name) => LIVE_PROBES[name].host);
|
|
16699
|
+
var liveProvider = (name) => LIVE_PROVIDERS.find((provider) => provider === name);
|
|
16700
|
+
var LIVE_TIMEOUT_MS = 15e3;
|
|
16701
|
+
var liveRequest = (provider, key, cwd) => {
|
|
16702
|
+
const probe = LIVE_PROBES[provider];
|
|
16703
|
+
return {
|
|
16704
|
+
command: "curl",
|
|
16705
|
+
args: [
|
|
16706
|
+
"--silent",
|
|
16707
|
+
"--show-error",
|
|
16708
|
+
"--proto",
|
|
16709
|
+
"=https",
|
|
16710
|
+
"--max-time",
|
|
16711
|
+
"10",
|
|
16712
|
+
"--header",
|
|
16713
|
+
"@-",
|
|
16714
|
+
"--write-out",
|
|
16715
|
+
"\n%{http_code}",
|
|
16716
|
+
`https://${probe.host}${probe.path}`
|
|
16717
|
+
],
|
|
16718
|
+
cwd,
|
|
16719
|
+
timeoutMs: LIVE_TIMEOUT_MS,
|
|
16720
|
+
input: `${probe.headers(key).join("\n")}
|
|
16721
|
+
`
|
|
16722
|
+
};
|
|
16723
|
+
};
|
|
16724
|
+
var liveOutcome = (result) => {
|
|
16725
|
+
if (!result.found || result.exitCode !== 0) return { verdict: "unreachable" };
|
|
16726
|
+
const status = Number.parseInt(result.stdout.trimEnd().split("\n").at(-1) ?? "", 10);
|
|
16727
|
+
if (!Number.isInteger(status) || status === 0) return { verdict: "unreachable" };
|
|
16728
|
+
if (status >= 200 && status < 300) return { verdict: "live", status };
|
|
16729
|
+
if (status === 401 || status === 403) return { verdict: "rejected", status };
|
|
16730
|
+
return { verdict: "unknown", status };
|
|
16731
|
+
};
|
|
16732
|
+
|
|
16733
|
+
// src/commands/secrets-live.ts
|
|
16734
|
+
var SECRETS_LIVE_SCHEMA = "finitemonkey/secrets-live@1";
|
|
16735
|
+
var USAGE2 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
|
|
16736
|
+
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16737
|
+
var target = (provider) => {
|
|
16738
|
+
const probe = LIVE_PROBES[provider];
|
|
16739
|
+
return { host: probe.host, url: `https://${probe.host}${probe.path}` };
|
|
16740
|
+
};
|
|
16741
|
+
var dryRun = (ctx, provider, variable) => {
|
|
16742
|
+
const { host, url: url2 } = target(provider);
|
|
16743
|
+
const data = { schema: SECRETS_LIVE_SCHEMA, provider, host, url: url2, variable, sent: false };
|
|
16744
|
+
return success(data, [ctx.t("secrets.live.dry", { host, url: url2, variable, provider })]);
|
|
16745
|
+
};
|
|
16746
|
+
var send = (ctx, provider, variable, key) => {
|
|
16747
|
+
const { host, url: url2 } = target(provider);
|
|
16748
|
+
ctx.io.stderr.write(`${ctx.t("secrets.live.contacting", { host, url: url2 })}
|
|
16749
|
+
`);
|
|
16750
|
+
const outcome2 = liveOutcome(ctx.runtime.exec(liveRequest(provider, key, ctx.cwd)));
|
|
16751
|
+
const data = { schema: SECRETS_LIVE_SCHEMA, provider, host, url: url2, variable, sent: true, ...outcome2 };
|
|
16752
|
+
const line2 = ctx.t(`secrets.live.${outcome2.verdict}`, { host, status: outcome2.status ?? 0, variable });
|
|
16753
|
+
if (outcome2.verdict !== "unreachable") return success(data, [line2]);
|
|
16754
|
+
return failureFrom(ctx.t, {
|
|
16755
|
+
code: "secrets_live_unreachable",
|
|
16756
|
+
messageKey: "secrets.live.unreachable",
|
|
16757
|
+
params: { host, status: 0, variable },
|
|
16758
|
+
fix: "install curl or check the network, then run the command again",
|
|
16759
|
+
exitCode: ExitCode.CheckFailed,
|
|
16760
|
+
data
|
|
16761
|
+
});
|
|
16762
|
+
};
|
|
16763
|
+
var secretsLiveCommand = {
|
|
16764
|
+
path: ["secrets", "live"],
|
|
16765
|
+
summaryKey: "secrets.command.live",
|
|
16766
|
+
usage: USAGE2,
|
|
16767
|
+
status: "ready",
|
|
16768
|
+
maxPositionals: 1,
|
|
16769
|
+
options: { env: { type: "string" }, send: { type: "boolean" } },
|
|
16770
|
+
run: (ctx, args2) => {
|
|
16771
|
+
const provider = liveProvider(args2.positionals[0]);
|
|
16772
|
+
const variable = stringOption(args2.values, "env") ?? "";
|
|
16773
|
+
if (provider === void 0 || !ENV_NAME.test(variable))
|
|
16774
|
+
return Promise.resolve(
|
|
16775
|
+
failureFrom(
|
|
16776
|
+
ctx.t,
|
|
16777
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", USAGE2, {
|
|
16778
|
+
detail: provider === void 0 ? args2.positionals[0] ?? "<provider>" : "--env <VARIABLE>"
|
|
16779
|
+
})
|
|
16780
|
+
)
|
|
16781
|
+
);
|
|
16782
|
+
if (args2.values["send"] !== true) return Promise.resolve(dryRun(ctx, provider, variable));
|
|
16783
|
+
if (!isPerson(ctx))
|
|
16784
|
+
return Promise.resolve(
|
|
16785
|
+
failureFrom(
|
|
16786
|
+
ctx.t,
|
|
16787
|
+
needsPersonProblem("secrets.error.person_only", `${USAGE2.replace(" [--send]", "")} --send`, {
|
|
16788
|
+
action: ctx.t("secrets.action.live")
|
|
16789
|
+
})
|
|
16790
|
+
)
|
|
16791
|
+
);
|
|
16792
|
+
const key = ctx.env[variable] ?? "";
|
|
16793
|
+
if (key === "")
|
|
16794
|
+
return Promise.resolve(
|
|
16795
|
+
failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE2, { variable }))
|
|
16796
|
+
);
|
|
16797
|
+
return Promise.resolve(send(ctx, provider, variable, key));
|
|
16798
|
+
}
|
|
16799
|
+
};
|
|
16800
|
+
|
|
16458
16801
|
// src/commands/secrets-scan.ts
|
|
16459
16802
|
var SECRETS_SCAN_SCHEMA = "finitemonkey/secrets-scan@1";
|
|
16460
16803
|
var view = (finding3, status) => ({
|
|
@@ -16475,15 +16818,15 @@ var modeOf3 = (values) => {
|
|
|
16475
16818
|
if (chosen[0] === true) return "staged";
|
|
16476
16819
|
return chosen[1] === true ? "history" : "path";
|
|
16477
16820
|
};
|
|
16478
|
-
var normalizedTarget = (
|
|
16479
|
-
var scopeOf = (
|
|
16480
|
-
const prefix = normalizedTarget(
|
|
16821
|
+
var normalizedTarget = (target2) => target2.replace(/\\/g, "/").replace(/^\.\/?/, "").replace(/\/$/, "");
|
|
16822
|
+
var scopeOf = (target2) => {
|
|
16823
|
+
const prefix = normalizedTarget(target2);
|
|
16481
16824
|
return (file) => prefix === "" || file === prefix || file.startsWith(`${prefix}/`);
|
|
16482
16825
|
};
|
|
16483
|
-
var runScan = async (ctx, root, mode,
|
|
16826
|
+
var runScan = async (ctx, root, mode, target2) => {
|
|
16484
16827
|
if (mode === "staged") return scanStaged(ctx.runtime, root);
|
|
16485
16828
|
if (mode === "history") return scanHistory(ctx.runtime, root);
|
|
16486
|
-
return scanPath(ctx.runtime, root, normalizedTarget(
|
|
16829
|
+
return scanPath(ctx.runtime, root, normalizedTarget(target2) || ".");
|
|
16487
16830
|
};
|
|
16488
16831
|
var historyIncidents = (ctx, root, fresh3) => fresh3.flatMap((finding3) => {
|
|
16489
16832
|
if (ruleById(finding3.ruleId)?.kind !== "credential") return [];
|
|
@@ -16564,8 +16907,8 @@ var secretsScanCommand = {
|
|
|
16564
16907
|
const fix = "finitemonkey help secrets scan";
|
|
16565
16908
|
if (mode === void 0) return failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.error.one_mode", fix));
|
|
16566
16909
|
const root = projectRoot(ctx);
|
|
16567
|
-
const
|
|
16568
|
-
const scanned = await runScan(ctx, root, mode,
|
|
16910
|
+
const target2 = stringOption(args2.values, "path") ?? ".";
|
|
16911
|
+
const scanned = await runScan(ctx, root, mode, target2);
|
|
16569
16912
|
if (!scanned.ok) return failureFrom(ctx.t, gitUnavailable(scanned.error));
|
|
16570
16913
|
const baseline = loadBaseline(ctx.runtime, root);
|
|
16571
16914
|
if (!baseline.ok) return failureFrom(ctx.t, baselineInvalid(baseline.error));
|
|
@@ -16573,7 +16916,7 @@ var secretsScanCommand = {
|
|
|
16573
16916
|
scanned.value,
|
|
16574
16917
|
baseline.value,
|
|
16575
16918
|
ctx.runtime.now(),
|
|
16576
|
-
mode === "path" ? scopeOf(
|
|
16919
|
+
mode === "path" ? scopeOf(target2) : void 0
|
|
16577
16920
|
);
|
|
16578
16921
|
if (mode === "path") prune(ctx, root, baseline.value, outcome2);
|
|
16579
16922
|
const incidents = mode === "history" ? historyIncidents(ctx, root, outcome2.fresh) : [];
|
|
@@ -16582,7 +16925,7 @@ var secretsScanCommand = {
|
|
|
16582
16925
|
};
|
|
16583
16926
|
|
|
16584
16927
|
// src/task/create.ts
|
|
16585
|
-
import { join as
|
|
16928
|
+
import { join as join40 } from "node:path";
|
|
16586
16929
|
|
|
16587
16930
|
// src/core/yaml/write.ts
|
|
16588
16931
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -16671,10 +17014,10 @@ var explicitChecks = (draft) => [
|
|
|
16671
17014
|
var draftChecks = (runtime, paths, draft) => {
|
|
16672
17015
|
const disk = {
|
|
16673
17016
|
read: (relative9) => {
|
|
16674
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
17017
|
+
const text6 = runtime.readText(toAbsolutePath(join40(paths.root, relative9)));
|
|
16675
17018
|
return text6.ok ? text6.value : void 0;
|
|
16676
17019
|
},
|
|
16677
|
-
exists: (relative9) => runtime.stat(toAbsolutePath(
|
|
17020
|
+
exists: (relative9) => runtime.stat(toAbsolutePath(join40(paths.root, relative9))) !== void 0
|
|
16678
17021
|
};
|
|
16679
17022
|
const detected = detectChecks(disk);
|
|
16680
17023
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -16804,8 +17147,8 @@ var showTask = (ctx, id) => {
|
|
|
16804
17147
|
const paths = projectOf(ctx);
|
|
16805
17148
|
if (paths === void 0) return noRepo(ctx);
|
|
16806
17149
|
const active = readState(ctx.runtime, paths).active;
|
|
16807
|
-
const
|
|
16808
|
-
const report2 = currentReport(ctx.runtime, paths, void 0,
|
|
17150
|
+
const target2 = id ?? active;
|
|
17151
|
+
const report2 = currentReport(ctx.runtime, paths, void 0, target2);
|
|
16809
17152
|
if (report2 === void 0) {
|
|
16810
17153
|
return failureFrom(
|
|
16811
17154
|
ctx.t,
|
|
@@ -16813,7 +17156,7 @@ var showTask = (ctx, id) => {
|
|
|
16813
17156
|
"task_not_found",
|
|
16814
17157
|
"error.task_not_found",
|
|
16815
17158
|
"finitemonkey task new <id> --goal <text>",
|
|
16816
|
-
{ task:
|
|
17159
|
+
{ task: target2 ?? "-" },
|
|
16817
17160
|
ExitCode.NotConfigured
|
|
16818
17161
|
)
|
|
16819
17162
|
);
|
|
@@ -16823,7 +17166,7 @@ var showTask = (ctx, id) => {
|
|
|
16823
17166
|
var doneTask = (ctx, id) => {
|
|
16824
17167
|
const paths = projectOf(ctx);
|
|
16825
17168
|
if (paths === void 0) return noRepo(ctx);
|
|
16826
|
-
const
|
|
17169
|
+
const target2 = id ?? readState(ctx.runtime, paths).active;
|
|
16827
17170
|
const run4 = runGate({
|
|
16828
17171
|
runtime: ctx.runtime,
|
|
16829
17172
|
cwd: ctx.cwd,
|
|
@@ -16832,7 +17175,7 @@ var doneTask = (ctx, id) => {
|
|
|
16832
17175
|
budgetMs: DEFAULT_GATE_BUDGET_MS,
|
|
16833
17176
|
fast: false,
|
|
16834
17177
|
noSkip: true,
|
|
16835
|
-
...
|
|
17178
|
+
...target2 === void 0 ? {} : { taskId: target2 }
|
|
16836
17179
|
});
|
|
16837
17180
|
const result = runResult(ctx, run4, true);
|
|
16838
17181
|
if (!result.ok || run4.kind !== "done" || run4.report.implicit) return result;
|
|
@@ -16924,9 +17267,9 @@ var isEmptyDir = (project, relative9) => {
|
|
|
16924
17267
|
return listed4.ok && listed4.value.length === 0;
|
|
16925
17268
|
};
|
|
16926
17269
|
var finish3 = (project, plan) => {
|
|
16927
|
-
const
|
|
16928
|
-
const removed = project.runtime.remove(project.abs(
|
|
16929
|
-
if (!removed.ok) return reject({ path:
|
|
17270
|
+
const target2 = plan.purge ? STATE_DIR : LOCK_FILE;
|
|
17271
|
+
const removed = project.runtime.remove(project.abs(target2));
|
|
17272
|
+
if (!removed.ok) return reject({ path: target2, detail: removed.error });
|
|
16930
17273
|
for (const dir of plan.removeDirs) if (isEmptyDir(project, dir)) project.runtime.remove(project.abs(dir));
|
|
16931
17274
|
return accept(void 0);
|
|
16932
17275
|
};
|
|
@@ -17069,7 +17412,10 @@ var REGISTRY = [
|
|
|
17069
17412
|
secretsScanCommand,
|
|
17070
17413
|
secretsAcceptCommand,
|
|
17071
17414
|
policyFalsePositiveCommand,
|
|
17415
|
+
offCommand,
|
|
17416
|
+
onCommand,
|
|
17072
17417
|
secretsIncidentCommand,
|
|
17418
|
+
secretsLiveCommand,
|
|
17073
17419
|
proseCheckCommand,
|
|
17074
17420
|
snapshotCommand,
|
|
17075
17421
|
undoCommand,
|
|
@@ -17081,7 +17427,7 @@ var REGISTRY = [
|
|
|
17081
17427
|
];
|
|
17082
17428
|
|
|
17083
17429
|
// src/i18n/lang-settings.ts
|
|
17084
|
-
import { join as
|
|
17430
|
+
import { join as join41 } from "node:path";
|
|
17085
17431
|
var read3 = (runtime, path) => {
|
|
17086
17432
|
const text6 = runtime.readText(toAbsolutePath(path));
|
|
17087
17433
|
return text6.ok ? text6.value : void 0;
|
|
@@ -17096,14 +17442,14 @@ var languageIn = (text6) => {
|
|
|
17096
17442
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
17097
17443
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
17098
17444
|
};
|
|
17099
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
17445
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join41(runtime.homeDir(), ".claude");
|
|
17100
17446
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
17101
17447
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
17102
|
-
const config = parseConfigYaml(read3(runtime,
|
|
17448
|
+
const config = parseConfigYaml(read3(runtime, join41(root, CONFIG_FILE)) ?? "");
|
|
17103
17449
|
const claudeFiles = [
|
|
17104
|
-
|
|
17105
|
-
|
|
17106
|
-
|
|
17450
|
+
join41(root, ".claude", "settings.local.json"),
|
|
17451
|
+
join41(root, ".claude", "settings.json"),
|
|
17452
|
+
join41(claudeDir(runtime, env), "settings.json")
|
|
17107
17453
|
];
|
|
17108
17454
|
return {
|
|
17109
17455
|
config: configText(config, "lang"),
|