@counterfact-labs/cli 0.1.1 → 0.1.2
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/commands/check.js +4 -2
- package/dist/commands/login.js +2 -1
- package/dist/files.js +2 -2
- package/dist/mcp.js +12 -4
- package/dist/secret-files.js +4 -1
- package/package.json +1 -1
package/dist/commands/check.js
CHANGED
|
@@ -35,9 +35,11 @@ export async function prepareCheck(path, ctx) {
|
|
|
35
35
|
throw new Error("No agent files were found in this folder.");
|
|
36
36
|
ctx.write(fileText(path, wanted));
|
|
37
37
|
const upload = await readUpload(root, files, plan.wanted);
|
|
38
|
-
const
|
|
38
|
+
const created = await ctx.api.request("checks", { ...facts, files: upload });
|
|
39
|
+
ctx.write(`Follow it in the app: ${created.url}\n`);
|
|
40
|
+
const response = await awaitQuote(created, ctx);
|
|
39
41
|
ctx.write(quoteText(response.quote));
|
|
40
|
-
return { root, response, files: upload };
|
|
42
|
+
return { root, response, files: upload, url: created.url };
|
|
41
43
|
}
|
|
42
44
|
/** A confirmed check can keep running and charge while the connection is down. */
|
|
43
45
|
async function pollConfirmed(id, ctx) {
|
package/dist/commands/login.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { saveCredentials } from "../credentials.js";
|
|
2
2
|
export async function login(ctx) {
|
|
3
3
|
const device = await ctx.api.request("device", {}, false);
|
|
4
|
-
ctx.write(`
|
|
4
|
+
ctx.write(`1. Open ${device.verificationUri}. If asked, sign in with the code we email you.\n`
|
|
5
|
+
+ `2. Then enter this code on that page: ${device.userCode}\n`);
|
|
5
6
|
const until = ctx.now() + device.expiresIn * 1000;
|
|
6
7
|
while (ctx.now() < until) {
|
|
7
8
|
await ctx.sleep(device.interval * 1000);
|
package/dist/files.js
CHANGED
|
@@ -2,10 +2,10 @@ import { constants } from "node:fs";
|
|
|
2
2
|
import { lstat, open, readdir, realpath } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { ignored, parseIgnore } from "./gitignore.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isReadableRepoPath } from "./secret-files.js";
|
|
6
6
|
const slash = (path) => path.split(sep).join("/");
|
|
7
7
|
function safe(path) {
|
|
8
|
-
return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") &&
|
|
8
|
+
return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && isReadableRepoPath(path)
|
|
9
9
|
&& path.split("/").every(part => !["", ".", "..", ".git", "node_modules"].includes(part));
|
|
10
10
|
}
|
|
11
11
|
async function ignoreFile(directory, base) {
|
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { once } from "node:events";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { resolve } from "node:path";
|
|
4
5
|
import { errorCode, errorText } from "./api.js";
|
|
5
6
|
import { commandContext } from "./context.js";
|
|
@@ -27,6 +28,7 @@ class ProtocolError extends Error {
|
|
|
27
28
|
}
|
|
28
29
|
function object(value) { return value !== null && typeof value === "object" && !Array.isArray(value); }
|
|
29
30
|
export async function serveMcp(input, output, options = {}) {
|
|
31
|
+
const { version } = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
30
32
|
const previews = new Map();
|
|
31
33
|
const requests = new Map();
|
|
32
34
|
let queue = Promise.resolve();
|
|
@@ -47,6 +49,7 @@ export async function serveMcp(input, output, options = {}) {
|
|
|
47
49
|
throw new ProtocolError(-32602, "Provide a finding id.");
|
|
48
50
|
let text = "", exitCode = 0;
|
|
49
51
|
let findingIds;
|
|
52
|
+
let url;
|
|
50
53
|
try {
|
|
51
54
|
const ctx = commandContext({ ...options, signal, write: chunk => {
|
|
52
55
|
// Progress is transient terminal text; only the final lines belong in a tool response.
|
|
@@ -64,11 +67,16 @@ export async function serveMcp(input, output, options = {}) {
|
|
|
64
67
|
previews.delete(path);
|
|
65
68
|
if (!prepared)
|
|
66
69
|
throw new Error("Get a preview first. Show the user the price and get their agreement before confirming.");
|
|
67
|
-
|
|
70
|
+
url = prepared.url;
|
|
71
|
+
const result = await confirmPrepared(prepared, ctx);
|
|
72
|
+
findingIds = result.findingIds;
|
|
73
|
+
url = result.taste.url;
|
|
68
74
|
}
|
|
69
75
|
else {
|
|
70
76
|
previews.delete(path);
|
|
71
|
-
|
|
77
|
+
const prepared = await prepareCheck(args.path, ctx);
|
|
78
|
+
previews.set(path, prepared);
|
|
79
|
+
url = prepared.url;
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
}
|
|
@@ -79,7 +87,7 @@ export async function serveMcp(input, output, options = {}) {
|
|
|
79
87
|
// Some clients (Claude Code among them) give the model only the structured result when one is present, so it
|
|
80
88
|
// repeats the text: without it the model sees no files, price or results.
|
|
81
89
|
return { content: [{ type: "text", text }], isError: exitCode !== 0,
|
|
82
|
-
structuredContent: { text, exitCode, ...(findingIds ? { findingIds } : {}) } };
|
|
90
|
+
structuredContent: { text, exitCode, ...(findingIds ? { findingIds } : {}), ...(url ? { url } : {}) } };
|
|
83
91
|
}
|
|
84
92
|
async function respond(message, id, signal) {
|
|
85
93
|
if (signal.aborted)
|
|
@@ -90,7 +98,7 @@ export async function serveMcp(input, output, options = {}) {
|
|
|
90
98
|
if (message.method === "initialize") {
|
|
91
99
|
const requested = object(message.params) ? message.params.protocolVersion : undefined;
|
|
92
100
|
result = { protocolVersion: ["2024-11-05", "2025-03-26", "2025-06-18"].includes(requested) ? requested : "2025-06-18",
|
|
93
|
-
capabilities: { tools: {} }, serverInfo: { name: "counterfact-labs", version
|
|
101
|
+
capabilities: { tools: {} }, serverInfo: { name: "counterfact-labs", version } };
|
|
94
102
|
}
|
|
95
103
|
else if (message.method === "ping")
|
|
96
104
|
result = {};
|
package/dist/secret-files.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
// Copied from src/lib/conventions/file-
|
|
1
|
+
// Copied from src/lib/conventions/file-admission.ts by the build.
|
|
2
2
|
export const SECRET_FILE_RE = /(^|\/)(\.env(?:\.[^/]*)?|\.(?:npmrc|pypirc|netrc)|(?:credentials|secrets)\.(?:json|ya?ml)|secrets\.toml|[^/]+-credentials\.json|service-account[^/]*\.json|id_(?:rsa|dsa|ecdsa|ed25519)|[^/]+\.(?:pem|key|p12|pfx))$/i;
|
|
3
|
+
export function isReadableRepoPath(path) {
|
|
4
|
+
return !SECRET_FILE_RE.test(path.replace(/\\/g, "/"));
|
|
5
|
+
}
|